{
  "version": 3,
  "sources": ["/cdn/shop/t/6/assets/pf-configurator.js?cb=1793657863911"],
  "sourcesContent": ["import * as THREE from 'three';\r\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\r\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\r\nimport { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\r\nimport { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\r\nimport { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';\r\nimport { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';\r\nimport { OutputPass } from 'three/addons/postprocessing/OutputPass.js';\r\nimport { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';\r\n\r\n// one shared loader with meshopt support \u2014 loads both compressed & plain GLBs\r\nconst gltfLoader = new GLTFLoader().setMeshoptDecoder(MeshoptDecoder);\r\n\r\n// \u2550\u2550 Config \u2014 build_shopify.py rewrites this block for the Liquid template \u2550\u2550\r\nconst ASSET_BASE = window.PF_FILE_URL.split('pfa.bin')[0];\r\nconst ASSET_VER  = '?v=6';               // bump to '?v=2' etc. after re-uploading GLBs\r\nconst MANIFEST_URL = window.PF_FILE_URL;\r\nconst CART_MODE  = 'shopify';\r\n\r\n// \u2500\u2500 Video/demo mode (composable with ?controls=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\r\n//   ?cam=yaw,pitch,dist   starting camera: yaw\u00B0 around the head (0 = face-front,\r\n//                         90 = its right side), pitch\u00B0 elevation, dist meters\r\n//   ?spin=DEG_PER_SEC     turntable speed (time-based, so it's exact); spin=0 locks\r\n//   ?pose=front|back|left|right|<deg>   head start azimuth\r\n//   ?demo=t:op;t:op,op\u2026   choreography, e.g. 0:effect.rainbow;3.5:effect.plasma;\r\n//                         7:fin.tron;10:display.matrix,color.pink;12:boop\r\n//                         (assets preload BEFORE the timeline starts \u2014 no dark beats)\r\n//   ?record=SECONDS       MediaRecorder capture of the canvas (30fps VP9 ~16Mbps),\r\n//                         starts in sync with the demo, auto-downloads .webm.\r\n//                         The tab MUST stay visible (browsers freeze hidden tabs).\r\n//   ?res=2                render-resolution multiplier for supersampled captures\r\nconst VID = (() => {\r\n  const q = new URLSearchParams(location.search);\r\n  return { cam: q.get('cam'), spin: q.get('spin'), pose: q.get('pose'),\r\n           demo: q.get('demo'), record: parseFloat(q.get('record')) || 0,\r\n           res: Math.min(4, parseFloat(q.get('res')) || 0) };\r\n})();\r\nconst FACE_MAKER_URL = 'https://facemaker.protofrenz.com/';\r\n\r\n// \u2550\u2550 Shopify SKU-code mapping \u2550\u2550 A configured head adds ONE complete-head SKU\r\n// (headbase + electronics + fin baked into a single sellable variant), plus a\r\n// SEPARATE ear line item if ears are chosen. Complete heads are sold as\r\n// <collection>-<style> products (studio-vex \u2192 ST-VEX-MAX-<color>-<fin>,\r\n// dlux-vex \u2192 DL-VEX-NP-PB-<fin>); \"head base only\" uses the hb-<style> product\r\n// (HB-<style>-<mount>-<fin|shape>). We compute the SKU CODE from the build and\r\n// resolve code \u2192 variant id at add-time via window.PF_SKU2ID (populated on boot\r\n// from the live /products/<handle>.js, since the CSV export has no numeric ids).\r\nconst STYLE_CODE = { cipher: 'CIPH', iris: 'IRIS', nyx: 'NYX', vex: 'VEX', mk3: 'MK3', mk5: 'MK5', zr: 'ZR', mk2: 'MK2' };\r\nconst COLL_CODE   = { studio: 'ST', dlux: 'DL', starter: 'SK', baseonly: 'HB', visor: 'VS', rewired: 'LR' };\r\nconst COLL_HANDLE = { studio: 'studio', dlux: 'dlux', starter: 'starter', baseonly: 'hb', visor: 'vs', rewired: 'lr' };\r\nconst DISPLAY_CODE = { MAX7219: 'MAX', NeoPixel: 'NP', HUB75: 'P3' };  // in the complete-head SKU\r\n// customer-facing display names (internal values stay MAX7219/HUB75/NeoPixel \u2014\r\n// SKU logic, share links and the manifest's led formats all key off them)\r\nconst DISPLAY_LABEL = { MAX7219: 'LED Matrix', HUB75: 'Full-Color Panel', NeoPixel: 'NeoPixel' };\r\nconst MOUNT_CODE = { MAX7219: 'MAX', NeoPixel: 'MAX', HUB75: 'P3' };   // head-base mount (MAX & NeoPixel share)\r\nconst FIN_CODE   = { fin_short: 'SH', fin_long: 'LG', fin_wild: 'WD', fin_gundam: 'GD',\r\n                     fin_astro: 'AT', fin_tron: 'DT', fin_epic: 'EP', fin_headset: 'HP',\r\n                     fin_ring: 'RG' };\r\nconst SHAPE_CODE = { round: 'RD', sharp: 'SP' };\r\nconst EAR_CODE   = { fox: 'FOX', fennec: 'FEN', cat: 'CAT', bat: 'BAT', lynx: 'LYX',\r\n                     bunny: 'BUN', mouse: 'MOU', wolf: 'PUP' };   // panda renamed \u2192 mouse; wolf\u2192pup\r\n// LED colors match the LIVE Studio complete-head SKUs (ST-<style>-MAX-<code>-<fin>).\r\n// NOTE: live cipher/iris/nyx use CHT/PUR/YEL; the Vex DRAFT uses CHR/PRP/YLW \u2014\r\n// Blair should align Vex to CHT/PUR/YEL when activating it.\r\nconst MAX_COLORS = [['BLU', 'Blue'], ['CHT', 'Chartreuse'], ['GRN', 'Green'], ['ORG', 'Orange'],\r\n                    ['PNK', 'Pink'], ['PUR', 'Purple'], ['RED', 'Red'], ['WHT', 'White'], ['YEL', 'Yellow']];\r\n// parts with no Shopify product yet \u2014 not offered until one exists (fin_circle\r\n// is now fin_ring / RG and IS sold, so nothing is hidden currently)\r\nconst NOT_SELLABLE = new Set();\r\n\r\nfunction resolveVariantId(skuCode) {\r\n  return (window.PF_SKU2ID && window.PF_SKU2ID[skuCode]) || null;\r\n}\r\n// On Shopify, build SKU-code \u2192 variant-id from the live products (the public\r\n// /products/<handle>.js endpoint returns variants with sku + id, no 20-product\r\n// Liquid cap). Runs on boot; no-ops in local preview. 404s (products not yet\r\n// created) are ignored.\r\nconst SHOPIFY_HANDLES = [\r\n  'studio-cipher', 'studio-iris', 'studio-nyx', 'studio-vex', 'studio-mk3', 'studio-mk5', 'studio-mk2',\r\n  'dlux-cipher', 'dlux-iris', 'dlux-nyx', 'dlux-vex',\r\n  'starter-mk3', 'starter-mk5',\r\n  'lr-mk2', 'lr-mk3', 'lr-mk5',   // Legacy Rewired NeoPixel heads\r\n  'hb-cipher', 'hb-iris', 'hb-nyx', 'hb-vex', 'hb-mk3', 'hb-mk5', 'hb-mk2', 'hb-zr',\r\n  'vs-cipher', 'vs-iris', 'vs-nyx', 'vs-vex', 'vs-mk3', 'vs-mk5', 'vs-mk2', 'vs-zr',   // VS-<style> replacement visors\r\n  'ear-fox', 'ear-fennec', 'ear-cat', 'ear-bat', 'ear-lynx', 'ear-bunny', 'ear-mouse', 'ear-pup',\r\n  'ear-fox-secondary', 'ear-fennec-secondary',   // ER-<code>2-<mat> secondary ears\r\n  'acc-cheek',   // AC-CHEEK-<head>-BUBBLE / -LIT bubble cheeks\r\n  'acc-color-addon',   // AC-COLOR-<1|2|BRIEF>-<style> \u2014 Color Booth customization tiers\r\n  'etsy-cheek-art',   // $0 submission product \u2014 Etsy cheek customers' art (AC-CHEEK-ETSY)\r\n];\r\n// The registered product catalogue. A legacy head only appears in a collection\r\n// when we've registered a `<coll>-<style>` product for it here \u2014 so MK2/ZR show\r\n// as head bases (hb-*) and MK2 in Studio, but not in Starter/Visor where no such\r\n// product exists. Draft products still 404 \u2192 SOON teaser; unregistered = hidden.\r\nconst KNOWN_HEAD_HANDLES = new Set(SHOPIFY_HANDLES);\r\nasync function loadShopifyVariants() {\r\n  if (CART_MODE !== 'shopify') return;\r\n  window.PF_SKU2ID = {}; window.PF_SKU2PRICE = {};   // price in cents (AJAX API)\r\n  window.PF_LIVE = new Set();   // handles that exist publicly (drafts 404 \u2192 COMING SOON)\r\n  window.PF_SOLD = new Set();   // SKUs whose variant is sold out (available: false)\r\n  // Only a REAL 404 (draft/unpublished product) may mark a product COMING SOON.\r\n  // A transient failure \u2014 storefront rate limiting from the parallel burst,\r\n  // a flaky connection \u2014 must not: retry, then fall back to the last good\r\n  // snapshot of that product so live products never flicker out of stock.\r\n  let cache = {};\r\n  try { cache = JSON.parse(localStorage.getItem('pf-products-v1') || '{}'); } catch {}\r\n  const apply = (h, skus) => {\r\n    window.PF_LIVE.add(h);\r\n    for (const [sku, [id, price, avail]] of Object.entries(skus)) {\r\n      window.PF_SKU2ID[sku] = id; window.PF_SKU2PRICE[sku] = price;\r\n      if (avail === false) window.PF_SOLD.add(sku);\r\n    }\r\n  };\r\n  const fetchOne = async (h) => {\r\n    for (let attempt = 0; attempt < 3; attempt++) {\r\n      try {\r\n        const r = await fetch(`/products/${h}.js`);\r\n        if (r.status === 404) { delete cache[h]; return; }   // real draft \u2192 COMING SOON\r\n        if (r.ok) {\r\n          const p = await r.json();\r\n          const skus = {};\r\n          for (const v of p.variants) if (v.sku) skus[v.sku] = [v.id, v.price, v.available !== false];\r\n          apply(h, skus); cache[h] = skus;\r\n          return;\r\n        }\r\n      } catch {}\r\n      await new Promise(res => setTimeout(res, 400 * (attempt + 1)));\r\n    }\r\n    if (cache[h]) apply(h, cache[h]);   // still failing \u2192 last-good snapshot\r\n  };\r\n  // small batches \u2014 30 simultaneous hits is what trips the rate limiter\r\n  for (let i = 0; i < SHOPIFY_HANDLES.length; i += 6)\r\n    await Promise.all(SHOPIFY_HANDLES.slice(i, i + 6).map(fetchOne));\r\n  try { localStorage.setItem('pf-products-v1', JSON.stringify(cache)); } catch {}\r\n}\r\n\r\n// \u2500\u2500 COMING SOON: draft head products tease instead of sell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n// A head product that isn't publicly live (draft \u2192 /products/<handle>.js 404s)\r\n// stays fully configurable as a teaser, but the CTA becomes a COMING SOON\r\n// notice and its style chip gets a tag. ?soon=studio-vex,dlux-vex simulates\r\n// drafts anywhere (incl. local preview) for testing.\r\nconst SOON_SIM = new Set((new URLSearchParams(location.search).get('soon') || '')\r\n  .split(',').map(s => s.trim()).filter(Boolean));\r\n// \u2500\u2500 SOLD OUT (CFG-2): a variant Shopify reports unavailable can't be built \u2500\u2500\r\n// Options whose every SKU is sold out are greyed with a tag, and a build that\r\n// still lands on one blocks Add to Cart. ?soldout=ST-NYX-MAX-BLU-SH,ER-FOX*\r\n// simulates it anywhere (exact SKUs, or a prefix ending in *) for testing.\r\nconst SOLD_SIM = (new URLSearchParams(location.search).get('soldout') || '')\r\n  .split(',').map(s => s.trim().toUpperCase()).filter(Boolean);\r\nfunction isSold(sku) {\r\n  if (!sku) return false;\r\n  if (window.PF_SOLD && window.PF_SOLD.has(sku)) return true;\r\n  return SOLD_SIM.some(e => e.endsWith('*') ? sku.startsWith(e.slice(0, -1)) : sku === e);\r\n}\r\n// every known SKU in a family (`ST-NYX` \u2192 ST-NYX-\u2026) is sold out. With no\r\n// product data (local preview) only the simulator can say so.\r\nfunction allSold(prefix) {\r\n  const known = Object.keys(window.PF_SKU2ID || {}).filter(s => s === prefix || s.startsWith(prefix + '-'));\r\n  return known.length ? known.every(isSold) : isSold(prefix);\r\n}\r\n// the head SKU (or another SKU function) as if some choices were different\r\nfunction skuWith(over, fn = skuHead) {\r\n  const saved = {};\r\n  for (const k of Object.keys(over)) { saved[k] = state[k]; state[k] = over[k]; }\r\n  try { return fn(); } finally { Object.assign(state, saved); }\r\n}\r\n// ?etsy=1 \u2014 Etsy cheek customers design in the Logo Studio and submit the art\r\n// FREE: a $0 submission product carries the file + their Etsy order number as\r\n// line-item properties, so the ticket lands in the same queue as Shopify orders\r\n// and joins to the Etsy order by number. No head is added to the cart.\r\nconst ETSY_MODE = new URLSearchParams(location.search).has('etsy');\r\nconst ETSY_SKU = 'AC-CHEEK-ETSY';\r\n// &order=NNNN prefills the order number \u2014 send Etsy buyers a personalized link\r\n// (you know their number when you message them) so nobody typos a receipt number\r\nconst ETSY_PREFILL = (new URLSearchParams(location.search).get('order') || '').trim();\r\n// ?shop=1 \u2014 the same free art submission for a protofrenz.com order that bought lit / NeoPixel cheeks without\r\n// art (the Ops dashboard's \"Art link\" and the order-tracking email send it). &order= is their order number;\r\n// &t= is a signed token proving the order is theirs \u2014 it rides along as the hidden \"_Art token\" and the Ops\r\n// matcher only attaches VERIFIED submissions; &cheek=np opens the NeoPixel stencil studio; &plate= the plate edge.\r\nconst SHOP_ART_MODE = !ETSY_MODE && new URLSearchParams(location.search).has('shop');\r\nconst ART_SUBMIT_MODE = ETSY_MODE || SHOP_ART_MODE;\r\nconst SHOP_ART_TOKEN = (new URLSearchParams(location.search).get('t') || '').trim().slice(0, 80);\r\n\r\n// ?cheekart=1 \u2014 popup mode for the acc-cheek product page: the buyer designs in\r\n// the Logo Studio and the finished print-ready PNG is handed BACK to the opener\r\n// via postMessage (the PDP injects it into its upload field and keeps the\r\n// purchase on the product page). &v=<variantId> hints the head style;\r\n// &origin=\u2026 is validated against OUR domains only \u2014 art never posts elsewhere.\r\nconst CHEEKART_MODE = new URLSearchParams(location.search).has('cheekart');\r\n// &edit=1 \u2014 reopen with the buyer's LAST design restored (full editable state\r\n// from localStorage: source art + transform + cleanup + strokes), not blank\r\nconst CHEEKART_EDIT = new URLSearchParams(location.search).has('edit');\r\n// &edit=order \u2014 the ops dashboard's \"Edit\": the opener sends the order's CURRENT art (after our 'ready'), loaded as\r\n// the source image \u2014 not the localStorage design (that's the PDP's own &edit=1). Cheek art phase 1, 2026-10-04.\r\nconst CHEEKART_EDIT_ORDER = new URLSearchParams(location.search).get('edit') === 'order';\r\n// &plate=pf|m16 \u2014 which printed plate (the dashboard passes it; on the PDP the &v= variant's SKU decides below)\r\nlet CHEEKART_NP = new URLSearchParams(location.search).get('cheek') === 'np';   // NeoPixel cheek popup (&cheek=np, or an -NP &v= variant)\r\nlet CHEEKART_PLATE = ((new URLSearchParams(location.search).get('plate') || '') + '').toLowerCase();\r\nif (CHEEKART_PLATE !== 'pf' && CHEEKART_PLATE !== 'm16') CHEEKART_PLATE = '';\r\nconst CHEEKART_VARIANT = parseInt(new URLSearchParams(location.search).get('v'), 10) || 0;\r\nconst CHEEKART_TARGET = (() => {\r\n  const o = new URLSearchParams(location.search).get('origin') || '';\r\n  const ok = ['https://protofrenz.com', 'https://www.protofrenz.com',\r\n              'https://xw50ez-t0.myshopify.com'].includes(o)\r\n             || /^https:\\/\\/[a-z0-9-]+\\.shopifypreview\\.com$/.test(o);\r\n  return ok ? o : 'https://protofrenz.com';\r\n})();\r\nfunction headHandleFor(style, coll = state.collection) { return `${COLL_HANDLE[coll]}-${style}`; }\r\nfunction styleLive(style, coll = state.collection) {\r\n  const h = headHandleFor(style, coll);\r\n  if (SOON_SIM.has(h)) return false;\r\n  if (CART_MODE !== 'shopify' || !window.PF_LIVE) return true;   // local preview: all live\r\n  return window.PF_LIVE.has(h);\r\n}\r\nfunction headLive() { return styleLive(state.style); }\r\n// accessory products (ears / secondary ears) can be draft too \u2014 those options\r\n// get a SOON tag and are disabled until the product goes live\r\nconst EAR_HANDLE = { fox: 'ear-fox', fennec: 'ear-fennec', cat: 'ear-cat', bat: 'ear-bat',\r\n                     lynx: 'ear-lynx', bunny: 'ear-bunny', mouse: 'ear-mouse', wolf: 'ear-pup' };\r\nfunction earHandleFor(cat, part) {\r\n  const base = earBase(part || '');\r\n  return cat === 'ear2' ? `ear-${base}-secondary` : (EAR_HANDLE[base] || `ear-${base}`);\r\n}\r\nfunction accLive(handle) {\r\n  if (SOON_SIM.has(handle)) return false;\r\n  if (CART_MODE !== 'shopify' || !window.PF_LIVE) return true;   // local preview: all live\r\n  return window.PF_LIVE.has(handle);\r\n}\r\n// Format a shop-currency amount (cents) in the visitor's currency. Shopify\r\n// Markets exposes the active presentment currency + conversion rate as globals;\r\n// we convert + format with Intl (correct symbol/decimals per currency). This is\r\n// an estimate \u2014 Shopify applies exact rounding at checkout. Falls back to USD.\r\nfunction money(cents) {\r\n  const cur = (typeof Shopify !== 'undefined' && Shopify.currency) || null;\r\n  const rate = parseFloat((cur && cur.rate) || '1') || 1;\r\n  const code = (cur && cur.active) || 'USD';\r\n  const amount = (cents / 100) * rate;\r\n  try { return new Intl.NumberFormat(undefined, { style: 'currency', currency: code }).format(amount); }\r\n  catch { return '$' + amount.toFixed(2); }\r\n}\r\n// live total for the current build (head + ears). Null in preview / when a SKU\r\n// has no price (unresolved/draft).\r\nfunction cartTotalCents() {\r\n  if (!window.PF_SKU2PRICE) return null;\r\n  const head = skuHead();\r\n  const hp = head && window.PF_SKU2PRICE[head];\r\n  if (hp == null) return null;                         // head unresolved \u2192 no price\r\n  let cents = hp;\r\n  const bc = skuBubbleCheek();\r\n  if (bc && window.PF_SKU2PRICE[bc] != null) cents += window.PF_SKU2PRICE[bc];\r\n  const ear = hasEars() ? skuEars() : null;\r\n  if (ear && window.PF_SKU2PRICE[ear] != null) cents += window.PF_SKU2PRICE[ear];\r\n  const e2 = hasEar2() ? skuEar2() : null;\r\n  if (e2 && window.PF_SKU2PRICE[e2] != null) cents += window.PF_SKU2PRICE[e2];\r\n  const cc = customColorsActive() ? skuColorAddon() : null;\r\n  if (cc && window.PF_SKU2PRICE[cc] != null) cents += window.PF_SKU2PRICE[cc];\r\n  return cents;\r\n}\r\nfunction updatePrice() {\r\n  const el = document.getElementById('cfg-price');\r\n  if (!el) return;\r\n  if (ART_SUBMIT_MODE || CHEEKART_MODE) { el.textContent = ''; return; }   // no price line in art modes\r\n  if (!headLive()) { el.textContent = ''; return; }            // teaser: the banner says it all\r\n  if (!window.PF_SKU2PRICE) { el.textContent = ''; return; }   // no price data (local preview)\r\n  const cents = cartTotalCents();\r\n  el.innerHTML = cents == null\r\n    ? '<span class=\"sub\">Price unavailable for this configuration</span>'\r\n    : money(cents) + (hasEars() ? '<span class=\"sub\">head + ears, before shipping</span>'\r\n                                : '<span class=\"sub\">before shipping</span>');\r\n}\r\nfunction earBase(name) { return name.toLowerCase().replace(/^ear2?_/, '').replace(/_small$/, ''); }\r\n\r\n// fin (modern) / shape (mk3) / null (mk5, no fin) \u2014 the head-SKU suffix segment\r\nfunction finOrShapeCode() {\r\n  if (isLegacy(state.style))\r\n    return shapesFor(state.style).length ? (SHAPE_CODE[state.shape] || null) : null;\r\n  return FIN_CODE[state.fin] || null;\r\n}\r\n// The single complete-head (or head-base-only) SKU for the current build.\r\nfunction skuHead() {\r\n  const st = STYLE_CODE[state.style]; if (!st) return null;\r\n  // visor only: one SKU per style (VS-CIPH \u2026) \u2014 no fin/shape segment\r\n  if (state.collection === 'visor') return `VS-${st}`;\r\n  const suffix = finOrShapeCode();\r\n  const tail = suffix ? `-${suffix}` : '';\r\n  if (state.collection === 'baseonly') {\r\n    // printed head, mount only \u2014 default to the HUB75 (P3) base; fall back to\r\n    // the MAX-mount base only if this style has no P3 base on Shopify\r\n    const p3 = `HB-${st}-P3${tail}`, max = `HB-${st}-MAX${tail}`;\r\n    if (CART_MODE === 'shopify' && !resolveVariantId(p3) && resolveVariantId(max)) return max;\r\n    return p3;\r\n  }\r\n  // Legacy Rewired: NeoPixel-only, no D-Lux 'PB' segment (LR-MK2-NP, LR-MK3-NP-SP)\r\n  if (state.collection === 'rewired') return `${COLL_CODE.rewired}-${st}-${DISPLAY_CODE.NeoPixel}${tail}`;\r\n  const coll = COLL_CODE[state.collection], disp = DISPLAY_CODE[state.display];\r\n  if (!coll || !disp) return null;\r\n  // 4th segment: MAX7219 \u2192 LED color, NeoPixel \u2192 fixed 'PB', HUB75 \u2192 none\r\n  const opt = state.display === 'MAX7219' ? state.maxColor\r\n            : state.display === 'NeoPixel' ? 'PB' : null;\r\n  const mid = opt ? `-${opt}` : '';\r\n  return `${coll}-${st}-${disp}${mid}${tail}`;\r\n}\r\n// customer-facing fin names where the part name differs from the product's\r\nconst FIN_LABEL = { fin_tron: 'DelTRON', fin_headset: 'Headphones' };\r\n// customer-facing names for the ear materials (SKUs keep TPU / PLA)\r\nconst EAR_MATERIAL_LABEL = { TPU: 'Squishy', PLA: 'Hard' };\r\n// Size switch memory: the last Full/Small pick carries over when switching\r\n// ears (null = follow the current ear, e.g. from a shared ?build= link)\r\nlet earSmallPref = null;\r\nfunction skuEars() {\r\n  const c = EAR_CODE[earBase(state.ear || '')]; if (!c) return null;\r\n  const size = /_small$/i.test(state.ear) ? '65' : '100';\r\n  return `ER-${c}-${state.earMaterial}${size}`;\r\n}\r\n// secondary ears: ER-<CODE>2-<MAT> (no size segment); share the ear material\r\nfunction skuEar2() {\r\n  const c = EAR_CODE[earBase(state.ear2 || '')]; if (!c) return null;\r\n  return `ER-${c}2-${state.earMaterial}`;\r\n}\r\n\r\nconst COLOR_BLACK = '#141418';   // printed black PLA+\r\n// Cheek LED color \u2014 the LED pixel ring behind a Standard LED (LIT) cheek.\r\n// Order property 'Cheek LED color' (cheek line only; the head line's own\r\n// 'LED color' is the MAX7219 face). Hexes are the starting palette \u2014 tune\r\n// against the real rings. Names ride on orders; keep them stable.\r\nconst CHEEK_LED_COLORS = [['Red', '#FF2A2A'], ['Yellow', '#FFD21E'], ['Violet', '#8B3DFF'],\r\n  ['Blue', '#2F6BFF'], ['Green', '#2BDB4A'], ['Pink', '#FF5FB0'], ['White', '#FFFFFF']];\r\nconst cheekLedName = (n) => (CHEEK_LED_COLORS.find(c => c[0].toLowerCase() === String(n || '').trim().toLowerCase()) || ['White'])[0];\r\nconst cheekLedHex = () => CHEEK_LED_COLORS.find(c => c[0] === cheekLedName(state.ledColor))[1];\r\n// NeoPixel cheek backing \u2014 what the light stencil is printed onto. Rides the\r\n// NP cheek cart line as 'Cheek backing'. Keys are stable; labels show.\r\nconst NP_BACKINGS = { white: 'White (diffuse)', clear: 'Clear' };\r\nconst npBackingKey = (v) => (String(v || '').trim().toLowerCase() === 'clear' ? 'clear' : 'white');\r\nconst COLOR_WHITE = '#f4f1e8';   // printed white PLA+ (both ear categories)\r\n\r\n// \u2550\u2550 Color Booth \u2014 custom colors / color-blocking \u2550\u2550\r\n// FILAMENT_COLORS is the stocked-filament palette (ids ride on orders, so keep\r\n// them stable; labels/hex are display-only). PLACEHOLDER SET \u2014 swap for the\r\n// real stocked colors before launch. An entry may carry mats: ['PLA'] or\r\n// ['TPU'] \u2014 the ear zones then only offer it for that ear material (no mats =\r\n// available in both). Keep the stock defaults (black/white) unrestricted.\r\nconst FILAMENT_COLORS = [\r\n  { id: 'black',  label: 'Stock Black',    hex: COLOR_BLACK },\r\n  { id: 'white',  label: 'Arctic White',   hex: COLOR_WHITE },\r\n  { id: 'gray',   label: 'Slate Gray',     hex: '#8d8d94' },\r\n  { id: 'red',    label: 'Fire Red',       hex: '#c22f2f' },\r\n  { id: 'orange', label: 'Blaze Orange',   hex: '#e8752a' },\r\n  { id: 'yellow', label: 'Cyber Yellow',   hex: '#e8c02a' },\r\n  { id: 'green',  label: 'Gecko Green',    hex: '#3fa14b' },\r\n  { id: 'teal',   label: 'Proto Teal',     hex: '#1fa8a0' },\r\n  { id: 'blue',   label: 'Electric Blue',  hex: '#2a62d9' },\r\n  { id: 'purple', label: 'Galaxy Purple',  hex: '#7a3fd1' },\r\n  { id: 'pink',   label: 'Neon Pink',      hex: '#e05fa8' },\r\n];\r\nconst VISOR_TINTS = [\r\n  { id: 'smoke',  label: 'Stock Smoke',    hex: '#14141c' },\r\n  { id: 'blue',   label: 'Midnight Blue',  hex: '#101c38' },\r\n  { id: 'purple', label: 'Ultraviolet',    hex: '#241238' },\r\n  { id: 'red',    label: 'Ember Red',      hex: '#331014' },\r\n  { id: 'green',  label: 'Emerald',        hex: '#0f2b18' },\r\n];\r\n// zone = a customer-colorable region; maps onto a render slot. def = the\r\n// stock color id \u2014 state.colors only ever stores DEVIATIONS from stock.\r\n// The display mount (frame slot) is deliberately NOT a zone: always stock\r\n// black. The cheek bubble dome isn't one either \u2014 it follows the visor tint.\r\n// finAccent is the patterned color-block region on fins that ship a\r\n// `<fin>_accent` part (see buildPart) \u2014 accent:true zones share their slot.\r\nconst COLOR_ZONES = {\r\n  shell:  { slot: 'base',   label: 'Head shell',           def: 'black' },\r\n  visor:  { slot: 'visor',  label: 'Visor tint',           def: 'smoke' },\r\n  gasket: { slot: 'gasket', label: 'Magnetic Visor Frame', def: 'black' },\r\n  ears:   { slot: 'ear',    label: 'Ears',                 def: 'white' },\r\n  ear2:   { slot: 'ear2',   label: 'Secondary ears',       def: 'white' },\r\n  fins:   { slot: 'fin',    label: 'Fin',                  def: 'black' },\r\n  finAccent: { slot: 'fin', label: 'Fin accent',           def: 'black', accent: true },\r\n  cheeks: { slot: 'cheek',  label: 'Cheek plates',         def: 'black' },\r\n};\r\nconst SLOT_ZONE = Object.fromEntries(Object.entries(COLOR_ZONES)\r\n  .filter(([, m]) => !m.accent).map(([z, m]) => [m.slot, z]));\r\n// painted accent regions (from accent_painter.html): triangle ranges\r\n// [start,count, start,count, \u2026] in mesh-traversal order, stored on the part's\r\n// manifest entry \u2014 so new accents ship with a Files upload, no redeploy\r\nfunction accentTrisFor(name) {\r\n  const r = name && name !== 'none' ? manifest.parts[name]?.accentTris : null;\r\n  return Array.isArray(r) && r.length ? r : null;\r\n}\r\nfunction zonePalette(zone) {\r\n  if (zone === 'visor') return VISOR_TINTS;\r\n  if (zone === 'ears' || zone === 'ear2')   // ear colors can differ per material\r\n    return FILAMENT_COLORS.filter(c => !c.mats || c.mats.includes(state.earMaterial));\r\n  return FILAMENT_COLORS;\r\n}\r\nfunction zoneColor(zone) {   // palette entry currently applied to a zone\r\n  const pal = zonePalette(zone), meta = COLOR_ZONES[zone];\r\n  return pal.find(c => c.id === state.colors?.[zone])\r\n      || pal.find(c => c.id === meta.def) || pal[0];\r\n}\r\n// is this zone's region actually on the current build?\r\nfunction zoneOnBuild(zone) {\r\n  const meta = COLOR_ZONES[zone], s = meta && slots.get(meta.slot);\r\n  if (!s) return false;\r\n  return meta.accent ? !!accentTrisFor(s.name) : true;\r\n}\r\n// deviations that are real: valid id (for the current ear material), differs\r\n// from stock, and the zone's part is actually on the current build (a gasket\r\n// color can't ride a legacy head)\r\nfunction cleanColors() {\r\n  const out = {};\r\n  for (const [zone, id] of Object.entries(state.colors || {})) {\r\n    const meta = COLOR_ZONES[zone];\r\n    if (!meta || id === meta.def || !zonePalette(zone).some(c => c.id === id)) continue;\r\n    if (!zoneOnBuild(zone)) continue;\r\n    out[zone] = id;\r\n  }\r\n  return out;\r\n}\r\nfunction customColorsActive() { return !visorOnly() && Object.keys(cleanColors()).length > 0; }\r\n// Billed against the LIVE acc-color-addon product: tier = how many distinct\r\n// custom colors the scheme uses (a visor tint counts as one).\r\n//   AC-COLOR-1-<style>  Single Color   \u00B7  AC-COLOR-2-<style>  Two-Tone\r\n//   AC-COLOR-BRIEF-<style>  Custom Brief (3+ colors / full color-block)\r\nconst AC_COLOR_STYLE = { cipher: 'PF-CIPHER', iris: 'PF-IRIS', nyx: 'PF-NYX',\r\n                         mk2: 'MK2', mk3: 'MK3', mk5: 'MK5', zr: 'ZILLIONROSS' };\r\nconst COLOR_TIER_LABEL = { 1: 'Single Color', 2: 'Two-Tone', BRIEF: 'Custom Brief' };\r\nfunction colorTier() {\r\n  // distinct custom colors; visor ids are namespaced so a blue tint and blue\r\n  // filament don't collapse into one\r\n  const n = new Set(Object.entries(cleanColors())\r\n    .map(([z, id]) => (z === 'visor' ? 'v:' : '') + id)).size;\r\n  return n === 0 ? null : n === 1 ? '1' : n === 2 ? '2' : 'BRIEF';\r\n}\r\nfunction skuColorAddon() {\r\n  const t = colorTier(), st = AC_COLOR_STYLE[state.style];\r\n  return t && st ? `AC-COLOR-${t}-${st}` : null;\r\n}\r\n// The Color Booth ships hidden until the real stocked-filament palette is in\r\n// (Blair 2026-10-05). Flip to true to launch it \u2014 everything else is wired.\r\nconst COLOR_BOOTH_ON = false;\r\nfunction colorBoothLive() {\r\n  return COLOR_BOOTH_ON && !!AC_COLOR_STYLE[state.style] && accLive('acc-color-addon');\r\n}\r\n// 'Head shell: Fire Red \u00B7 Visor tint: Midnight Blue' \u2014 rides the order so the\r\n// print queue reads the scheme without decoding _config\r\nfunction colorSummaryText() {\r\n  return Object.keys(cleanColors())\r\n    .map(z => `${COLOR_ZONES[z].label}: ${zoneColor(z).label}`).join(' \u00B7 ');\r\n}\r\n\r\n// \u2550\u2550 Collections \u2014 the first selector; gates styles, displays and fins.\r\n// Mirrors how the heads are sold. styles: null = every style in the manifest;\r\n// a style list filters to what exists (Starter stays disabled until MK3/MK5\r\n// assets are uploaded).\r\n// styleTypes gates which heads a collection offers: 'modern' = cipher/iris/\r\n// nyx/vex (shared head_base + gasket/frame/visor package, full features);\r\n// 'legacy' = mk3/mk5 (standalone head shell + visor + frame, no gasket / fins\r\n// / secondary ears). Capabilities follow the HEAD TYPE, not the collection.\r\nconst COLLECTIONS = {\r\n  starter:  { label: 'Starter',        styleTypes: ['legacy'], displays: ['HUB75'],\r\n              note: 'MK3 / MK5 heads with a full-color panel display.' },\r\n  studio:   { label: 'Studio',         styleTypes: ['modern', 'legacy'], displays: ['MAX7219', 'HUB75'],\r\n              note: 'The studio line \u2014 every style, LED matrix or full-color panel.' },\r\n  dlux:     { label: 'D-LUX',          styleTypes: ['modern'], displays: ['NeoPixel'],\r\n              note: 'Full-color NeoPixel electronics.' },\r\n  rewired:  { label: 'Legacy Rewired', styleTypes: ['legacy'], displays: ['NeoPixel'],\r\n              note: 'MK2 / MK3 / MK5 shells rebuilt with full-color NeoPixel electronics.' },\r\n  baseonly: { label: 'Head base only', styleTypes: ['modern', 'legacy'], displays: [],\r\n              note: 'The printed head, no electronics.' },\r\n  visor:    { label: 'Visor only', styleTypes: ['modern', 'legacy'], displays: [],\r\n              note: 'A replacement visor on its own \u2014 tinted and ready to swap in.' },\r\n};\r\n\r\n// \u2550\u2550 Scene (loader core ported from the validated harness) \u2550\u2550\r\nconst viewer = document.getElementById('cfg-viewer');\r\nconst renderer = new THREE.WebGLRenderer({ antialias: true });\r\nrenderer.setPixelRatio(Math.min(devicePixelRatio, 2));\r\nviewer.appendChild(renderer.domElement);\r\nconst scene = new THREE.Scene();\r\nscene.background = new THREE.Color(0x0C0B14);\r\nconst camera = new THREE.PerspectiveCamera(40, 1, 0.001, 100);\r\nconst controls = new OrbitControls(camera, renderer.domElement);\r\ncontrols.enableDamping = true;\r\ncontrols.minDistance = 0.25; controls.maxDistance = 1.4;\r\ncontrols.enablePan = false;\r\n\r\nscene.add(new THREE.AmbientLight(0xffffff, 0.5));\r\nconst key = new THREE.DirectionalLight(0xffffff, 1.6); key.position.set(2, 3, 2); scene.add(key);\r\nconst rimT = new THREE.DirectionalLight(0x2EECC5, 1.0); rimT.position.set(-3, 1, -2); scene.add(rimT);\r\nconst rimP = new THREE.DirectionalLight(0x9B5FE3, 0.6); rimP.position.set(3, -1, -2); scene.add(rimP);\r\n\r\n// Fusion exports are Z-up; Three.js is Y-up\r\nconst orient = new THREE.Group();\r\norient.rotation.x = -Math.PI / 2;\r\nscene.add(orient);\r\n// The turntable rotates `spinner`; `root` sits inside it offset so the spin axis\r\n// passes through the HEAD's visual centre (recenterSpin) \u2014 otherwise the head\r\n// orbits the model origin (~15cm off) and \"sweeps\" on camera instead of spinning.\r\nconst spinner = new THREE.Group();\r\norient.add(spinner);\r\nconst root = new THREE.Group();\r\nspinner.add(root);\r\nconst MODEL_FROM_WORLD = new THREE.Matrix4().makeRotationX(Math.PI / 2);\r\n// World \u2192 root-local: removes BOTH the orient group AND root's live Z spin, so a\r\n// bbox measured here is deterministic (independent of the idle-spin angle). The\r\n// fit head anchors in this frame, else it jumps with the spin angle at enable.\r\nfunction worldToRoot() {\r\n  root.updateWorldMatrix(true, false);\r\n  return new THREE.Matrix4().copy(root.matrixWorld).invert();\r\n}\r\n// The head's anchor box, in root space (spin-independent): the shell + gasket +\r\n// visor only, so adding ears / fins / cheeks or swapping the display never moves\r\n// the head on screen. Hiding the visor reuses the box measured with it on (per\r\n// shape), and parts are measured at full size even mid grow-in animation.\r\nconst headBoxCache = new Map();\r\nfunction headCoreBox() {\r\n  const inv = worldToRoot();\r\n  const box = new THREE.Box3();\r\n  for (const key of ['base', 'gasket', 'visor']) {\r\n    const s = slots.get(key);\r\n    if (!s) continue;\r\n    const sc = s.group.scale.x;\r\n    s.group.scale.setScalar(1); s.group.updateMatrixWorld(true);\r\n    box.union(new THREE.Box3().setFromObject(s.group).applyMatrix4(inv));\r\n    s.group.scale.setScalar(sc); s.group.updateMatrixWorld(true);\r\n  }\r\n  const key = `${state.style}|${state.shape}|${visorOnly()}`;\r\n  if (!state.visorOff || visorOnly() || isLegacy(state.style)) {\r\n    if (!box.isEmpty()) headBoxCache.set(key, box.clone());\r\n    return box;\r\n  }\r\n  return headBoxCache.get(key)?.clone() || box;\r\n}\r\n// put the spin axis through the head's centre (headCoreBox), so swapping parts\r\n// never shifts the head mid-take\r\nfunction recenterSpin() {\r\n  const box = headCoreBox();\r\n  if (box.isEmpty()) return;\r\n  const c = box.getCenter(new THREE.Vector3());\r\n  root.position.x = -c.x;\r\n  root.position.y = -c.y;   // Z is the spin axis \u2014 height needs no recentering\r\n}\r\n\r\n// \u2500\u2500 Visor-only visibility: a lone tinted visor nearly vanishes on the dark bg,\r\n// so give it a studio environment (white softbox reflections, like\r\n// configurator_studio) + a lifted background. Reverts for normal head builds. \u2500\u2500\r\nlet _visorEnv = null;\r\nfunction studioEnv() {\r\n  if (_visorEnv) return _visorEnv;\r\n  const c = document.createElement('canvas'); c.width = 2048; c.height = 1024;\r\n  const x = c.getContext('2d');\r\n  x.fillStyle = '#0c0b14'; x.fillRect(0, 0, 2048, 1024);\r\n  for (const [px, w, a] of [[0.22, 120, 0.85], [0.5, 190, 0.6], [0.8, 120, 0.8]]) {\r\n    const g = x.createLinearGradient(px * 2048 - w, 0, px * 2048 + w, 0);\r\n    g.addColorStop(0, 'rgba(255,255,255,0)'); g.addColorStop(0.5, `rgba(255,255,255,${a})`); g.addColorStop(1, 'rgba(255,255,255,0)');\r\n    x.fillStyle = g; x.fillRect(px * 2048 - w, 1024 * 0.08, 2 * w, 1024 * 0.84);\r\n  }\r\n  const t = new THREE.CanvasTexture(c);\r\n  t.mapping = THREE.EquirectangularReflectionMapping; t.colorSpace = THREE.SRGBColorSpace;\r\n  _visorEnv = t; return t;\r\n}\r\nconst BG_DARK = new THREE.Color(0x0C0B14), BG_VISOR = new THREE.Color(0x1b1a24);\r\n// \u2500\u2500 Viewer backdrop: the black head barely separates from the near-black page,\r\n// so the default is a lifted \"studio\" gradient; Face & Fit offers Dark / Studio /\r\n// Light (remembered per visitor, ?bg= overrides). Film / video rigs stay Dark.\r\n// The glow pass renders over black regardless (renderGlow), so LEDs bloom the same.\r\nconst BACKDROPS = { dark: ['Dark'], studio: ['Studio', '#55536a', '#1a1923'], light: ['Light', '#eceef3', '#a9acb9'] };\r\nlet backdrop = null;\r\nfunction curBackdrop() {\r\n  if (backdrop) return backdrop;\r\n  const q = new URLSearchParams(location.search).get('bg');\r\n  let v = null;\r\n  try { v = localStorage.getItem('pfBackdrop'); } catch {}\r\n  backdrop = (FILM_MODE || VID.cam || VID.pose || VID.demo || VID.record) ? 'dark'\r\n    : BACKDROPS[q] ? q : BACKDROPS[v] ? v : 'studio';\r\n  return backdrop;\r\n}\r\nconst _backdropTex = {};\r\nfunction backdropBg(visorView) {\r\n  const b = curBackdrop();\r\n  if (b === 'dark') return visorView ? BG_VISOR : BG_DARK;\r\n  if (!_backdropTex[b]) {   // soft radial: lightest just above centre, behind the head\r\n    const [, inner, outer] = BACKDROPS[b], S = 512;\r\n    const c = document.createElement('canvas'); c.width = c.height = S;\r\n    const x = c.getContext('2d'), g = x.createRadialGradient(S * 0.5, S * 0.42, 0, S * 0.5, S * 0.5, S * 0.75);\r\n    g.addColorStop(0, inner); g.addColorStop(1, outer);\r\n    x.fillStyle = g; x.fillRect(0, 0, S, S);\r\n    const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace;\r\n    _backdropTex[b] = t;\r\n  }\r\n  return _backdropTex[b];\r\n}\r\nfunction setBackdrop(b) {\r\n  backdrop = b;\r\n  try { localStorage.setItem('pfBackdrop', b); } catch {}\r\n  scene.background = backdropBg(visorOnly());\r\n  renderBackdropUI();\r\n}\r\nfunction renderBackdropUI() {\r\n  const el = document.getElementById('opt-backdrop'); el.innerHTML = '';\r\n  for (const [k, [label]] of Object.entries(BACKDROPS))\r\n    optButton(el, label, k === curBackdrop(), false, () => setBackdrop(k));\r\n}\r\nfunction applyVisorLook(on) {\r\n  scene.environment = on ? studioEnv() : null;\r\n  scene.environmentIntensity = on ? 1.3 : 1;\r\n  scene.background = backdropBg(on);\r\n  const v = slots.get('visor');\r\n  if (v) v.group.traverse(o => {\r\n    if (o.material && 'envMapIntensity' in o.material) { o.material.envMapIntensity = on ? 3.1 : 1; o.material.needsUpdate = true; }\r\n  });\r\n}\r\n\r\n// \u2500\u2500 fin\u2194ear overlap check: some pairings genuinely intersect at their default\r\n// snap positions (physical reality \u2014 the disclaimer owns it). Detect it and\r\n// point the customer at the position adjusters. Sampled fin vertices tested\r\n// against slightly-inset per-mesh ear boxes (tight enough to skip the usual\r\n// \"boxes always touch\" false positives). \u2500\u2500\r\nconst overlapRay = new THREE.Raycaster();\r\nfunction partsOverlap(slotA, slotB) {\r\n  const a = slots.get(slotA), b = slots.get(slotB);\r\n  if (!a || !b) return { overlap: false, frac: 0 };\r\n  // measure at TRUE scale \u2014 the swap grow-in may have parts mid-tween at 0.82,\r\n  // which shrinks geometry and under-detects; restore after\r\n  const norm = [];\r\n  for (const g of [a.group, b.group]) if (g.scale.x !== 1) { norm.push([g, g.scale.x]); g.scale.setScalar(1); }\r\n  a.group.updateWorldMatrix(true, true);\r\n  b.group.updateWorldMatrix(true, true);\r\n  // stage 1 (cheap): sampled A-vertices inside per-mesh B boxes = candidates.\r\n  // Boxes alone over-flag: a tall leaning ear's box crosses the centreline where\r\n  // the fin legitimately sits (\"between the ears\" \u2260 \"through an ear\").\r\n  const boxes = [];\r\n  b.group.traverse(o => {\r\n    if (!o.isMesh) return;\r\n    const bx = new THREE.Box3().setFromObject(o);\r\n    bx.expandByScalar(-0.004);\r\n    if (!bx.isEmpty()) boxes.push(bx);\r\n  });\r\n  const done = (r) => { for (const [g, s] of norm) g.scale.setScalar(s); return r; };\r\n  if (!boxes.length) return done({ overlap: false, frac: 0 });\r\n  const candidates = [];\r\n  let total = 0;\r\n  const v = new THREE.Vector3();\r\n  a.group.traverse(o => {\r\n    if (!o.isMesh) return;\r\n    const pos = o.geometry.attributes.position;\r\n    const stride = Math.max(1, Math.floor(pos.count / 250));\r\n    for (let i = 0; i < pos.count; i += stride) {\r\n      v.fromBufferAttribute(pos, i).applyMatrix4(o.matrixWorld);\r\n      total++;\r\n      for (const bx of boxes) if (bx.containsPoint(v)) { candidates.push(v.clone()); break; }\r\n    }\r\n  });\r\n  if (!candidates.length) return done({ overlap: false, frac: 0, total });\r\n  // stage 2 (exact-ish): parity raycast \u2014 odd crossings of B's surface along +X\r\n  // means the point is INSIDE the mesh volume. Needs DoubleSide during the test\r\n  // (raycaster skips backfaces otherwise), restored after.\r\n  const flipped = [];\r\n  b.group.traverse(o => { if (o.isMesh && o.material.side !== THREE.DoubleSide) {\r\n    flipped.push([o.material, o.material.side]); o.material.side = THREE.DoubleSide; } });\r\n  const step = Math.max(1, Math.floor(candidates.length / 36));   // cap the expensive casts\r\n  let insideMesh = 0, tested = 0;\r\n  const DIR = new THREE.Vector3(1, 0, 0);\r\n  for (let i = 0; i < candidates.length; i += step) {\r\n    overlapRay.set(candidates[i], DIR);\r\n    const n = overlapRay.intersectObject(b.group, true).length;\r\n    tested++;\r\n    if (n % 2 === 1) insideMesh++;\r\n  }\r\n  for (const [m, side] of flipped) m.side = side;\r\n  const frac = tested ? insideMesh / tested : 0;\r\n  return done({ overlap: frac > 0.12 && insideMesh >= 3, frac: +frac.toFixed(3),\r\n                insideMesh, tested, candidates: candidates.length, total });\r\n}\r\nfunction updateOverlapNote() {\r\n  const el = document.getElementById('overlap-note');\r\n  if (!el) return;\r\n  // BOTH directions: a thin part poking into a thick one only registers from the\r\n  // poking side (wolf ear \u2192 astro fin taught us this \u2014 30% of ear verts inside\r\n  // the fin, zero fin verts inside the ear shell)\r\n  const pair = (x, y) => partsOverlap(x, y).overlap || partsOverlap(y, x).overlap;\r\n  el.style.display = (pair('fin', 'ear') || pair('fin', 'ear2')) ? 'block' : 'none';\r\n}\r\n\r\n// \u2500\u2500 NeoPixel glow: SELECTIVE bloom \u2014 ONLY the LED panels (on BLOOM_LAYER)\r\n// bloom; the shell, ears and visor never do. Two composers: bloomComposer hides\r\n// everything except the panels and blooms them; finalComposer renders the scene\r\n// normally and adds that bloom back. Used only for NeoPixel. \u2500\u2500\r\nconst BLOOM_LAYER = 1;\r\nconst bloomLayer = new THREE.Layers(); bloomLayer.set(BLOOM_LAYER);\r\nconst visStash = new Map();\r\n\r\nconst bloomPass = new UnrealBloomPass(new THREE.Vector2(1, 1),\r\n  0.6 /* strength */, 0.45 /* radius */, 0.0 /* threshold (panels are the only lit thing) */);\r\nconst bloomComposer = new EffectComposer(renderer);\r\nbloomComposer.renderToScreen = false;\r\nbloomComposer.addPass(new RenderPass(scene, camera));\r\nbloomComposer.addPass(bloomPass);\r\n\r\nconst mixPass = new ShaderPass(new THREE.ShaderMaterial({\r\n  uniforms: { baseTexture: { value: null }, bloomTexture: { value: bloomComposer.renderTarget2.texture } },\r\n  vertexShader: 'varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }',\r\n  fragmentShader: 'uniform sampler2D baseTexture; uniform sampler2D bloomTexture; varying vec2 vUv;' +\r\n    'void main(){ gl_FragColor = texture2D(baseTexture, vUv) + texture2D(bloomTexture, vUv); }',\r\n}), 'baseTexture');\r\nmixPass.needsSwap = true;\r\nconst finalComposer = new EffectComposer(renderer);\r\nfinalComposer.addPass(new RenderPass(scene, camera));\r\nfinalComposer.addPass(mixPass);\r\nfinalComposer.addPass(new OutputPass());\r\n\r\n// ?res=N \u2014 supersampled captures independent of window size (4K on a laptop)\r\nif (VID.res) {\r\n  renderer.setPixelRatio(VID.res);\r\n  bloomComposer.setPixelRatio(VID.res);\r\n  finalComposer.setPixelRatio(VID.res);\r\n}\r\n\r\nconst BLACK_BG = new THREE.Color(0x000000);\r\nconst DARK_MAT = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.DoubleSide });   // bloom-pass occluder\r\nconst matStash = new Map();\r\n// HUB75 + MAX7219 panels now glow too, at HALF of D-Lux NeoPixel's brightness (Blair)\r\nfunction glowScale() { return state.display === 'NeoPixel' ? 1 : 0.5; }\r\nfunction renderGlow() {\r\n  bloomPass.strength = 0.6 * glowScale();\r\n  const bg = scene.background; scene.background = BLACK_BG;   // no background haze in the bloom pass\r\n  // In the bloom pass, OPAQUE parts (shell, ears) go solid black so they OCCLUDE\r\n  // panels behind them \u2014 the glow no longer bleeds through the head shell. Only\r\n  // see-through parts (visor, cheek dome, logos) are hidden so the glow shows through.\r\n  scene.traverse(o => {\r\n    if (!o.isMesh || bloomLayer.test(o.layers)) return;\r\n    if (o.material && o.material.transparent) { visStash.set(o, o.visible); o.visible = false; }\r\n    else { matStash.set(o, o.material); o.material = DARK_MAT; }\r\n  });\r\n  bloomComposer.render();     // panels bloom; black shell/ears occlude what's behind them\r\n  for (const [o, v] of visStash) o.visible = v; visStash.clear();\r\n  for (const [o, m] of matStash) o.material = m; matStash.clear();\r\n  scene.background = bg;\r\n  finalComposer.render();\r\n}\r\n\r\n// coloured spill light \u2014 the pixels cast their colour onto the shell/visor\r\nconst SPILL_INTENSITY = 0.35;\r\nconst spill = new THREE.PointLight(0xffffff, 0, 0.5, 2.0);\r\nroot.add(spill);\r\nconst ledCenter = new THREE.Vector3();\r\nconst ledDominant = new THREE.Color(0x00ffaa);\r\n\r\nfunction resize() {\r\n  const w = viewer.clientWidth, h = viewer.clientHeight;\r\n  if (!w || !h) return;\r\n  renderer.setSize(w, h); camera.aspect = w / h; camera.updateProjectionMatrix();\r\n  bloomComposer.setSize(w, h); finalComposer.setSize(w, h);\r\n}\r\naddEventListener('resize', resize); resize();\r\n// the Shopify theme can reflow the container without a window resize (headers\r\n// collapsing, fonts loading) \u2014 track the viewer element itself\r\nnew ResizeObserver(resize).observe(viewer);\r\n\r\n// \u2550\u2550 State \u2550\u2550\r\nlet manifest = null;\r\nconst gltfCache = new Map();     // partName \u2192 Promise<gltf>\r\nconst slots = new Map();         // slotKey \u2192 { name, group }\r\n// turntable spin \u2014 TIME-based (rad/sec) so ?spin=20 means exactly 20\u00B0/s at any\r\n// display refresh rate. Default matches the old feel (0.003 rad/frame @60fps).\r\nconst SPIN_DEFAULT = 0.18;       // rad/sec \u2248 10.3\u00B0/s\r\nlet spinRadSec = (VID.spin !== null && VID.spin !== '')\r\n  ? (parseFloat(VID.spin) || 0) * Math.PI / 180\r\n  : SPIN_DEFAULT;\r\n// ?controls=1 \u2014 hidden film rig for shooting product video: keyboard control of\r\n// the turntable speed on a level plane, camera level/front/side snaps, and a\r\n// clean-frame toggle that hides the whole options UI.\r\nconst FILM_MODE = new URLSearchParams(location.search).get('controls') === '1';\r\nconst state = { collection: 'studio', style: null, shape: null, display: 'MAX7219',\r\n                ear: null, earSnap: 0, earMaterial: 'TPU', ear2: 'none', ear2Snap: 0,\r\n                fin: null, finSnap: 0, cheek: null, maxColor: 'BLU',\r\n                cheekMode: 'standard', ringStyle: 'dots',   // cheeks: standard | bubble | neopixel; ring look: dots | halo\r\n                ledColor: cheekLedName(new URLSearchParams(location.search).get('led')),   // LIT cheek ring color\r\n                npBacking: npBackingKey(new URLSearchParams(location.search).get('backing')),   // NP cheek backing: white | clear\r\n                logoImage: null, logoMirror: true,   // bubble cheeks + uploaded logo\r\n                facePreset: 'rainbow', faceImage: null,\r\n                faceEyes: 0, faceNose: 0, faceMouth: 0,   // firmware face presets (MAX/Neo)\r\n                visorOff: false,   // preview toggle: modern heads shown without visor+gasket\r\n                colors: {},        // Color Booth: zone \u2192 filament/tint id (deviations only)\r\n                fitOn: false, circIn: 22, glassesOn: false };\r\n// 'Visor only' collection: the viewer shows JUST the visor and the cart sells\r\n// the VS-<style> replacement product \u2014 every other option is moot there.\r\nconst visorOnly = () => state.collection === 'visor';\r\n\r\n// modern styles come from gaskets (all share head_base); legacy styles come\r\n// from standalone head shells (category 'head'), keyed by their style token.\r\nfunction modernStyles() { return [...new Set(partsByCat('gasket').map(styleOf))]; }\r\nfunction legacyStyles() { return [...new Set(partsByCat('head').map(styleOf))]; }\r\nfunction isLegacy(style) { return legacyStyles().includes(style); }\r\nfunction stylesOfType(t) { return t === 'legacy' ? legacyStyles() : modernStyles(); }\r\nfunction collectionStyles(cKey) {\r\n  const seen = new Set();\r\n  return COLLECTIONS[cKey].styleTypes.flatMap(stylesOfType)\r\n    .filter(s => !seen.has(s) && seen.add(s))\r\n    // legacy heads appear only where a `<coll>-<style>` product is registered;\r\n    // modern styles are always offered (full package coverage)\r\n    .filter(s => !isLegacy(s) || KNOWN_HEAD_HANDLES.has(`${COLL_HANDLE[cKey]}-${s}`));\r\n}\r\n// legacy heads can have shape variants (head_mk3_round / _sharp \u2192 round/sharp)\r\nfunction shapesFor(style) {\r\n  if (!isLegacy(style)) return [];\r\n  return partsByCat('head').filter(n => styleOf(n) === style)\r\n    .map(n => n.toLowerCase().split('_').slice(2).join('_')).filter(Boolean);\r\n}\r\nfunction headShell(style, shape) {\r\n  if (!isLegacy(style))\r\n    return Object.entries(manifest.parts).find(([, i]) => i.category === null)?.[0] || null;\r\n  const heads = partsByCat('head').filter(n => styleOf(n) === style);\r\n  if (heads.length <= 1) return heads[0] || null;\r\n  return heads.find(n => n.toLowerCase().endsWith('_' + String(shape).toLowerCase())) || heads[0];\r\n}\r\nfunction allowedDisplays() {\r\n  return COLLECTIONS[state.collection].displays\r\n    .filter(d => displayAvailable(state.style, d));\r\n}\r\n// legacy heads carry no modular fins\r\nconst effectiveFin = () => (isLegacy(state.style) || visorOnly()) ? 'none' : state.fin;\r\n\r\n// Cheeks aren't chosen \u2014 they come WITH the fin: studs with short/long/astro/\r\n// tron, vents with wild. (Other fins ship no cheek for now.)\r\nconst FIN_CHEEK = { fin_short: 'CHEEK_STUD', fin_long: 'CHEEK_STUD', fin_astro: 'CHEEK_STUD',\r\n                    fin_tron: 'CHEEK_STUD', fin_ring: 'CHEEK_STUD', fin_wild: 'CHEEK_VENT' };\r\nconst autoCheek = () => (isLegacy(state.style) || visorOnly()) ? null : (FIN_CHEEK[state.fin] || null);\r\n\r\n// \u2500\u2500 Bubble cheeks: a plate (opaque) + a tinted dome, with the customer's logo\r\n// between them. A customer CHOICE on modern (replaces the auto stud/vent);\r\n// comes DEFAULT on MK3/MK5. Head code: modern\u2192pf, else the legacy style. \u2500\u2500\r\nconst cheekHeadCode = () => isLegacy(state.style) ? state.style : 'pf';\r\nconst cheekPlatePart = () => `cheek_plate_${cheekHeadCode()}`;\r\nconst bubbleDomePart = () => `bubble_${cheekHeadCode()}`;\r\nconst CHEEK_HEAD_SKU = { pf: 'PF', mk3: 'MK3', mk5: 'MK5', mk2: 'MK2', zr: 'ZR' };\r\n// NeoPixel cheek rings need a ProtoFrenz 3.0 controller \u2014 offered only where the\r\n// collection ships PF3 electronics, and only for heads that have an AC-CHEEK-*-NP\r\n// SKU (pf / mk3 / mk5; not mk2 / zr).\r\nconst PF3_COLLECTIONS = new Set(['studio', 'dlux', 'rewired']);\r\nconst neoAvailable = () => !visorOnly() && PF3_COLLECTIONS.has(state.collection)\r\n  && !!CHEEK_HEAD_SKU[cheekHeadCode()];\r\n// Resolve the raw 3-way state into what actually renders for this head/collection:\r\n//  neopixel (rings) \u2192 only when available; legacy always has at least a bubble;\r\n//  modern falls back to standard studs/vents.\r\nconst effectiveCheekMode = () => {\r\n  if (visorOnly()) return 'none';\r\n  if (state.cheekMode === 'neopixel' && neoAvailable()) return 'neopixel';\r\n  if (isLegacy(state.style)) return 'bubble';   // legacy: always a bubble\r\n  return state.cheekMode === 'bubble' ? 'bubble' : 'standard';\r\n};\r\nconst bubbleActive = () => { const m = effectiveCheekMode(); return m === 'bubble' || m === 'neopixel'; };\r\nconst neoActive = () => effectiveCheekMode() === 'neopixel';\r\n// A logo means LIT \u2014 only on a plain bubble (LIT SKU / colour art).\r\nconst hasLogo = () => effectiveCheekMode() === 'bubble' && !!state.logoImage;\r\n// The cheek designer is offered on bubble AND NeoPixel cheeks. On NeoPixel it\r\n// renders in WHITE (the ring provides the colour); it doesn't change the -NP SKU.\r\nconst hasCheekDesign = () => (effectiveCheekMode() === 'bubble' || neoActive()) && !!state.logoImage;\r\n// Luminance mask of the cheek art, used as the alphaMap on NeoPixel cheeks:\r\n// bright (white-drawn) pixels become the lit diffuser cutout, the black\r\n// background stays dark. Can't use the art's alpha \u2014 the Logo Studio bakes an\r\n// opaque black background into the full circle, so alpha is 1 everywhere.\r\nfunction lumaMask(srcTex) {\r\n  const img = srcTex.image; if (!img) return srcTex;\r\n  const c = document.createElement('canvas'); c.width = img.width; c.height = img.height;\r\n  const x = c.getContext('2d');\r\n  x.drawImage(img, 0, 0);\r\n  const id = x.getImageData(0, 0, c.width, c.height), p = id.data;\r\n  for (let i = 0; i < p.length; i += 4) {\r\n    // perceptual luminance \u00D7 source alpha (alphaMap reads the green channel)\r\n    const l = (p[i] * 0.299 + p[i + 1] * 0.587 + p[i + 2] * 0.114) * (p[i + 3] / 255);\r\n    p[i] = p[i + 1] = p[i + 2] = l; p[i + 3] = 255;\r\n  }\r\n  x.putImageData(id, 0, 0);\r\n  return new THREE.CanvasTexture(c);\r\n}\r\n// a conic rainbow the NeoPixel design is filled with (rotated in the render loop\r\n// so the colour swirls through the cutout \u2014 like a ring of RGB LEDs behind it)\r\nlet _rainbowTex = null, cheekSwirl = null;\r\nfunction rainbowTexture() {\r\n  if (_rainbowTex) return _rainbowTex;\r\n  const S = 512, c = document.createElement('canvas'); c.width = c.height = S;\r\n  const x = c.getContext('2d'), cx = S / 2, cy = S / 2;\r\n  for (let a = 0; a < 360; a++) {\r\n    x.beginPath(); x.moveTo(cx, cy);\r\n    x.arc(cx, cy, S, (a - 0.6) * Math.PI / 180, (a + 0.6) * Math.PI / 180); x.closePath();\r\n    x.fillStyle = `hsl(${a}, 100%, 55%)`; x.fill();\r\n  }\r\n  const t = new THREE.CanvasTexture(c);\r\n  t.center.set(0.5, 0.5); t.colorSpace = THREE.SRGBColorSpace;\r\n  _rainbowTex = t; return t;\r\n}\r\n// Clear NeoPixel backing: no diffuser, so the ring's 12 LEDs show through the\r\n// stencil's openings as distinct points (colours chase; the LEDs don't move).\r\n// Ring radius on the decal = rInner\u00B7half / (rFrac\u00B72\u00B7half) = 0.30 / (2 \u00D7 0.42).\r\nlet _npDotsTex = null;\r\nfunction drawNpDots(t) {\r\n  const c = _npDotsTex.image, x = c.getContext('2d'), S = c.width, cx = S / 2;\r\n  x.clearRect(0, 0, S, S);\r\n  x.fillStyle = 'rgba(214,214,222,0.16)'; x.fillRect(0, 0, S, S);   // clear sheet: very light, mostly transparent gray\r\n  const ringR = S / 2 * (0.30 / (2 * 0.42)), dot = S * 0.022;\r\n  for (let i = 0; i < 12; i++) {\r\n    const a = i / 12 * Math.PI * 2, hue = (i * 30 + t * 40) % 360;\r\n    const px = cx + Math.cos(a) * ringR, py = cx + Math.sin(a) * ringR;\r\n    const g = x.createRadialGradient(px, py, 0, px, py, dot * 1.6);   // crisp: no diffusion halo\r\n    g.addColorStop(0, '#ffffff'); g.addColorStop(0.35, `hsl(${hue},100%,62%)`);\r\n    g.addColorStop(0.7, `hsla(${hue},100%,55%,0.6)`); g.addColorStop(1, 'rgba(0,0,0,0)');\r\n    x.fillStyle = g; x.fillRect(px - dot * 1.6, py - dot * 1.6, dot * 3.2, dot * 3.2);\r\n  }\r\n  _npDotsTex.needsUpdate = true;\r\n}\r\nfunction npLightTexture() {\r\n  if (state.npBacking !== 'clear') return rainbowTexture();\r\n  if (!_npDotsTex) {\r\n    const c = document.createElement('canvas'); c.width = c.height = 512;\r\n    _npDotsTex = new THREE.CanvasTexture(c); _npDotsTex.colorSpace = THREE.SRGBColorSpace;\r\n    drawNpDots(0);\r\n  }\r\n  return _npDotsTex;\r\n}\r\n// NeoPixel \u2192 AC-CHEEK-<code>-NP. Blank bubble = BUBBLE, logo = LIT. Modern bills\r\n// either as its own line; legacy bundles the blank bubble in the head (a logo\r\n// upgrades it to a LIT add-on line).\r\nconst skuBubbleCheek = () => {\r\n  const mode = effectiveCheekMode();\r\n  const code = CHEEK_HEAD_SKU[cheekHeadCode()];\r\n  if (!code || mode === 'standard' || mode === 'none') return null;\r\n  if (mode === 'neopixel') return `AC-CHEEK-${code}-NP`;\r\n  if (isLegacy(state.style)) return state.logoImage ? `AC-CHEEK-${code}-LIT` : null;\r\n  return `AC-CHEEK-${code}-${state.logoImage ? 'LIT' : 'BUBBLE'}`;\r\n};\r\n// Logo decal placement in the dome's LOCAL frame (dome is a shallow disc, thin\r\n// along local Z = its normal): PUSH sits the logo along that normal toward the\r\n// flat/outer face, R_FRAC = radius vs dome half-width, DX/DY = centering nudge.\r\n// Tunable by eye like the fit-head knobs.\r\n// Per-head logo placement. The logo auto-uprights (top toward the head's up\r\n// projected onto the dome face); these nudge it IN THE UPRIGHTED PLANE:\r\n// down (m, along \u2212up), side (m, along the in-plane horizontal), push (m, along\r\n// the dome normal, in/out against the bubble), roll (\u00B0 on top of auto-upright),\r\n// rFrac (radius vs dome half-width). Flip a sign if a nudge goes the wrong way.\r\nconst LOGO_ADJ = {\r\n  pf:  { down: 0.04, side: 0, push: -0.005, roll: 0, rFrac: 0.42 },\r\n  mk3: { down: 0,    side: 0, push: 0.002, roll: 0, rFrac: 0.42 },\r\n  mk5: { down: 0,    side: 0, push: 0.002, roll: 0, rFrac: 0.42 },\r\n  mk2: { down: 0,    side: 0, push: 0.002, roll: 0, rFrac: 0.42 },\r\n  zr:  { down: 0,    side: 0, push: 0.002, roll: 0, rFrac: 0.42 },\r\n};\r\n\r\nconst partsByCat = (cat) => Object.entries(manifest.parts)\r\n  .filter(([, i]) => i.category === cat).map(([n]) => n);\r\nconst styleOf = (n) => n.toLowerCase().split('_')[1] || '';\r\nconst pretty = (n) => n.replace(/^(frame|fin|ear2|ear|cheek|visor|gasket)_/i, '')\r\n                       .replace(/_/g, ' ').toLowerCase();\r\n\r\n// \u2550\u2550 Loading & assembly (harness-proven chain: holder \u2192 rotator@pivot) \u2550\u2550\r\nfunction applyRotation(rotator, rot) {\r\n  const r = rot || [0, 0, 0];\r\n  rotator.rotation.set(r[0]*Math.PI/180, r[1]*Math.PI/180, r[2]*Math.PI/180);\r\n}\r\n\r\n// \u2550\u2550 Protected part files \u2550\u2550 the Shopify upload set is built by\r\n// protect_assets.py: each part is roughened, distorted (non-uniform scale),\r\n// meshopt-compressed, then XOR-scrambled under an opaque name; the manifest is\r\n// scrambled too. Here we unscramble in memory and undo the distortion exactly\r\n// (normals were pre-corrected), so everything downstream sees true geometry.\r\n// Clean GLBs (local glb_out/) start with 'glTF' and load untouched.\r\n// ASSET_KEY / fnv1a / keystream / distortFactors MUST match protect_assets.py.\r\nconst ASSET_KEY = 'pf-cfg-3d-2026';\r\nfunction fnv1a(str) {\r\n  let h = 0x811c9dc5;\r\n  for (const b of new TextEncoder().encode(str)) { h ^= b; h = Math.imul(h, 0x01000193) >>> 0; }\r\n  return h >>> 0;\r\n}\r\nfunction unscramble(buf, salt) {\r\n  const b = new Uint8Array(buf.slice(0));\r\n  let s = fnv1a(`${ASSET_KEY}|${salt}`) || 1;\r\n  for (let i = 0; i < b.length; i++) {\r\n    s = (s ^ (s << 13)) >>> 0; s = (s ^ (s >>> 17)) >>> 0; s = (s ^ (s << 5)) >>> 0;\r\n    b[i] ^= s & 255;\r\n  }\r\n  return b.buffer;\r\n}\r\nfunction distortFactors(name) {\r\n  return ['x', 'y', 'z'].map(ax => {\r\n    const h = fnv1a(`${ASSET_KEY}|d|${name}|${ax}`);\r\n    return 1 + ((h >>> 16) & 1 ? 1 : -1) * (0.025 + 0.015 * ((h % 10000) / 10000));\r\n  });\r\n}\r\nconst isGlb = (buf) => { const h = new Uint8Array(buf, 0, 4); return h[0] === 0x67 && h[1] === 0x6c && h[2] === 0x54 && h[3] === 0x46; };\r\n// localhost-only: ?assets=protected loads the built upload set to verify it\r\nconst PROTECTED_TEST = /^(localhost|127\\.0\\.0\\.1)$/.test(location.hostname)\r\n  && new URLSearchParams(location.search).get('assets') === 'protected';\r\nconst PART_BASE = PROTECTED_TEST ? './shopify_upload_protected/' : ASSET_BASE;\r\nconst PART_MANIFEST_URL = PROTECTED_TEST ? PART_BASE + 'pfa.bin' : MANIFEST_URL;\r\nfunction loadGLTF(name) {\r\n  if (!gltfCache.has(name)) {\r\n    const info = manifest.parts[name];\r\n    gltfCache.set(name, fetch(PART_BASE + info.path + ASSET_VER)\r\n      .then(r => { if (!r.ok) throw new Error(`${info.path} \u2192 HTTP ${r.status}`); return r.arrayBuffer(); })\r\n      .then(async (buf) => {\r\n        const plain = isGlb(buf);\r\n        const gltf = await gltfLoader.parseAsync(plain ? buf : unscramble(buf, info.path), '');\r\n        if (!plain) { const f = distortFactors(name); gltf.scene.scale.set(1 / f[0], 1 / f[1], 1 / f[2]); }\r\n        return gltf;\r\n      }));\r\n  }\r\n  return gltfCache.get(name);\r\n}\r\n\r\nasync function buildPart(name, { offset, rotation } = {}) {\r\n  const info = manifest.parts[name];\r\n  const gltf = await loadGLTF(name);\r\n  const isVisor = info.category === 'visor';\r\n  const isBubble = info.category === 'bubble';   // tinted PETG cheek dome\r\n  const isEar = info.category === 'ear' || info.category === 'ear2';\r\n  // visor + bubble: depthWrite off + high renderOrder so they never depth-clip\r\n  // what's behind them (transparent-sorting fix). Visor bumped +1/3 tint per Blair\r\n  // (0.55\u21920.73); the cheek bubble stays clearer so the logo reads through it.\r\n  const material = (isVisor || isBubble)\r\n    ? new THREE.MeshPhysicalMaterial({ color: 0x14141c, transparent: true,\r\n        opacity: isBubble ? 0.65 : 0.73, roughness: 0.05, metalness: 0.1, clearcoat: 1,\r\n        depthWrite: false })\r\n    : new THREE.MeshStandardMaterial({ color: isEar ? COLOR_WHITE : COLOR_BLACK,\r\n        roughness: 0.38, metalness: 0.08 });\r\n\r\n  const off = offset ?? info.offset ?? [0, 0, 0];\r\n  const rot = rotation ?? info.rotation ?? [0, 0, 0];\r\n  // pivot fallback must match the harness (bbox center) so a head positioned\r\n  // there rotates identically here\r\n  const pivot = info.pivot\r\n    ? new THREE.Vector3(...info.pivot)\r\n    : new THREE.Box3().setFromObject(gltf.scene).getCenter(new THREE.Vector3());\r\n\r\n  const chain = (content) => {\r\n    const holder = new THREE.Group();\r\n    holder.position.set(...off);\r\n    const rotator = new THREE.Group();\r\n    rotator.position.copy(pivot);\r\n    applyRotation(rotator, rot);\r\n    const inner = new THREE.Group();\r\n    inner.position.copy(pivot.clone().negate());\r\n    inner.add(content);\r\n    rotator.add(inner);\r\n    holder.add(rotator);\r\n    return holder;\r\n  };\r\n\r\n  // Painted color-block regions (Accent Painter tool): accentTris on the\r\n  // manifest entry carves those triangles into their own meshes with a second\r\n  // material so the Color Booth colors them independently\r\n  const accentTris = (isVisor || isBubble) ? null : accentTrisFor(name);\r\n  const accentMat = accentTris ? new THREE.MeshStandardMaterial({ color: COLOR_BLACK,\r\n    roughness: 0.38, metalness: 0.08 }) : null;\r\n\r\n  const scene0 = gltf.scene.clone();\r\n  scene0.traverse(o => { if (o.isMesh) { o.material = material; if (isVisor || isBubble) o.renderOrder = 2; } });\r\n  if (accentTris) splitAccent(scene0, accentTris, accentMat);\r\n  const group = new THREE.Group();\r\n  group.add(chain(scene0));\r\n  if (manifest.mirror?.[info.category]) {\r\n    const wrap = new THREE.Group();\r\n    wrap.scale.x = -1;\r\n    const clone = gltf.scene.clone();\r\n    clone.traverse(o => { if (o.isMesh) { o.material = material; if (isVisor || isBubble) o.renderOrder = 2; } });\r\n    if (accentTris) splitAccent(clone, accentTris, accentMat);\r\n    wrap.add(chain(clone));\r\n    group.add(wrap);\r\n  }\r\n  return group;\r\n}\r\n\r\n// Carve accent triangles (global ranges over the part's meshes, in traversal\r\n// order \u2014 the Accent Painter counts them the same way) out of each mesh into\r\n// a sibling mesh flagged userData.pfAccent. Attributes are shared; only the\r\n// index buffers differ, so the cost is two draw calls per painted mesh.\r\nfunction splitAccent(scene, ranges, accentMat) {\r\n  const acc = new Set();\r\n  for (let i = 0; i < ranges.length; i += 2)\r\n    for (let t = ranges[i], e = ranges[i] + ranges[i + 1]; t < e; t++) acc.add(t);\r\n  const meshes = [];\r\n  scene.traverse(o => { if (o.isMesh) meshes.push(o); });\r\n  let tOff = 0;\r\n  for (const m of meshes) {\r\n    const g = m.geometry, idx = g.index;\r\n    const triCount = ((idx ? idx.count : g.attributes.position.count) / 3) | 0;\r\n    const a = [], b = [];\r\n    for (let t = 0; t < triCount; t++) {\r\n      const dst = acc.has(tOff + t) ? a : b;\r\n      const i3 = t * 3;\r\n      if (idx) dst.push(idx.getX(i3), idx.getX(i3 + 1), idx.getX(i3 + 2));\r\n      else dst.push(i3, i3 + 1, i3 + 2);\r\n    }\r\n    tOff += triCount;\r\n    if (!a.length) continue;   // no accent triangles in this mesh\r\n    const carve = (tris, mat, isAcc) => {\r\n      const ng = new THREE.BufferGeometry();\r\n      for (const [k, attr] of Object.entries(g.attributes)) ng.setAttribute(k, attr);\r\n      ng.setIndex(tris);\r\n      const nm = new THREE.Mesh(ng, mat);\r\n      nm.position.copy(m.position); nm.quaternion.copy(m.quaternion); nm.scale.copy(m.scale);\r\n      nm.renderOrder = m.renderOrder;\r\n      if (isAcc) nm.userData.pfAccent = true;\r\n      m.parent.add(nm);\r\n    };\r\n    if (b.length) carve(b, m.material, false);\r\n    carve(a, accentMat, true);\r\n    m.parent.remove(m);\r\n  }\r\n}\r\n\r\nconst slotTokens = new Map();    // slot \u2192 token; guards rapid-click races\r\n\r\n// part-swap grow-in (processed in tick); disabled during the initial assembly so\r\n// the first paint doesn't cascade \u2014 enabled once boot completes\r\nconst swapAnims = [];\r\nlet swapAnimsEnabled = false;\r\nfunction animateSwapIn(group) {\r\n  if (!swapAnimsEnabled) return;\r\n  group.scale.setScalar(0.82);\r\n  swapAnims.push({ group, t0: clock.getElapsedTime() });\r\n}\r\n\r\nasync function setSlot(key, name, opts) {\r\n  const token = Symbol();\r\n  slotTokens.set(key, token);\r\n  const prev = slots.get(key);\r\n  if (!name || name === 'none' || !manifest.parts[name]) {  // empty / unknown slot\r\n    if (prev) { root.remove(prev.group); slots.delete(key); }\r\n    return;\r\n  }\r\n  const group = await buildPart(name, opts);\r\n  if (slotTokens.get(key) !== token) return;   // superseded by a later click\r\n  const cur = slots.get(key);\r\n  if (cur) root.remove(cur.group);\r\n  root.add(group);\r\n  if (!cur || cur.name !== name) animateSwapIn(group);   // only on real changes\r\n  slots.set(key, { name, group });\r\n}\r\n\r\n// Repaint every slot from the Color Booth state. Parts always BUILD in stock\r\n// colors (buildPart), then this recolors in place \u2014 so it must run after the\r\n// slot jobs in applyState and after any swatch click. Skips textured meshes\r\n// (decals/logos) and, outside the visor zone, transparent ones (bubble dome).\r\nfunction applyColors() {\r\n  // visor-only sells the stock VS-<style> product \u2014 always previewed stock\r\n  const hexFor = (zone) => visorOnly()\r\n    ? zonePalette(zone).find(c => c.id === COLOR_ZONES[zone].def).hex\r\n    : zoneColor(zone).hex;\r\n  for (const [key, { group }] of slots) {\r\n    if (key === 'bubble') {   // tinted PETG dome always matches the visor tint\r\n      const hex = hexFor('visor');\r\n      group.traverse(o => {\r\n        if (o.isMesh && o.material?.transparent && !o.material.map) o.material.color.set(hex);\r\n      });\r\n      continue;\r\n    }\r\n    const zone = SLOT_ZONE[key];\r\n    if (!zone) continue;   // display mount (frame slot) etc. stay stock black\r\n    const hex = hexFor(zone);\r\n    const accentHex = key === 'fin' ? hexFor('finAccent') : null;\r\n    group.traverse(o => {\r\n      if (!o.isMesh || !o.material || o.material.map) return;\r\n      if (zone === 'visor' ? o.material.transparent : !o.material.transparent)\r\n        o.material.color.set(o.userData.pfAccent && accentHex ? accentHex : hex);\r\n    });\r\n  }\r\n}\r\n\r\n// \u2550\u2550 Custom cheek image (bubble cheeks: image under a clear window) \u2550\u2550\r\nconst DEV_CHEEK = new URLSearchParams(location.search).has('devcheek');\r\nconst isCustomCheek = (n) => !!n && n !== 'none' && (/bubble|custom/i.test(n) || DEV_CHEEK);\r\nlet cheekTexture = null, cheekThumb = null, cheekImageName = null;\r\nlet cheekDecals = [];\r\n\r\nfunction circleTexture(img) {\r\n  const S = 512;\r\n  const cv = document.createElement('canvas'); cv.width = cv.height = S;\r\n  const c = cv.getContext('2d');\r\n  c.beginPath(); c.arc(S/2, S/2, S/2, 0, Math.PI*2); c.clip();\r\n  const sc = Math.max(S / img.width, S / img.height);\r\n  c.drawImage(img, (S - img.width*sc)/2, (S - img.height*sc)/2, img.width*sc, img.height*sc);\r\n  const tex = new THREE.CanvasTexture(cv);\r\n  tex.colorSpace = THREE.SRGBColorSpace;\r\n  return tex;\r\n}\r\n\r\nasync function rebuildCheekDecals() {\r\n  for (const g of cheekDecals) root.remove(g);\r\n  cheekDecals = [];\r\n  if (!isCustomCheek(state.cheek) || !cheekTexture || !manifest.parts[state.cheek]) return;\r\n  const gltf = await loadGLTF(state.cheek);\r\n  const box = new THREE.Box3().setFromObject(gltf.scene);\r\n  const size = new THREE.Vector3(); box.getSize(size);\r\n  const center = new THREE.Vector3(); box.getCenter(center);\r\n  const r = Math.min(size.y, size.z) * 0.38;\r\n  // independent left/right decals (not mirrored geometry) so the image\r\n  // reads correctly on both sides\r\n  for (const s of [-1, 1]) {\r\n    const g = new THREE.Group();\r\n    const img = new THREE.Mesh(new THREE.CircleGeometry(r, 48),\r\n      new THREE.MeshBasicMaterial({ map: cheekTexture }));\r\n    img.rotation.y = s < 0 ? -Math.PI/2 : Math.PI/2;   // face outward\r\n    img.renderOrder = 1;\r\n    // the clear bubble window \u2014 same material family as the visor\r\n    const dome = new THREE.Mesh(new THREE.CircleGeometry(r * 1.06, 48),\r\n      new THREE.MeshPhysicalMaterial({ color: 0x14141c, transparent: true,\r\n        opacity: 0.3, roughness: 0.05, metalness: 0.1, clearcoat: 1,\r\n        depthWrite: false }));\r\n    dome.rotation.y = img.rotation.y;\r\n    dome.position.x = s * 0.0015;\r\n    dome.renderOrder = 2;\r\n    g.add(img); g.add(dome);\r\n    const outward = s < 0 ? box.min.x : -box.min.x;\r\n    g.position.set(outward + s * 0.0015, center.y, center.z);\r\n    root.add(g);\r\n    cheekDecals.push(g);\r\n  }\r\n}\r\n\r\n// The uploaded logo, circle-masked, sits just under the tinted dome on each\r\n// cheek \u2014 placed in the dome's local frame so it rides the harness position.\r\nlet logoDecals = [];\r\nasync function rebuildLogoDecals() {\r\n  for (const g of logoDecals) root.remove(g);\r\n  logoDecals = [];\r\n  if (!bubbleActive() || !cheekTexture) return;\r\n  // NeoPixel cheeks: the design becomes a diffuser cutout \u2014 its bright pixels let\r\n  // the swirling rainbow through, the black rest of the plate stays dark.\r\n  const neo = neoActive();\r\n  const neoMask = neo ? lumaMask(cheekTexture) : null;   // brightness \u2192 alphaMap\r\n  cheekSwirl = neo ? npLightTexture() : null;            // rotated in the render loop\r\n  const name = bubbleDomePart();\r\n  const info = manifest.parts[name];\r\n  if (!info) return;\r\n  const gltf = await loadGLTF(name);\r\n  const box = new THREE.Box3().setFromObject(gltf.scene);\r\n  const size = box.getSize(new THREE.Vector3());\r\n  // the dome rides the legacy group shift (asmOpt); the logo must ride it too,\r\n  // or it drifts from the dome by that shift on legacy heads\r\n  const own = info.offset ?? [0, 0, 0];\r\n  const g = isLegacy(state.style) ? (manifest.legacy_shift?.[state.style]?.offset || [0, 0, 0]) : [0, 0, 0];\r\n  const off = new THREE.Vector3(own[0] + g[0], own[1] + g[1], own[2] + g[2]);\r\n  const rot = info.rotation ?? [0, 0, 0];\r\n  const pivot = info.pivot ? new THREE.Vector3(...info.pivot) : box.getCenter(new THREE.Vector3());\r\n  const adj = LOGO_ADJ[cheekHeadCode()] || LOGO_ADJ.pf;\r\n  const r = Math.min(size.x, size.y) * adj.rFrac;\r\n  const d2r = Math.PI / 180;\r\n  const Rmat = new THREE.Matrix4().makeRotationFromEuler(\r\n    new THREE.Euler(rot[0] * d2r, rot[1] * d2r, rot[2] * d2r, 'XYZ'));\r\n  // dome centre (+push along its normal) and facing normal, in model space\r\n  const centerMain = off.clone().add(pivot)\r\n    .add(new THREE.Vector3(0, 0, adj.push).applyMatrix4(Rmat));\r\n  const normalMain = new THREE.Vector3(0, 0, 1).applyMatrix4(Rmat).normalize();\r\n\r\n  // Place a logo facing `normal` at `center`, auto-uprighted (top \u2192 head's up\r\n  // projected), then nudged down/side in that uprighted plane. mirror=true\r\n  // flips the art (mesh scale, not basis \u2014 reflections can't be quaternions).\r\n  const place = (center, normal, mirror) => {\r\n    const z = normal.clone().normalize();\r\n    let up = new THREE.Vector3(0, 0, 1).sub(z.clone().multiplyScalar(z.z));   // model-up \u22A5 normal\r\n    if (up.lengthSq() < 1e-6) up.set(0, 1, 0);\r\n    up.normalize();\r\n    if (adj.roll) up.applyAxisAngle(z, adj.roll * d2r);\r\n    const x = up.clone().cross(z).normalize();\r\n    const pos = center.clone()\r\n      .add(up.clone().multiplyScalar(-adj.down))     // down = along \u2212up\r\n      .add(x.clone().multiplyScalar(adj.side));      // side = along in-plane horizontal\r\n    // Bubble LIT: additive colour logo, drawn over the dome so it GLOWS through\r\n    // the tint. NeoPixel: the design shape (alphaMap) is filled with the swirling\r\n    // rainbow (map) and put on the bloom layer, so the cutout lights up in colour\r\n    // while the rest of the plate stays dark. depthTest STAYS ON so the opaque\r\n    // plate/head occlude it (only visible through the bubble).\r\n    const m = new THREE.Mesh(new THREE.CircleGeometry(r, 48), neo\r\n      ? new THREE.MeshBasicMaterial({ map: npLightTexture(), alphaMap: neoMask, transparent: true,\r\n          side: THREE.DoubleSide, depthWrite: false })\r\n      : new THREE.MeshBasicMaterial({ map: cheekTexture, color: cheekLedHex(), transparent: true, side: THREE.DoubleSide,\r\n          blending: THREE.AdditiveBlending, depthWrite: false }));\r\n    if (neo) m.layers.enable(BLOOM_LAYER);   // the lit design glows\r\n    m.quaternion.setFromRotationMatrix(new THREE.Matrix4().makeBasis(x, up, z));\r\n    if (mirror) m.scale.x = -1;   // mirror the art (DoubleSide, so winding flip is fine)\r\n    m.position.copy(pos);\r\n    m.renderOrder = 3;   // over the tinted dome (renderOrder 2) \u2014 glows through\r\n    m.visible = !state.fitOn;   // hidden during fit check (head goes x-ray)\r\n    root.add(m); logoDecals.push(m);\r\n  };\r\n  place(centerMain, normalMain, false);\r\n  // other cheek = reflection across X=0 (matches the dome's scale.x=-1 twin).\r\n  // Mirrored (default): a true reflection \u2014 an arrow points toward the face on\r\n  // BOTH cheeks. Same: the art reads identically on both sides (better for text).\r\n  place(new THREE.Vector3(-centerMain.x, centerMain.y, centerMain.z),\r\n        new THREE.Vector3(-normalMain.x, normalMain.y, normalMain.z),\r\n        state.logoMirror);\r\n}\r\n\r\n// \u2500\u2500 NeoPixel cheek rings: concentric 12-inner + 16-outer rings on each cheek,\r\n// placed in the bubble dome's frame (like the logo) and put on BLOOM_LAYER so\r\n// they glow through the tinted dome. Two looks: discrete dots or smooth halos;\r\n// animated (rainbow chase + breathe) in renderCheekRings(). \u2500\u2500\r\nconst RING_ADJ = {\r\n  pf:  { push: -0.004, down: 0.04, side: 0, rInner: 0.30, rOuter: 0.58 },\r\n  mk3: { push:  0.001, down: 0,    side: 0, rInner: 0.30, rOuter: 0.58 },\r\n  mk5: { push:  0.001, down: 0,    side: 0, rInner: 0.30, rOuter: 0.58 },\r\n  mk2: { push:  0.001, down: 0,    side: 0, rInner: 0.30, rOuter: 0.58 },\r\n  zr:  { push:  0.001, down: 0,    side: 0, rInner: 0.30, rOuter: 0.58 },\r\n};\r\nlet cheekRings = [];\r\nasync function rebuildCheekRings() {\r\n  for (const g of cheekRings) root.remove(g);\r\n  cheekRings = [];\r\n  // no design \u2192 show the bare 12-LED ring; WITH a design the design itself is the\r\n  // lit cutout (rebuildLogoDecals), so skip the ring.\r\n  if (!neoActive() || hasCheekDesign()) return;\r\n  const name = bubbleDomePart();\r\n  const info = manifest.parts[name];\r\n  if (!info) return;\r\n  const gltf = await loadGLTF(name);\r\n  const box = new THREE.Box3().setFromObject(gltf.scene);\r\n  const size = box.getSize(new THREE.Vector3());\r\n  const own = info.offset ?? [0, 0, 0];\r\n  const gs = isLegacy(state.style) ? (manifest.legacy_shift?.[state.style]?.offset || [0, 0, 0]) : [0, 0, 0];\r\n  const off = new THREE.Vector3(own[0] + gs[0], own[1] + gs[1], own[2] + gs[2]);\r\n  const rot = info.rotation ?? [0, 0, 0];\r\n  const pivot = info.pivot ? new THREE.Vector3(...info.pivot) : box.getCenter(new THREE.Vector3());\r\n  const adj = RING_ADJ[cheekHeadCode()] || RING_ADJ.pf;\r\n  const d2r = Math.PI / 180;\r\n  const Rmat = new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(rot[0]*d2r, rot[1]*d2r, rot[2]*d2r, 'XYZ'));\r\n  const half = Math.min(size.x, size.y) * 0.5;\r\n  const rIn = half * adj.rInner;   // single 12-LED ring (rOuter no longer used)\r\n  const centerMain = off.clone().add(pivot).add(new THREE.Vector3(0, 0, adj.push).applyMatrix4(Rmat));\r\n  const normalMain = new THREE.Vector3(0, 0, 1).applyMatrix4(Rmat).normalize();\r\n\r\n  const mkMat = () => new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true,\r\n    side: THREE.DoubleSide, depthWrite: false });\r\n  const buildRing = (center, normal) => {\r\n    const z = normal.clone().normalize();\r\n    let up = new THREE.Vector3(0, 0, 1).sub(z.clone().multiplyScalar(z.z));\r\n    if (up.lengthSq() < 1e-6) up.set(0, 1, 0);\r\n    up.normalize();\r\n    const x = up.clone().cross(z).normalize();\r\n    // same down/side nudge the logo uses (down runs along the cheek's tilted\r\n    // up-axis, so it also corrects the slight sideways drift)\r\n    const pos = center.clone()\r\n      .add(up.clone().multiplyScalar(-adj.down))\r\n      .add(x.clone().multiplyScalar(adj.side));\r\n    const g = new THREE.Group();\r\n    g.quaternion.setFromRotationMatrix(new THREE.Matrix4().makeBasis(x, up, z));\r\n    g.position.copy(pos);\r\n    const nodes = [];\r\n    // ships with a single 12-LED ring (the outer 16-LED ring was dropped)\r\n    if (state.ringStyle === 'dots') {\r\n      const dotR = half * 0.055, count = 12;\r\n      for (let i = 0; i < count; i++) {\r\n        const a = (i / count) * Math.PI * 2;\r\n        const m = new THREE.Mesh(new THREE.CircleGeometry(dotR, 12), mkMat());\r\n        m.position.set(Math.cos(a) * rIn, Math.sin(a) * rIn, 0);\r\n        m.layers.enable(BLOOM_LAYER); m.renderOrder = 3;\r\n        g.add(m); nodes.push({ mesh: m, phase: a });\r\n      }\r\n    } else {   // halo: one smooth ring\r\n      const w = half * 0.05;\r\n      const m = new THREE.Mesh(new THREE.RingGeometry(rIn - w/2, rIn + w/2, 64), mkMat());\r\n      m.layers.enable(BLOOM_LAYER); m.renderOrder = 3;\r\n      g.add(m); nodes.push({ mesh: m, phase: 0 });\r\n    }\r\n    g.userData.nodes = nodes;\r\n    g.visible = !state.fitOn;\r\n    root.add(g); cheekRings.push(g);\r\n  };\r\n  buildRing(centerMain, normalMain);\r\n  buildRing(new THREE.Vector3(-centerMain.x, centerMain.y, centerMain.z),\r\n            new THREE.Vector3(-normalMain.x, normalMain.y, normalMain.z));\r\n}\r\nfunction renderCheekRings(t) {\r\n  if (cheekSwirl && cheekSwirl === _npDotsTex) drawNpDots(t);   // clear: fixed LEDs, colours chase\r\n  else if (cheekSwirl) cheekSwirl.rotation = t * 0.7;   // swirl the rainbow through the NeoPixel design\r\n  if (!cheekRings.length) return;\r\n  const breathe = 0.55 + 0.45 * (0.5 + 0.5 * Math.sin(t * 1.5));   // gentle pulse\r\n  for (const g of cheekRings) {\r\n    g.visible = !state.fitOn;\r\n    for (const n of g.userData.nodes) {\r\n      const hue = ((t * 0.11) + n.phase / (Math.PI * 2)) % 1;      // rainbow chase around the ring\r\n      n.mesh.material.color.setHSL(hue, 1, 0.28 + 0.32 * breathe);\r\n    }\r\n  }\r\n}\r\n\r\nlet logoFile = null;   // the actual uploaded File \u2014 attached to the cart line item\r\n// back to a plain bubble: drop the cheek art (the Logo Studio keeps its canvas,\r\n// so reopening it brings the design back)\r\nfunction clearCheekArt() {\r\n  cheekTexture = null; cheekThumb = null; cheekImageName = null;\r\n  logoFile = null; logoMaskFile = null; state.logoImage = null;\r\n  document.getElementById('cheek-img-name').textContent =\r\n    'Design your cheek art in the Logo Studio \u2014 it shows under the tinted bubble on both cheeks.';\r\n}\r\nlet logoMaskFile = null;   // cheek-art popup: the light mask PNG (black = blocks light) \u2014 only when something is blocked\r\ndocument.getElementById('cheek-img').onchange = (e) => {\r\n  const f = e.target.files[0];\r\n  if (!f) return;\r\n  logoFile = f; logoMaskFile = null;   // a direct upload carries no light mask\r\n  const img = new Image();\r\n  img.onload = () => {\r\n    cheekTexture = circleTexture(img);\r\n    const tc = document.createElement('canvas'); tc.width = tc.height = 96;\r\n    tc.getContext('2d').drawImage(img, 0, 0, 96, 96);\r\n    cheekThumb = tc.toDataURL('image/jpeg', 0.7);   // small order-reference thumb\r\n    cheekImageName = f.name;\r\n    state.logoImage = f.name;\r\n    document.getElementById('cheek-img-name').textContent = f.name + ' \u2014 shows under the bubble';\r\n    rebuildLogoDecals();\r\n    rebuildCheekRings();   // NeoPixel: a design replaces the bare ring\r\n    renderUI();\r\n    URL.revokeObjectURL(img.src);\r\n  };\r\n  img.src = URL.createObjectURL(f);\r\n};\r\n\r\n// HUB75 Starter face upload \u2014 the WF1 controller shows one static image; we\r\n// render it (pixelated to the panel) in the preview only (not attached to cart)\r\ndocument.getElementById('face-upload').onchange = (e) => {\r\n  const f = e.target.files[0];\r\n  if (!f) return;\r\n  const img = new Image();\r\n  img.onload = () => {\r\n    const bm = document.createElement('canvas'); bm.width = 128; bm.height = 32;\r\n    const bx = bm.getContext('2d');\r\n    bx.imageSmoothingEnabled = false;\r\n    bx.drawImage(img, 0, 0, 128, 32);      // fit to panel aspect (rescaled at draw)\r\n    faceBitmap = bm;\r\n    state.faceImage = f.name;\r\n    document.getElementById('face-img-name').textContent = f.name;\r\n    renderUI();\r\n    URL.revokeObjectURL(img.src);\r\n  };\r\n  img.src = URL.createObjectURL(f);\r\n};\r\n\r\n// dev hook for automated smoke tests\r\nwindow.__cfgDev = {\r\n  setCheekImage(dataUrl, name = 'dev.png') {\r\n    const img = new Image();\r\n    img.onload = () => {\r\n      cheekTexture = circleTexture(img);\r\n      cheekImageName = name; state.logoImage = name;\r\n      document.getElementById('cheek-img-name').textContent = name;\r\n      rebuildLogoDecals();\r\n      rebuildCheekRings();\r\n    };\r\n    img.src = dataUrl;\r\n  },\r\n  logoInfo: () => {\r\n    const r = v => [+v.x.toFixed(3), +v.y.toFixed(3), +v.z.toFixed(3)];\r\n    const b = slots.get('bubble');\r\n    const domeC = b ? new THREE.Box3().setFromObject(b.group).applyMatrix4(worldToRoot()).getCenter(new THREE.Vector3()) : null;\r\n    const logoC = logoDecals[0] ? logoDecals[0].getWorldPosition(new THREE.Vector3()).applyMatrix4(worldToRoot()) : null;\r\n    return { decals: logoDecals.length, hasTexture: !!cheekTexture, dome: bubbleDomePart(),\r\n      domeCenter: domeC ? r(domeC) : null, logoCenter: logoC ? r(logoC) : null,\r\n      logoVsDome: (domeC && logoC) ? r(logoC.clone().sub(domeC)) : null,\r\n      mirror: state.logoMirror, scales: logoDecals.map(d => +d.scale.x.toFixed(2)) };\r\n  },\r\n  partCenters: () => {\r\n    const out = {};\r\n    for (const [k, v] of slots) {\r\n      const c = new THREE.Box3().setFromObject(v.group).applyMatrix4(worldToRoot())\r\n        .getCenter(new THREE.Vector3());\r\n      out[k] = [+c.x.toFixed(3), +c.y.toFixed(3), +c.z.toFixed(3)];\r\n    }\r\n    return out;\r\n  },\r\n  decalCount: () => cheekDecals.length,\r\n  glowInfo: () => ({ display: state.display, spillIntensity: +spill.intensity.toFixed(3),\r\n    spillColor: '#' + spill.color.getHexString(), spillPos: [+spill.position.x.toFixed(3),\r\n    +spill.position.y.toFixed(3), +spill.position.z.toFixed(3)],\r\n    bloom: { strength: bloomPass.strength, threshold: bloomPass.threshold, radius: bloomPass.radius },\r\n    usingComposer: ledPanels.length > 0, glowScale: glowScale(),\r\n    panels: ledPanels.length }),\r\n  forceFrame: () => { const t = clock.getElapsedTime(); renderLED(t); updateSpill(t);\r\n    let glowErr = null;\r\n    if (ledPanels.length) { try { renderGlow(); } catch (e) { glowErr = String(e); } }\r\n    else renderer.render(scene, camera);\r\n    return { ...window.__cfgDev.glowInfo(), glowErr, fitFwd: FIT_FWD }; },\r\n  overlap: (a = 'fin', b = 'ear') => partsOverlap(a, b),\r\n  spinInfo: () => ({ rootPos: [+root.position.x.toFixed(4), +root.position.y.toFixed(4), +root.position.z.toFixed(4)],\r\n    spinnerRotZ: +spinner.rotation.z.toFixed(3), anims: swapAnims.length,\r\n    cached: gltfCache.size, parts: Object.keys(manifest?.parts || {}).length }),\r\n  faceInfo: (t) => {   // firmware-face debug: what got drawn where\r\n    renderLED(t ?? clock.getElapsedTime());\r\n    const d = faceCtx.getImageData(0, 0, 128, 32).data;\r\n    const litIn = (r) => { let n = 0;\r\n      for (let y = Math.max(0, Math.floor(r.y0)); y < Math.min(32, Math.ceil(r.y1)); y++)\r\n        for (let x = Math.max(0, Math.floor(r.x0)); x < Math.min(128, Math.ceil(r.x1)); x++)\r\n          if (d[(y * 128 + x) * 4] > 40) n++;\r\n      return n; };\r\n    const rects = {};\r\n    for (const [k, r] of Object.entries(moduleRects))\r\n      rects[k] = { x: [+r.x0.toFixed(1), +r.x1.toFixed(1)], y: [+r.y0.toFixed(1), +r.y1.toFixed(1)], lit: litIn(r) };\r\n    const grids = {};\r\n    if (panelGrids) for (const [k, g] of Object.entries(panelGrids))\r\n      grids[k] = { size: [g[0].length, g.length], lit: g.flat().filter(Boolean).length };\r\n    return { facesLoaded: !!FACES, firmwareMode: !!panelGrids, grids, rects }; },\r\n  fitInfo: () => {\r\n    if (!mannequin) return null;\r\n    const r = v => [+v.x.toFixed(3), +v.y.toFixed(3), +v.z.toFixed(3)];\r\n    const mb = new THREE.Box3().setFromObject(mannequin).applyMatrix4(worldToRoot());  // model space\r\n    const out = { headMin: r(mb.min), headMax: r(mb.max), headCenter: r(mb.getCenter(new THREE.Vector3())),\r\n                  groupPos: r(mannequin.position), scale: +mannequin.scale.x.toFixed(4) };\r\n    const base = slots.get('base');\r\n    if (base) {\r\n      const bb = new THREE.Box3().setFromObject(base.group).applyMatrix4(worldToRoot());\r\n      out.baseMin = r(bb.min); out.baseMax = r(bb.max); out.baseCenter = r(bb.getCenter(new THREE.Vector3()));\r\n    }\r\n    return out;\r\n  },\r\n  probe: () => ({ style: state.style, shape: state.shape, collection: state.collection,\r\n    heads: partsByCat('head'), legacyStyles: legacyStyles(),\r\n    isLegacyMk3: isLegacy('mk3'), shapesMk3: shapesFor('mk3'),\r\n    studioStyles: collectionStyles('studio'),\r\n    slots: Object.fromEntries([...slots].map(([k, v]) => [k, v.name])),\r\n    baseCenter: (() => { const b = slots.get('base');\r\n      if (!b) return null;\r\n      const c = new THREE.Box3().setFromObject(b.group).getCenter(new THREE.Vector3());\r\n      return [c.x, c.y, c.z]; })() }),\r\n};\r\n\r\n// \u2550\u2550 Logo Studio \u2014 draw or adapt customer art into a print-ready cheek circle \u2550\u2550\r\n// Layers: black background + a transformable IMAGE layer (with UV-print cleanup:\r\n// brightness/contrast/posterize/remove-a-color) + a PAINT layer (brush/eraser,\r\n// optional symmetry). Everything inside the circle prints; outside auto-fills\r\n// black (which the additive dome decal renders as invisible \u2014 matches the LIT\r\n// cheek exactly). Output feeds the normal cheek pipeline: cheekTexture for the\r\n// 3D preview + a real File so the multipart cart attach ships it on the order.\r\n// \u2500\u2500 Cheek LED color UI (Logo Studio row + configurator cheek row) \u2500\u2500\r\n// LIT is possible on a plain bubble cheek; not on NeoPixel (RGB ring), not in\r\n// the Etsy flow (later), not in a popup opened for an -NP variant.\r\nfunction ledLitPossible() {\r\n  return !ART_SUBMIT_MODE && effectiveCheekMode() === 'bubble' && !(CHEEKART_MODE && CHEEKART_NP);\r\n}\r\nfunction applyLedTint() {   // recolor the lit decals in place (no rebuild)\r\n  if (neoActive()) return;\r\n  const hex = cheekLedHex();\r\n  for (const m of logoDecals) m.material.color.set(hex);\r\n}\r\nfunction setCheekLed(name) {\r\n  state.ledColor = cheekLedName(name);\r\n  applyLedTint(); syncLedUI();\r\n  if (LS.ctx) LS.render();\r\n}\r\nfunction syncLedUI() {\r\n  const rows = [['ls-ledrow', 'ls-ledsw', ledLitPossible()],\r\n                ['cheek-led-row', 'opt-cheek-led', ledLitPossible() && !!state.logoImage]];\r\n  for (const [rowId, swId, on] of rows) {\r\n    const row = document.getElementById(rowId), sw = document.getElementById(swId);\r\n    if (!row || !sw) continue;\r\n    row.style.display = on ? '' : 'none';\r\n    if (!on) continue;\r\n    sw.innerHTML = '';\r\n    for (const [name, hex] of CHEEK_LED_COLORS) {\r\n      const b = document.createElement('button');\r\n      b.className = 'cfg-opt' + (name === cheekLedName(state.ledColor) ? ' on' : '');\r\n      const dot = document.createElement('i'); dot.style.background = hex;\r\n      b.append(dot, name);\r\n      b.onclick = () => setCheekLed(name);\r\n      sw.appendChild(b);\r\n    }\r\n  }\r\n}\r\n\r\n// NeoPixel stencil helpers: strict black/white by luminance, and the\r\n// \"see-through\" checker the studio shows for a clear backing\r\nfunction npThreshold(ctx, S) {\r\n  const im = ctx.getImageData(0, 0, S, S), d = im.data;\r\n  for (let i = 0; i < d.length; i += 4) {\r\n    const v = (d[i] * 0.299 + d[i + 1] * 0.587 + d[i + 2] * 0.114) >= 128 ? 255 : 0;\r\n    d[i] = d[i + 1] = d[i + 2] = v; d[i + 3] = 255;\r\n  }\r\n  ctx.putImageData(im, 0, 0);\r\n}\r\nlet _npChecker = null;\r\nfunction npChecker(ctx) {\r\n  if (!_npChecker) {\r\n    const c = document.createElement('canvas'); c.width = c.height = 32;\r\n    const x = c.getContext('2d');\r\n    x.fillStyle = '#ffffff'; x.fillRect(0, 0, 32, 32);\r\n    x.fillStyle = '#c9c9d2'; x.fillRect(0, 0, 16, 16); x.fillRect(16, 16, 16, 16);\r\n    _npChecker = c;\r\n  }\r\n  return ctx.createPattern(_npChecker, 'repeat');\r\n}\r\n\r\nconst LS = {\r\n  S: 1024, open: false, img: null, baseScale: 1,\r\n  OUT: 2048,   // saved output in the cheek-art popup (print: 68.5 mm @600 DPI = 1618 px); editing stays at S\r\n  ltol: 40, lit: false,   // Light mode: \"tap a colour\" match + the Unlit/Lit preview (cheek art phase 2)\r\n  // the printed circle = the whole canvas, incl. a 0.5 mm bleed ring; the guide marks the VISIBLE edge\r\n  PLATES: { pf: { full: 68.5, visible: 67.5 }, m16: { full: 65.5, visible: 64.5 } },\r\n  t: { scale: 1, rot: 0, x: 0, y: 0 },\r\n  fx: { bright: 0, contrast: 0, poster: 0, knock: [], tol: 60 },\r\n  tool: 'draw', brush: 28, color: '#ffe000', sym: false, knockArmed: false,\r\n  glowOn: true, cx: false, cy: false,   // NeoPixel glow preview; stamp center snaps\r\n  stampText: '', stampFont: 'Impact', stampShape: null, stampSize: 220, hover: null,\r\n  history: [],\r\n  el: (id) => document.getElementById(id),\r\n\r\n  init() {\r\n    if (this.imgLayer) return;\r\n    const mk = () => { const c = document.createElement('canvas'); c.width = c.height = this.S; return c; };\r\n    this.imgLayer = mk(); this.paintLayer = mk(); this.maskLayer = mk();\r\n    {   // Light mode in EVERY Logo Studio (popup, Etsy, configurator) \u2014 Blair 2026-10-04\r\n      this.el('ls-light').style.display = '';\r\n      this.el('ls-light-tools').querySelectorAll('[data-tool]').forEach(b =>\r\n        b.onclick = () => { this.tool = b.dataset.tool; this.knockArmed = false; this.syncUI(); this.render(); });\r\n      this.el('ls-ltol').oninput = (e) => { this.ltol = +e.target.value; };\r\n      this.el('ls-lit').onclick = () => { this.lit = !this.lit; this.syncUI(); this.render(); };\r\n      this.el('ls-lclear').onclick = () => { this.snapshot(); this.maskLayer.getContext('2d').clearRect(0, 0, this.S, this.S); this.render(); };\r\n    }\r\n    this.cv = this.el('ls-canvas'); this.ctx = this.cv.getContext('2d', { willReadFrequently: true });\r\n    // tools\r\n    this.el('ls-tools').querySelectorAll('[data-tool]').forEach(b =>\r\n      b.onclick = () => { this.tool = b.dataset.tool; this.knockArmed = false; this.syncUI(); });\r\n    this.el('ls-sym').onclick = () => { this.sym = !this.sym; this.syncUI(); };\r\n    this.el('ls-ink').querySelectorAll('[data-ink]').forEach(b => b.onclick = () => {\r\n      this.color = b.dataset.ink;\r\n      if (this.tool === 'move' || this.tool === 'erase') this.tool = 'draw';\r\n      this.syncUI();\r\n    });\r\n    this.el('ls-glow').onclick = () => { this.glowOn = !this.glowOn; this.syncUI(); };\r\n    this.el('ls-cx').onclick = () => { this.cx = !this.cx; this.syncUI(); this.render(); };\r\n    this.el('ls-cy').onclick = () => { this.cy = !this.cy; this.syncUI(); this.render(); };\r\n    this.el('ls-backing').querySelectorAll('[data-backing]').forEach(b => b.onclick = () => {\r\n      state.npBacking = npBackingKey(b.dataset.backing);\r\n      this.syncUI(); this.render();\r\n      if (cheekTexture) rebuildLogoDecals();\r\n    });\r\n    this.el('ls-undo').onclick = () => this.undo();\r\n    this.el('ls-brush').oninput = (e) => { this.brush = +e.target.value; };\r\n    this.el('ls-color').oninput = (e) => { this.color = e.target.value; this.syncUI(); };\r\n    // swatches\r\n    const sw = this.el('ls-swatches');\r\n    for (const c of ['#ffffff', '#141418', '#ffe000', '#ff1500', '#1a1aff', '#2EECC5', '#9B5FE3', '#ff54c8']) {\r\n      const b = document.createElement('b'); b.style.background = c;\r\n      b.onclick = () => { this.color = c; this.el('ls-color').value = c; this.syncUI(); };\r\n      sw.appendChild(b);\r\n    }\r\n    // image + fx\r\n    this.el('ls-file').onchange = (e) => { const f = e.target.files[0]; if (f) this.loadImage(URL.createObjectURL(f)); };\r\n    this.el('ls-knock').onclick = () => { this.knockArmed = true; this.tool = 'pick'; this.syncUI(); };\r\n    const fx = (id, k, scale = 1) => this.el(id).oninput = (e) => { this.fx[k] = +e.target.value * scale; this.bake(); };\r\n    this.el('ls-scale').oninput = (e) => { this.t.scale = +e.target.value / 100; this.bake(); };\r\n    this.el('ls-rot').oninput = (e) => { this.t.rot = +e.target.value * Math.PI / 180; this.bake(); };\r\n    fx('ls-bright', 'bright'); fx('ls-contrast', 'contrast'); fx('ls-tol', 'tol');\r\n    this.el('ls-poster').onchange = (e) => { this.fx.poster = +e.target.value; this.bake(); };\r\n    // text & shape stamps: type or pick a shape, then click the canvas to place\r\n    this.el('ls-text').oninput = (e) => {\r\n      this.stampText = e.target.value;\r\n      if (this.stampText) { this.stampShape = null; this.tool = 'stamp'; }\r\n      else if (!this.stampShape) this.tool = 'draw';\r\n      this.syncUI();\r\n    };\r\n    this.el('ls-font').onchange = (e) => { this.stampFont = e.target.value; this.render(); };\r\n    this.el('ls-shapes').querySelectorAll('[data-shape]').forEach(b => b.onclick = () => {\r\n      this.stampShape = this.stampShape === b.dataset.shape ? null : b.dataset.shape;   // re-click unarms\r\n      this.tool = this.stampShape ? 'stamp' : (this.stampText ? 'stamp' : 'draw');\r\n      this.syncUI(); this.render();\r\n    });\r\n    this.el('ls-stampsize').oninput = (e) => { this.stampSize = +e.target.value; this.render(); };\r\n    // pointer input\r\n    let drag = null;\r\n    const pos = (e) => { const r = this.cv.getBoundingClientRect();\r\n      return { x: (e.clientX - r.left) / r.width * this.S, y: (e.clientY - r.top) / r.height * this.S }; };\r\n    this.cv.onpointerdown = (e) => {\r\n      this.cv.setPointerCapture(e.pointerId);\r\n      const p = pos(e);\r\n      if (this.tool === 'pick') { this.pickAt(p); return; }\r\n      if (this.tool === 'ltap') { this.lightTapAt(p); return; }\r\n      if (this.tool === 'fill') { this.fillAt(p); return; }\r\n      if (this.tool === 'stamp') { this.snapshot(); this.stampAt(p); return; }\r\n      if (this.tool === 'move') { drag = p; return; }\r\n      this.snapshot();\r\n      drag = p; this.stroke(p, p);\r\n    };\r\n    this.cv.onpointermove = (e) => {\r\n      if (this.tool === 'stamp') { this.hover = pos(e); this.render(); return; }   // ghost preview\r\n      if (!drag) return;\r\n      const p = pos(e);\r\n      if (this.tool === 'move') { this.t.x += p.x - drag.x; this.t.y += p.y - drag.y; drag = p; this.bake(); return; }\r\n      this.stroke(drag, p); drag = p;\r\n    };\r\n    this.cv.onpointerup = () => { drag = null; };\r\n    this.cv.onpointerleave = () => { if (this.hover) { this.hover = null; this.render(); } };\r\n    this.el('ls-close').onclick = () => this.el('ls-modal').classList.remove('show');\r\n    this.el('ls-apply').onclick = () => this.apply();\r\n    this.syncUI(); this.render();\r\n  },\r\n\r\n  show() {\r\n    this.init();\r\n    // NeoPixel cheeks: the design is white (the ring supplies the colour), so\r\n    // lock the studio to white and hide the colour controls.\r\n    this.whiteOnly = neoActive();\r\n    syncLedUI();\r\n    if (this.whiteOnly) {\r\n      this.color = '#ffffff'; this.el('ls-color').value = '#ffffff';\r\n      if (this.tool === 'pick') this.tool = 'draw';\r\n    }\r\n    this.el('ls-modal').classList.add('show');\r\n    this.syncUI(); this.render();\r\n  },\r\n\r\n  loadImage(src, name) {\r\n    const img = new Image();\r\n    img.onload = () => {\r\n      this.img = img;\r\n      this.baseScale = this.S / Math.max(img.width, img.height);   // slider 100% = fit\r\n      if (this._restore) {\r\n        // edit-mode reopen: keep the saved transform + cleanup instead of resetting.\r\n        // (baseScale recomputes from the persisted \u2014 possibly downscaled \u2014 source,\r\n        // but rendered size is dimension-relative, so the design lands identically)\r\n        this.t = this._restore.t; this.fx = this._restore.fx;\r\n        this._restore = null;\r\n      } else {\r\n        this.t = { scale: 1, rot: 0, x: 0, y: 0 };\r\n        this.fx.knock = [];\r\n        this.el('ls-scale').value = 100; this.el('ls-rot').value = 0;\r\n      }\r\n      this.bake();\r\n    };\r\n    img.src = src;\r\n  },\r\n\r\n  // \u2500\u2500 edit-mode persistence (?cheekart=1&edit=1): full editable state \u2500\u2500\r\n  persist() {\r\n    try {\r\n      const s = { t: this.t, fx: this.fx, color: this.color, brush: this.brush,\r\n                  sym: this.sym, logoMirror: state.logoMirror, backing: state.npBacking, savedAt: Date.now() };\r\n      s.paint = this.paintLayer.toDataURL('image/png');\r\n      if (this.hasMask()) s.mask = this.maskLayer.toDataURL('image/png');\r\n      if (this.img) {\r\n        // cap the persisted source (~localStorage quota); rendered size is\r\n        // dimension-relative so a downscaled source restores pixel-identically\r\n        const cap = 1200, k = Math.min(1, cap / Math.max(this.img.width, this.img.height));\r\n        const c = document.createElement('canvas');\r\n        c.width = Math.max(1, Math.round(this.img.width * k));\r\n        c.height = Math.max(1, Math.round(this.img.height * k));\r\n        c.getContext('2d').drawImage(this.img, 0, 0, c.width, c.height);\r\n        let d = c.toDataURL('image/png');\r\n        if (d.length > 2_500_000) d = c.toDataURL('image/jpeg', 0.85);   // huge photos: drop alpha\r\n        s.img = d;\r\n      }\r\n      localStorage.setItem('pf-cheek-studio-state', JSON.stringify(s));\r\n    } catch (e) { /* quota/privacy mode \u2014 best effort */ }\r\n  },\r\n  restore() {\r\n    try {\r\n      const raw = localStorage.getItem('pf-cheek-studio-state');\r\n      if (!raw) return false;\r\n      const s = JSON.parse(raw);\r\n      this.color = s.color || this.color; this.brush = s.brush || this.brush;\r\n      this.sym = !!s.sym;\r\n      state.logoMirror = s.logoMirror !== false;\r\n      if (s.backing) state.npBacking = npBackingKey(s.backing);\r\n      this.el('ls-color').value = this.color;\r\n      this.el('ls-brush').value = this.brush;\r\n      if (s.paint) {\r\n        const im = new Image();\r\n        im.onload = () => { const c = this.paintLayer.getContext('2d');\r\n          c.clearRect(0, 0, this.S, this.S); c.drawImage(im, 0, 0); this.render(); };\r\n        im.src = s.paint;\r\n      }\r\n      if (s.mask && this.maskLayer) {\r\n        const mi = new Image();\r\n        mi.onload = () => { const mc = this.maskLayer.getContext('2d'); mc.clearRect(0, 0, this.S, this.S); mc.drawImage(mi, 0, 0, this.S, this.S); this.render(); };\r\n        mi.src = s.mask;\r\n      }\r\n      if (s.img) { this._restore = { t: s.t, fx: s.fx }; this.loadImage(s.img); }\r\n      else if (s.t) { this.t = s.t; this.fx = s.fx || this.fx; }\r\n      // sync the sliders to the restored values\r\n      this.el('ls-scale').value = Math.round((s.t?.scale ?? 1) * 100);\r\n      this.el('ls-rot').value = Math.round((s.t?.rot ?? 0) * 180 / Math.PI);\r\n      this.el('ls-bright').value = s.fx?.bright ?? 0;\r\n      this.el('ls-contrast').value = s.fx?.contrast ?? 0;\r\n      this.el('ls-tol').value = s.fx?.tol ?? 60;\r\n      this.el('ls-poster').value = String(s.fx?.poster ?? 0);\r\n      this.syncUI(); this.render();\r\n      return true;\r\n    } catch (e) { return false; }\r\n  },\r\n\r\n  // rebuild the image layer: transform, then the UV-print cleanup pixel ops\r\n  bake() { this.bakeTo(this.imgLayer, 1); this.render(); },\r\n  // the image layer at K x the working size, straight from the source pixels (K=2: the saved print output)\r\n  bakeTo(cv, K) {\r\n    const S = this.S * K;\r\n    if (cv.width !== S) { cv.width = cv.height = S; }\r\n    const c = cv.getContext('2d', { willReadFrequently: true });\r\n    c.clearRect(0, 0, S, S);\r\n    if (this.img) {\r\n      c.save();\r\n      c.translate(S / 2 + this.t.x * K, S / 2 + this.t.y * K);\r\n      c.rotate(this.t.rot);\r\n      const s = this.baseScale * this.t.scale * K;\r\n      c.scale(s, s);\r\n      c.drawImage(this.img, -this.img.width / 2, -this.img.height / 2);\r\n      c.restore();\r\n      const im = c.getImageData(0, 0, S, S), d = im.data;\r\n      const br = this.fx.bright * 1.27;\r\n      const cf = (259 * (this.fx.contrast * 1.27 + 255)) / (255 * (259 - this.fx.contrast * 1.27));\r\n      const P = this.fx.poster, tol2 = this.fx.tol * this.fx.tol;\r\n      for (let i = 0; i < d.length; i += 4) {\r\n        if (!d[i + 3]) continue;\r\n        for (let ch = 0; ch < 3; ch++) {\r\n          let v = cf * (d[i + ch] - 128) + 128 + br;\r\n          if (P) v = Math.round(v / 255 * (P - 1)) / (P - 1) * 255;\r\n          d[i + ch] = Math.max(0, Math.min(255, v));\r\n        }\r\n        for (const k of this.fx.knock) {\r\n          const dr = d[i] - k[0], dg = d[i + 1] - k[1], db = d[i + 2] - k[2];\r\n          if (dr * dr + dg * dg + db * db < tol2) { d[i + 3] = 0; break; }\r\n        }\r\n      }\r\n      c.putImageData(im, 0, 0);\r\n    }\r\n  },\r\n  // the SAVED print output at K x (black outside the circle, as compose(true)), for the cheek-art popup\r\n  composeOut(K) {\r\n    const S = this.S * K, out = document.createElement('canvas'); out.width = out.height = S;\r\n    const c = out.getContext('2d');\r\n    c.fillStyle = '#000'; c.fillRect(0, 0, S, S);\r\n    c.beginPath(); c.arc(S / 2, S / 2, S / 2, 0, Math.PI * 2); c.clip();\r\n    const il = document.createElement('canvas'); this.bakeTo(il, K);\r\n    c.drawImage(il, 0, 0);\r\n    c.imageSmoothingEnabled = true; c.imageSmoothingQuality = 'high';\r\n    c.drawImage(this.paintLayer, 0, 0, S, S);\r\n    if (this.whiteOnly) npThreshold(c, S);\r\n    return out;\r\n  },\r\n\r\n  stroke(a, b) {\r\n    const light = this.tool === 'lblock' || this.tool === 'lglow';\r\n    const c = (light ? this.maskLayer : this.paintLayer).getContext('2d');\r\n    const seg = (x1, y1, x2, y2) => {\r\n      c.globalCompositeOperation = (this.tool === 'erase' || this.tool === 'lglow') ? 'destination-out' : 'source-over';\r\n      c.strokeStyle = c.fillStyle = light ? '#000' : this.color;\r\n      c.lineWidth = this.brush; c.lineCap = 'round';\r\n      c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke();\r\n    };\r\n    seg(a.x, a.y, b.x, b.y);\r\n    if (this.sym) seg(this.S - a.x, a.y, this.S - b.x, b.y);\r\n    this.render();\r\n  },\r\n\r\n  // \u2500\u2500 text & shape stamps \u2500\u2500\r\n  // NeoPixel glow preview: an overlay canvas, mix-blend multiply over the\r\n  // stencil \u2014 white (light) areas show the glow, black stays black. White\r\n  // backing = the diffuse swirling rainbow; clear = a nearly-transparent gray\r\n  // sheet with the 12 ring LEDs as crisp points (ring radius as on the 3D cheek).\r\n  glowTick() {\r\n    const ov = this.el('ls-glow-cv');\r\n    const on = this.whiteOnly && this.glowOn && this.el('ls-modal').classList.contains('show');\r\n    ov.style.display = on ? 'block' : 'none';\r\n    if (!on) { this._glowRAF = null; return; }\r\n    const cv = this.cv;\r\n    ov.style.left = (cv.offsetLeft + cv.clientLeft) + 'px';\r\n    ov.style.top = (cv.offsetTop + cv.clientTop) + 'px';\r\n    ov.style.width = cv.clientWidth + 'px'; ov.style.height = cv.clientHeight + 'px';\r\n    const x = ov.getContext('2d'), S = ov.width, c = S / 2, t = performance.now() / 1000;\r\n    x.fillStyle = '#ffffff'; x.fillRect(0, 0, S, S);   // white = leave the canvas as is\r\n    x.save();\r\n    x.beginPath(); x.arc(c, c, c - 4, 0, Math.PI * 2); x.clip();\r\n    if (state.npBacking === 'clear') {\r\n      x.fillStyle = '#3a3a42'; x.fillRect(0, 0, S, S);   // the dim inside, seen through the clear sheet\r\n      const ringR = c * (0.30 / (2 * 0.42)), dot = S * 0.022;\r\n      for (let i = 0; i < 12; i++) {\r\n        const a = i / 12 * Math.PI * 2, hue = (i * 30 + t * 40) % 360;\r\n        const px = c + Math.cos(a) * ringR, py = c + Math.sin(a) * ringR;\r\n        const g = x.createRadialGradient(px, py, 0, px, py, dot * 1.6);\r\n        g.addColorStop(0, '#ffffff'); g.addColorStop(0.35, `hsl(${hue},100%,62%)`);\r\n        g.addColorStop(0.7, `hsla(${hue},100%,55%,0.6)`); g.addColorStop(1, 'rgba(58,58,66,0)');\r\n        x.fillStyle = g; x.fillRect(px - dot * 1.6, py - dot * 1.6, dot * 3.2, dot * 3.2);\r\n      }\r\n    } else {\r\n      x.translate(c, c); x.rotate(t * 0.7);\r\n      x.drawImage(rainbowTexture().image, -c, -c, S, S);\r\n    }\r\n    x.restore();\r\n    this._glowRAF = requestAnimationFrame(() => this.glowTick());\r\n  },\r\n  snapCenter(p) {   // Center \u2194 / \u2195 toggles lock the stamp to the middle\r\n    return { x: this.cx ? this.S / 2 : p.x, y: this.cy ? this.S / 2 : p.y };\r\n  },\r\n  stampAt(p) {\r\n    const c = this.paintLayer.getContext('2d');\r\n    p = this.snapCenter(p);\r\n    this.drawStamp(c, p, 1);\r\n    if (this.sym && !this.cx) this.drawStamp(c, { x: this.S - p.x, y: p.y }, 1);   // position mirrors; glyphs stay readable\r\n    this.render();\r\n  },\r\n  drawStamp(c, p, alpha) {\r\n    c.save();\r\n    c.globalCompositeOperation = 'source-over';\r\n    c.globalAlpha = alpha;\r\n    c.fillStyle = this.color;\r\n    if (this.stampShape) this.shapePath(c, this.stampShape, p.x, p.y, this.stampSize / 2);\r\n    else if (this.stampText) {\r\n      c.font = `bold ${this.stampSize * 0.55}px \"${this.stampFont}\", sans-serif`;\r\n      c.textAlign = 'center'; c.textBaseline = 'middle';\r\n      c.fillText(this.stampText, p.x, p.y);\r\n    }\r\n    c.restore();\r\n  },\r\n  shapePath(c, kind, x, y, r) {\r\n    c.beginPath();\r\n    if (kind === 'pawbig') { kind = 'paw'; r *= 2.2; }   // one large paw print at the same Size\r\n    switch (kind) {\r\n      case 'halo':   // a ring: filled even-odd so the middle stays open\r\n        c.arc(x, y, r * 0.95, 0, Math.PI * 2);\r\n        c.moveTo(x + r * 0.68, y); c.arc(x, y, r * 0.68, 0, Math.PI * 2);\r\n        break;\r\n      case 'heart':\r\n        c.moveTo(x, y + r * 0.85);\r\n        c.bezierCurveTo(x - r * 1.3, y + r * 0.05, x - r * 0.85, y - r * 0.95, x, y - r * 0.35);\r\n        c.bezierCurveTo(x + r * 0.85, y - r * 0.95, x + r * 1.3, y + r * 0.05, x, y + r * 0.85);\r\n        break;\r\n      case 'star':\r\n        for (let i = 0; i < 10; i++) {\r\n          const rr = i % 2 ? r * 0.42 : r, a = -Math.PI / 2 + i * Math.PI / 5;\r\n          c[i ? 'lineTo' : 'moveTo'](x + rr * Math.cos(a), y + rr * Math.sin(a));\r\n        }\r\n        break;\r\n      case 'diamond':\r\n        c.moveTo(x, y - r); c.lineTo(x + r * 0.7, y); c.lineTo(x, y + r); c.lineTo(x - r * 0.7, y);\r\n        break;\r\n      case 'circle':\r\n        c.arc(x, y, r * 0.9, 0, Math.PI * 2);\r\n        break;\r\n      case 'bolt':\r\n        c.moveTo(x + r * 0.25, y - r); c.lineTo(x - r * 0.55, y + r * 0.15); c.lineTo(x - r * 0.05, y + r * 0.15);\r\n        c.lineTo(x - r * 0.25, y + r); c.lineTo(x + r * 0.55, y - r * 0.1); c.lineTo(x + r * 0.05, y - r * 0.1);\r\n        break;\r\n      case 'paw': {\r\n        c.ellipse(x, y + r * 0.35, r * 0.52, r * 0.42, 0, 0, Math.PI * 2);\r\n        const toes = [[-0.62, -0.18, 0.22], [-0.22, -0.52, 0.24], [0.22, -0.52, 0.24], [0.62, -0.18, 0.22]];\r\n        for (const [tx, ty, tr] of toes) { c.moveTo(x + tx * r + tr * r, y + ty * r); c.arc(x + tx * r, y + ty * r, tr * r, 0, Math.PI * 2); }\r\n        break; }\r\n    }\r\n    c.closePath(); c.fill(kind === 'halo' ? 'evenodd' : 'nonzero');\r\n  },\r\n\r\n  // \u2500\u2500 Light mode (cheek art phase 2): tap a colour \u2192 every pixel of that COMPOSITED colour (within Match) flips\r\n  // between glowing and blocking. Reads RGB of what you see, never the art's alpha (compose() is an opaque disc).\r\n  lightTapAt(p) {\r\n    const S = this.S, x0 = p.x | 0, y0 = p.y | 0;\r\n    if (x0 < 0 || y0 < 0 || x0 >= S || y0 >= S) return;\r\n    this.snapshot();\r\n    const src = this.compose(false).getContext('2d').getImageData(0, 0, S, S).data;\r\n    const mctx = this.maskLayer.getContext('2d', { willReadFrequently: true });\r\n    const m = mctx.getImageData(0, 0, S, S), md = m.data;\r\n    const i0 = (y0 * S + x0) * 4, tr = src[i0], tg = src[i0 + 1], tb = src[i0 + 2];\r\n    const block = md[i0 + 3] < 128, tol2 = this.ltol * this.ltol;\r\n    for (let i = 0; i < md.length; i += 4) {\r\n      const dr = src[i] - tr, dg = src[i + 1] - tg, db = src[i + 2] - tb;\r\n      if (dr * dr + dg * dg + db * db <= tol2) { md[i] = md[i + 1] = md[i + 2] = 0; md[i + 3] = block ? 255 : 0; }\r\n    }\r\n    mctx.putImageData(m, 0, 0);\r\n    this.render();\r\n  },\r\n  hasMask() {\r\n    if (!this.maskLayer) return false;\r\n    const d = this.maskLayer.getContext('2d', { willReadFrequently: true }).getImageData(0, 0, this.S, this.S).data;\r\n    for (let i = 3; i < d.length; i += 4) if (d[i] > 8) return true;\r\n    return false;\r\n  },\r\n  // the saved mask at K x: black where light is blocked, transparent elsewhere, clipped to the print circle\r\n  composeMaskOut(K) {\r\n    const S = this.S * K, out = document.createElement('canvas'); out.width = out.height = S;\r\n    const c = out.getContext('2d');\r\n    c.beginPath(); c.arc(S / 2, S / 2, S / 2, 0, Math.PI * 2); c.clip();\r\n    c.imageSmoothingEnabled = true; c.imageSmoothingQuality = 'high';\r\n    if (this.whiteOnly) {   // NeoPixel stencil: blocked = its black areas\r\n      const a = this.compose(false).getContext('2d').getImageData(0, 0, this.S, this.S).data;\r\n      const m = document.createElement('canvas'); m.width = m.height = this.S;\r\n      const mc = m.getContext('2d'), md = mc.createImageData(this.S, this.S);\r\n      for (let i = 0; i < a.length; i += 4) if (a[i] < 128) md.data[i + 3] = 255;\r\n      mc.putImageData(md, 0, 0);\r\n      c.drawImage(m, 0, 0, S, S);\r\n    } else c.drawImage(this.maskLayer, 0, 0, S, S);\r\n    return out;\r\n  },\r\n\r\n  pickAt(p) {\r\n    const d = this.compose(false).getContext('2d').getImageData(p.x | 0, p.y | 0, 1, 1).data;\r\n    if (this.knockArmed) {\r\n      this.snapshot();\r\n      this.fx.knock.push([d[0], d[1], d[2]]);\r\n      this.knockArmed = false; this.tool = 'move'; this.bake();\r\n    } else {\r\n      this.color = '#' + [d[0], d[1], d[2]].map(v => v.toString(16).padStart(2, '0')).join('');\r\n      this.el('ls-color').value = this.color; this.tool = 'draw';\r\n    }\r\n    this.syncUI();\r\n  },\r\n\r\n  // \u2500\u2500 bucket fill: flood the region under the click with the current colour,\r\n  // bounded by colour similarity in the composited image (what you see); the\r\n  // fill is painted onto the paint layer so it undoes/erases like a brush \u2500\u2500\r\n  fillAt(p) {\r\n    const S = this.S, x0 = p.x | 0, y0 = p.y | 0;\r\n    if (x0 < 0 || y0 < 0 || x0 >= S || y0 >= S) return;\r\n    this.snapshot();\r\n    const src = this.compose(false).getContext('2d').getImageData(0, 0, S, S).data;\r\n    const pctx = this.paintLayer.getContext('2d');\r\n    const out = pctx.getImageData(0, 0, S, S), od = out.data;\r\n    const n = parseInt(this.color.slice(1), 16), fr = (n >> 16) & 255, fg = (n >> 8) & 255, fb = n & 255;\r\n    const TOL = 40;                       // per-channel colour tolerance\r\n    const filled = new Uint8Array(S * S);\r\n    const flood = (sx, sy) => {           // scanline flood from a seed pixel\r\n      const s0 = (sy * S + sx) * 4, tr = src[s0], tg = src[s0 + 1], tb = src[s0 + 2];\r\n      const match = (i) => { const j = i * 4;\r\n        return Math.abs(src[j] - tr) <= TOL && Math.abs(src[j + 1] - tg) <= TOL && Math.abs(src[j + 2] - tb) <= TOL; };\r\n      const stack = [sx, sy];\r\n      while (stack.length) {\r\n        let y = stack.pop(), x = stack.pop(), i = y * S + x;\r\n        while (x >= 0 && !filled[i] && match(i)) { x--; i--; }\r\n        x++; i++;\r\n        let up = false, dn = false;\r\n        while (x < S && !filled[i] && match(i)) {\r\n          filled[i] = 1;\r\n          const j = i * 4; od[j] = fr; od[j + 1] = fg; od[j + 2] = fb; od[j + 3] = 255;\r\n          if (y > 0) { const m = !filled[i - S] && match(i - S);\r\n            if (m && !up) { stack.push(x, y - 1); up = true; } else if (!m) up = false; }\r\n          if (y < S - 1) { const m = !filled[i + S] && match(i + S);\r\n            if (m && !dn) { stack.push(x, y + 1); dn = true; } else if (!m) dn = false; }\r\n          x++; i++;\r\n        }\r\n      }\r\n    };\r\n    flood(x0, y0);\r\n    if (this.sym) flood(S - 1 - x0, y0);   // mirror-fill to honour the symmetry toggle\r\n    pctx.putImageData(out, 0, 0);\r\n    this.render();\r\n  },\r\n\r\n  snapshot() {\r\n    const cp = document.createElement('canvas'); cp.width = cp.height = this.S;\r\n    cp.getContext('2d').drawImage(this.paintLayer, 0, 0);\r\n    let mk2 = null;\r\n    if (this.maskLayer) { mk2 = document.createElement('canvas'); mk2.width = mk2.height = this.S; mk2.getContext('2d').drawImage(this.maskLayer, 0, 0); }\r\n    this.history.push({ paint: cp, knock: this.fx.knock.slice(), mask: mk2 });\r\n    if (this.history.length > 25) this.history.shift();\r\n  },\r\n  undo() {\r\n    const h = this.history.pop(); if (!h) return;\r\n    const c = this.paintLayer.getContext('2d');\r\n    c.clearRect(0, 0, this.S, this.S); c.drawImage(h.paint, 0, 0);\r\n    if (h.mask && this.maskLayer) { const mc = this.maskLayer.getContext('2d'); mc.clearRect(0, 0, this.S, this.S); mc.drawImage(h.mask, 0, 0); }\r\n    this.fx.knock = h.knock; this.bake();\r\n  },\r\n\r\n  // black bg + image + paint; masked = clip to the print circle (final output)\r\n  compose(masked) {\r\n    const out = document.createElement('canvas'); out.width = out.height = this.S;\r\n    const c = out.getContext('2d', { willReadFrequently: true });\r\n    c.fillStyle = '#000'; c.fillRect(0, 0, this.S, this.S);\r\n    if (masked) { c.beginPath(); c.arc(this.S / 2, this.S / 2, this.S / 2, 0, Math.PI * 2); c.clip(); }\r\n    c.drawImage(this.imgLayer, 0, 0);\r\n    c.drawImage(this.paintLayer, 0, 0);\r\n    if (this.whiteOnly) npThreshold(c, this.S);\r\n    return out;\r\n  },\r\n\r\n  render() {\r\n    if (!this.ctx) return;\r\n    this.ctx.drawImage(this.compose(false), 0, 0);\r\n    if (this.whiteOnly && state.npBacking === 'clear') {\r\n      this.ctx.save(); this.ctx.globalCompositeOperation = 'multiply';\r\n      this.ctx.fillStyle = npChecker(this.ctx); this.ctx.fillRect(0, 0, this.S, this.S); this.ctx.restore();\r\n    }\r\n    if (this.maskLayer && !this.whiteOnly) {\r\n      const S0 = this.S, lightTool = this.tool === 'ltap' || this.tool === 'lblock' || this.tool === 'lglow';\r\n      if (this.lit) {   // glowing areas brightened, blocked areas dark\r\n        const g = document.createElement('canvas'); g.width = g.height = S0;\r\n        const gc = g.getContext('2d'); gc.drawImage(this.compose(false), 0, 0);\r\n        if (ledLitPossible()) {\r\n          gc.globalCompositeOperation = 'multiply'; gc.fillStyle = cheekLedHex(); gc.fillRect(0, 0, S0, S0);\r\n          this.ctx.save(); this.ctx.globalCompositeOperation = 'multiply'; this.ctx.fillStyle = cheekLedHex();\r\n          this.ctx.fillRect(0, 0, S0, S0); this.ctx.restore();\r\n        }\r\n        gc.globalCompositeOperation = 'destination-out'; gc.drawImage(this.maskLayer, 0, 0);\r\n        this.ctx.save(); this.ctx.globalCompositeOperation = 'lighter'; this.ctx.globalAlpha = 0.6; this.ctx.drawImage(g, 0, 0); this.ctx.restore();\r\n        this.ctx.save(); this.ctx.globalAlpha = 0.82; this.ctx.drawImage(this.maskLayer, 0, 0); this.ctx.restore();\r\n      } else if (lightTool) {   // show what's blocked\r\n        const t = document.createElement('canvas'); t.width = t.height = S0;\r\n        const tc = t.getContext('2d'); tc.drawImage(this.maskLayer, 0, 0);\r\n        tc.globalCompositeOperation = 'source-in'; tc.fillStyle = 'rgba(255,40,80,0.55)'; tc.fillRect(0, 0, S0, S0);\r\n        this.ctx.drawImage(t, 0, 0);\r\n      }\r\n    }\r\n    // dim outside the print circle + ring, so the print zone is obvious\r\n    const c = this.ctx, S = this.S;\r\n    c.save();\r\n    c.beginPath(); c.rect(0, 0, S, S);\r\n    c.arc(S / 2, S / 2, S / 2 - 6, 0, Math.PI * 2, true);\r\n    c.fillStyle = 'rgba(12,11,20,0.72)'; c.fill('evenodd');\r\n    c.beginPath(); c.arc(S / 2, S / 2, S / 2 - 6, 0, Math.PI * 2);\r\n    c.strokeStyle = '#2EECC5'; c.lineWidth = 4; c.stroke();\r\n    const pl = this.PLATES[CHEEKART_PLATE];\r\n    if ((CHEEKART_MODE || SHOP_ART_MODE) && pl) {   // visible edge; the ring outside it is the 0.5 mm print bleed\r\n      const rv = (S / 2) * pl.visible / pl.full;\r\n      c.beginPath(); c.arc(S / 2, S / 2, rv, 0, Math.PI * 2);\r\n      c.setLineDash([14, 10]); c.strokeStyle = 'rgba(255,255,255,0.85)'; c.lineWidth = 3; c.stroke(); c.setLineDash([]);\r\n      c.font = '600 22px sans-serif'; c.textAlign = 'center'; c.fillStyle = 'rgba(255,255,255,0.85)';\r\n      c.fillText((CHEEKART_PLATE === 'pf' ? 'ProtoFrenz' : 'Legacy M16') + ' \u00B7 visible edge \u2014 outside it is bleed', S / 2, S - 18);\r\n    }\r\n    c.restore();\r\n    // ghost of the pending text/shape stamp under the cursor\r\n    if (this.tool === 'stamp' && this.hover && (this.stampShape || this.stampText)) {\r\n      const h = this.snapCenter(this.hover);\r\n      this.drawStamp(c, h, 0.55);\r\n      if (this.sym && !this.cx) this.drawStamp(c, { x: S - h.x, y: h.y }, 0.3);\r\n    }\r\n  },\r\n\r\n  syncUI() {\r\n    // NeoPixel white-lock: hide colour picking (swatches, colour input, eyedropper,\r\n    // remove-a-colour) \u2014 the design is white and the ring provides the colour.\r\n    const wo = !!this.whiteOnly;\r\n    if (wo && this.color !== '#000000') { this.color = '#ffffff'; this.el('ls-color').value = '#ffffff'; }\r\n    this.el('ls-swatches').style.display = wo ? 'none' : '';\r\n    this.el('ls-color').style.display = wo ? 'none' : '';\r\n    this.el('ls-knock').style.display = wo ? 'none' : '';\r\n    const pick = this.el('ls-tools').querySelector('[data-tool=\"pick\"]');\r\n    if (pick) pick.style.display = wo ? 'none' : '';\r\n    // NeoPixel: the stencil IS the light mask \u2014 paints + backing replace colour and the Light tools\r\n    this.el('ls-np-top').style.display = wo ? '' : 'none';\r\n    this.el('ls-ink').style.display = wo ? '' : 'none';\r\n    this.el('ls-glow').textContent = this.glowOn ? '\uD83D\uDCA1 Glow: on' : '\uD83D\uDCA1 Glow: off';\r\n    this.el('ls-glow').classList.toggle('on', this.glowOn);\r\n    this.el('ls-cx').classList.toggle('on', this.cx);\r\n    this.el('ls-cy').classList.toggle('on', this.cy);\r\n    if (wo && this.glowOn && !this._glowRAF) this.glowTick();\r\n    this.el('ls-light').style.display = wo ? 'none' : '';\r\n    this.el('ls-poster').closest('.ls-row').style.display = wo ? 'none' : '';\r\n    if (wo) {\r\n      this.el('ls-ink').querySelectorAll('[data-ink]').forEach(b => b.classList.toggle('on', b.dataset.ink === this.color));\r\n      this.el('ls-backing').querySelectorAll('[data-backing]').forEach(b => b.classList.toggle('on', b.dataset.backing === state.npBacking));\r\n      this.el('ls-np-note').textContent = state.npBacking === 'clear'\r\n        ? 'Clear backing: a nearly-transparent light-gray sheet \u2014 the 12 LEDs show through your light areas as crisp points of color.'\r\n        : 'White backing: a diffuser spreads the LEDs into a soft, even glow through your light areas.';\r\n    }\r\n    this.el('ls-tools').querySelectorAll('[data-tool]').forEach(b =>\r\n      b.classList.toggle('on', b.dataset.tool === this.tool && !this.knockArmed));\r\n    if (this.el('ls-light-tools')) {\r\n      this.el('ls-light-tools').querySelectorAll('[data-tool]').forEach(b => b.classList.toggle('on', b.dataset.tool === this.tool));\r\n      this.el('ls-lit').textContent = this.lit ? 'Lit \u2713' : 'Unlit';\r\n      this.el('ls-lit').classList.toggle('on', this.lit);\r\n    }\r\n    this.el('ls-shapes').querySelectorAll('[data-shape]').forEach(b =>\r\n      b.classList.toggle('on', this.tool === 'stamp' && b.dataset.shape === this.stampShape));\r\n    this.el('ls-sym').classList.toggle('on', this.sym);\r\n    this.el('ls-hint').textContent = this.knockArmed\r\n      ? 'Now click the color you want to remove (adjust Match if it takes too little or too much).'\r\n      : this.tool === 'stamp'\r\n      ? 'Click the canvas to place it \u2014 Size sets how big, the color picker sets its color. Undo removes a placement.'\r\n      : wo ? 'It all starts black (blocked). Paint \u2600 Light where the LEDs should shine through; \u2B1B Block covers it again. '\r\n        + 'Images turn black & white \u2014 Bright moves the cut-off.'\r\n      : 'Load your ref art (or just draw). Everything inside the circle prints on the cheek; the rest fills black automatically.';\r\n  },\r\n\r\n  apply() {\r\n    if (CHEEKART_MODE) this.persist();   // \"Edit design\" on the PDP restores this\r\n    const out = this.compose(true);\r\n    cheekTexture = new THREE.CanvasTexture(out);\r\n    cheekTexture.colorSpace = THREE.SRGBColorSpace;\r\n    const tc = document.createElement('canvas'); tc.width = tc.height = 96;\r\n    tc.getContext('2d').drawImage(out, 0, 0, 96, 96);\r\n    cheekThumb = tc.toDataURL('image/jpeg', 0.7);\r\n    logoMaskFile = null; this.maskPending = null;\r\n    // review: the send path AWAITS this, so a fast \"Use on cheeks\" \u2192 Send can't post the art without its mask\r\n    if (this.whiteOnly || this.hasMask())\r\n      this.maskPending = new Promise((res) => this.composeMaskOut(CHEEKART_MODE ? this.OUT / this.S : 1).toBlob((mb) => {   // = the art's size in this mode\r\n        logoMaskFile = mb ? new File([mb], 'logo-studio-mask.png', { type: 'image/png' }) : null; res();\r\n      }, 'image/png'));\r\n    (CHEEKART_MODE ? this.composeOut(this.OUT / this.S) : out).toBlob((blob) => {\r\n      logoFile = new File([blob], 'logo-studio.png', { type: 'image/png' });\r\n      cheekImageName = 'logo-studio.png'; state.logoImage = 'logo-studio.png';\r\n      document.getElementById('cheek-img-name').textContent = 'Logo Studio design \u2014 shows under the bubble';\r\n      rebuildLogoDecals(); rebuildCheekRings(); renderUI();\r\n      this.el('ls-modal').classList.remove('show');\r\n    }, 'image/png');\r\n  },\r\n};\r\nwindow.__cfgDev.logoStudio = LS;\r\ndocument.getElementById('ls-open').onclick = () => LS.show();   // lazy-inits the studio\r\n\r\n// \u2550\u2550 Fit check \u2014 schematic reference head scaled by circumference \u2550\u2550\r\n// Per-style limits go here as the data arrives (inches). Two tiers:\r\n// over `snug` = informative (thin padding, place gently), over `max` = won't\r\n// fit. A bare number means max, with snug an inch under it.\r\n// Max head circumference per style (inches, from Blair's measurements). A bare\r\n// number = max, with snug an inch under. Modern (Iris/Nyx/Cipher/Vex) share the\r\n// ProtoFrenz base = 26.8\"; MK3/MK5 = 25.2\".\r\nconst FIT_LIMITS_IN = {\r\n  default: 26.8,   // ProtoFrenz base (all modern styles)\r\n  mk3: 25.2,\r\n  mk5: 25.2,\r\n  // MK2 tops out at 24\"; its entry opening runs small, so it's a tight fit near\r\n  // the top of the range \u2014 wider snug band + a note (an object overrides snug).\r\n  mk2: { snug: 22, max: 24, note: 'MK2 has a smaller entry opening \u2014 even in range it goes on tight, so measure carefully.' },\r\n};\r\n\r\nfunction fitLimits() {\r\n  let v = FIT_LIMITS_IN[state.style] ?? FIT_LIMITS_IN.default;\r\n  if (typeof v === 'number') v = { snug: v - 1, max: v };\r\n  return v;\r\n}\r\n\r\n// Fit reference = the real mannequin head GLB (chin at the origin, face toward\r\n// -Y, Z up). baseCirc is the model-unit circumference of the head's widest\r\n// slice; scaling by (targetCircumference / baseCirc) both fits the chosen size\r\n// AND converts model units \u2192 metres in one shot. Chin-anchored: because the\r\n// chin sits at the GLB origin, scale.setScalar() grows the head up-and-back\r\n// from a fixed chin \u2014 the anchor Blair asked for.\r\n// baseCirc = model-unit circumference of the widest slice; earRel = the head's\r\n// ear point (temple line) in model units relative to the chin [y depth, z height]\r\nconst FIT_HEAD = { baseCirc: 138.084, earY: 19.856, earZ: 15.432 };\r\n// mutable so the fit head can be positioned LIVE (arrow keys while Fit Check is\r\n// on) \u2014 the on-screen readout prints the values to bake back here.\r\nlet FIT_FWD  = 0.0;    // nudge head along Y (\u2212 = toward face) after ear-align (m)\r\nlet FIT_UP   = -0.03;  // nudge head along Z (+ = up) after ear-align (m)\r\nlet FIT_SIDE = 0.0;    // nudge head along X (+ = viewer's right) (m) \u2014 0 = base centre\r\nlet fitHeadScene = null;\r\nfunction loadFitHead() {\r\n  if (!fitHeadScene)\r\n    fitHeadScene = gltfLoader.loadAsync(ASSET_BASE + 'fit_head.bin' + ASSET_VER).then(g => g.scene);\r\n  return fitHeadScene;\r\n}\r\nlet mannequin = null;\r\nlet xrayed = [];\r\nlet mannequinGen = 0;\r\n\r\n// x-ray: make the printed parts translucent while fit check is on\r\nfunction applyXray(on) {\r\n  for (const r of xrayed) Object.assign(r.material,\r\n    { opacity: r.opacity, transparent: r.transparent, depthWrite: r.depthWrite });\r\n  xrayed = [];\r\n  // the cheek logos keep their texture (x-ray skips mapped materials), so with the\r\n  // head shell see-through they'd float visible from behind \u2014 hide them during fit\r\n  for (const m of logoDecals) m.visible = !on;\r\n  if (!on) return;\r\n  for (const [key, { group }] of slots) {\r\n    if (key === 'ear' || key === 'ear2') continue;   // keep the ears solid during fit check\r\n    group.traverse(o => {\r\n      if (o.isMesh && o.material && !o.material.map) {\r\n        const m = o.material;\r\n        if (xrayed.some(r => r.material === m)) return;\r\n        xrayed.push({ material: m, opacity: m.opacity,\r\n                      transparent: m.transparent, depthWrite: m.depthWrite });\r\n        m.transparent = true;\r\n        m.opacity = Math.min(m.opacity, 0.3);\r\n        m.depthWrite = false;\r\n      }\r\n    });\r\n  }\r\n}\r\n\r\n// Wayfarer-style glasses on the fit head so a customer can gauge glasses\r\n// clearance. Sized as fractions of the head (local space) so they scale/seat\r\n// with the mannequin. fwd = position along the face-front axis (\u2212 = further\r\n// forward, in front of the nose). Tunable by eye like the fit-head knobs.\r\nconst GLASSES = { z: 0.58, fwd: -0.10, lensW: 0.30, lensH: 0.24, frame: 0.028, depth: 0.05, gap: 0.05 };\r\nfunction buildGlasses(box) {\r\n  const size = box.getSize(new THREE.Vector3());\r\n  const w = size.x, h = size.z, d = size.y;\r\n  const eyeZ = box.min.z + h * GLASSES.z;\r\n  const front = box.min.y + d * GLASSES.fwd;      // face front is min.y; \u2212 pushes forward\r\n  const lensW = w * GLASSES.lensW, lensH = w * GLASSES.lensH;\r\n  const frame = w * GLASSES.frame, depth = w * GLASSES.depth, gap = w * GLASSES.gap;\r\n  const half = gap / 2 + lensW / 2;               // lens-centre offset from the midline\r\n  const g = new THREE.Group();\r\n  const mat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.4, metalness: 0.2 });\r\n  const bar = (cx, cz, bw, bh) => {               // a frame bar (box) in the XZ plane, facing \u00B1Y\r\n    const m = new THREE.Mesh(new THREE.BoxGeometry(bw, depth, bh), mat);\r\n    m.position.set(cx, front, cz); g.add(m);\r\n  };\r\n  for (const sgn of [-1, 1]) {\r\n    const cx = sgn * half;\r\n    bar(cx, eyeZ + lensH / 2 - frame / 2, lensW, frame);    // top\r\n    bar(cx, eyeZ - lensH / 2 + frame / 2, lensW, frame);    // bottom\r\n    bar(cx - lensW / 2 + frame / 2, eyeZ, frame, lensH);    // outer\r\n    bar(cx + lensW / 2 - frame / 2, eyeZ, frame, lensH);    // inner\r\n    const arm = new THREE.Mesh(new THREE.BoxGeometry(depth, d * 0.62, frame), mat);   // temple, back +Y\r\n    arm.position.set(sgn * (half + lensW / 2 - depth / 2), front + d * 0.31, eyeZ + lensH * 0.28);\r\n    g.add(arm);\r\n  }\r\n  const bridge = new THREE.Mesh(new THREE.BoxGeometry(gap + frame * 2, depth, frame), mat);\r\n  bridge.position.set(0, front, eyeZ + lensH / 2 - frame);\r\n  g.add(bridge);\r\n  g.traverse(o => { if (o.isMesh) o.renderOrder = 2; });\r\n  return g;\r\n}\r\n\r\nasync function updateMannequin() {\r\n  const gen = ++mannequinGen;\r\n  if (mannequin) { root.remove(mannequin); mannequin = null; }\r\n  applyXray(state.fitOn);\r\n  const badge = document.getElementById('fit-badge');\r\n  if (!state.fitOn) { badge.style.display = 'none'; return; }\r\n\r\n  const { snug, max, note } = fitLimits();\r\n  const level = state.circIn > max ? 2 : state.circIn > snug ? 1 : 0;\r\n  const tint = [0x2EECC5, 0xFFB020, 0xFF4D6A][level];\r\n\r\n  const src = await loadFitHead();\r\n  if (gen !== mannequinGen || !state.fitOn) return;   // superseded or toggled off mid-load\r\n\r\n  const head = src.clone(true);\r\n  const mat = new THREE.MeshStandardMaterial({ color: tint, transparent: true,\r\n    opacity: 0.42, roughness: 0.6, depthWrite: false });\r\n  head.traverse(o => { if (o.isMesh) { o.material = mat; o.renderOrder = 1; } });\r\n\r\n  const g = new THREE.Group();\r\n  g.add(head);\r\n  if (state.glassesOn) g.add(buildGlasses(new THREE.Box3().setFromObject(head)));\r\n  const s = (state.circIn * 0.0254) / FIT_HEAD.baseCirc;   // fit size + units\u2192m\r\n  g.scale.setScalar(s);\r\n\r\n  // Seat the head in the helmet's main cavity by aligning the mannequin's ear\r\n  // line (the head's natural anchor) to the head-base bounding box \u2014 which is\r\n  // stable regardless of which ears/fins are fitted (the ear PART is a bad\r\n  // height reference: tall ears like bat sit far above the ear canal). The head\r\n  // is chin-origin, so its ear point is s\u00B7(0, earY, earZ) above the chin.\r\n  let px = 0, py = 0.14, pz = 0.06;\r\n  const base = slots.get('base');\r\n  if (base) {\r\n    const mb = new THREE.Box3().setFromObject(base.group).applyMatrix4(worldToRoot());\r\n    const c = mb.getCenter(new THREE.Vector3());\r\n    px = c.x + FIT_SIDE;                       // centred left-right (+ nudge)\r\n    py = c.y - s * FIT_HEAD.earY + FIT_FWD;    // ear line at base-centre depth\r\n    pz = c.z - s * FIT_HEAD.earZ + FIT_UP;     // ear line at base-centre height\r\n  }\r\n  g.position.set(px, py, pz);\r\n  mannequin = g;\r\n  root.add(mannequin);\r\n\r\n  const styleName = state.style.toUpperCase();\r\n  badge.style.display = 'block';\r\n  badge.textContent = [\r\n    `\u2713 Fits ${styleName} comfortably (up to ${snug}\u2033)`,\r\n    `\u25D0 Snug fit for ${styleName} \u2014 padding may need to be thin, and the helmet should go on gently`,\r\n    `\u26A0 Over the ${max}\u2033 limit for ${styleName} \u2014 it isn't going to fit`,\r\n  ][level];\r\n  if (note && level < 2) badge.textContent += '  \u00B7  ' + note;   // style-specific caveat (e.g. MK2 entry hole)\r\n  badge.style.color = ['var(--teal)', '#FFB020', '#FF4D6A'][level];\r\n}\r\n\r\ndocument.getElementById('fit-on').onchange = (e) => {\r\n  state.fitOn = e.target.checked;\r\n  const disp = state.fitOn ? 'flex' : 'none';\r\n  document.getElementById('fit-size-row').style.display = disp;\r\n  document.getElementById('fit-glasses-row').style.display = disp;\r\n  updateMannequin();\r\n};\r\ndocument.getElementById('fit-glasses').onchange = (e) => {\r\n  state.glassesOn = e.target.checked;\r\n  updateMannequin();\r\n};\r\ndocument.getElementById('fit-circ').oninput = (e) => {\r\n  state.circIn = Number(e.target.value);\r\n  document.getElementById('fit-circ-val').textContent =\r\n    `${state.circIn}\u2033 \u00B7 ${(state.circIn * 2.54).toFixed(1)} cm`;\r\n  updateMannequin();\r\n};\r\n// LIVE fit-head positioning \u2014 DEV ONLY (add ?fitpos to the URL). Arrow keys\r\n// nudge the mannequin while Fit Check is on; the readout prints the FIT_* values\r\n// to bake into the consts. Gated so stray arrow keys never move it in normal use.\r\nconst FIT_POS_MODE = new URLSearchParams(location.search).has('fitpos');\r\naddEventListener('keydown', (e) => {\r\n  if (!FIT_POS_MODE || !state.fitOn) return;\r\n  const S = e.shiftKey ? 0.002 : 0.005;   // Shift = 2 mm fine, else 5 mm\r\n  let hit = true;\r\n  switch (e.key) {\r\n    case 'ArrowLeft':  FIT_SIDE -= S; break;\r\n    case 'ArrowRight': FIT_SIDE += S; break;\r\n    case 'ArrowUp':    FIT_UP   += S; break;\r\n    case 'ArrowDown':  FIT_UP   -= S; break;\r\n    case 'PageUp':     FIT_FWD  -= S; break;   // \u2212 = toward the face\r\n    case 'PageDown':   FIT_FWD  += S; break;\r\n    default: hit = false;\r\n  }\r\n  if (!hit) return;\r\n  e.preventDefault();\r\n  updateMannequin();\r\n  const pos = document.getElementById('fit-pos');\r\n  pos.style.display = 'block';\r\n  pos.textContent = `SIDE ${FIT_SIDE.toFixed(3)}  UP ${FIT_UP.toFixed(3)}  FWD ${FIT_FWD.toFixed(3)}`;\r\n});\r\n\r\n// \u2550\u2550 LED panels (per style + format, spatial face mapping \u2014 from harness) \u2550\u2550\r\n// MAX7219 is single-color: the whole face renders in the customer's chosen LED\r\n// color. NeoPixel/HUB75 are full-color (null = use the face's own RGB).\r\n// Matched by eye to the real matrices in the acc-max16 listing photos (+ Blair's\r\n// red photo): blue = deep 470nm (not cyan), white = COOL blue-white, yellow =\r\n// golden 590nm amber, orange = amber-orange, pink leans magenta, purple leans\r\n// blue-violet, red = pure 630nm (not scarlet).\r\nconst MAX_HEX = { BLU: '#1a1aff', CHT: '#8bff00', GRN: '#00ff44', ORG: '#ff8300',\r\n                  PNK: '#ff54c8', PUR: '#8932fb', RED: '#ff1500', WHT: '#f2f6ff', YEL: '#ffc400' };\r\nconst ledOverride = () => state.display === 'MAX7219' ? (MAX_HEX[state.maxColor] || '#ff2a00') : null;\r\nconst faceCanvas = document.createElement('canvas');\r\nfaceCanvas.width = 128; faceCanvas.height = 32;\r\nconst faceCtx = faceCanvas.getContext('2d', { willReadFrequently: true });\r\nlet ledPanels = [];\r\nlet faceBitmap = null;          // uploaded static face (HUB75 Starter / WF1)\r\n\r\n// HUB75 default face \u2014 Blair's full-colour face design (Starter shows it static,\r\n// its own colours; Studio recolours its shape with the chosen effect e.g. a\r\n// gradient). Served alongside the GLBs; until the asset exists the HUB75 face\r\n// falls back to the procedural animation, so this never breaks the preview.\r\nlet hub75DefaultFace = null;\r\n(function () {\r\n  const im = new Image();\r\n  im.onload = () => { hub75DefaultFace = im; };\r\n  im.onerror = () => { hub75DefaultFace = null; };\r\n  im.src = ASSET_BASE + 'face_hub75_default.png' + ASSET_VER;\r\n})();\r\n// Studio-HUB75 (PF 3.0) can animate: a horizontal 19-frame sprite sheet (2432\u00D732,\r\n// each frame 128\u00D732) of Blair's face gif. Starter (WF1) stays on the static frame.\r\nlet hub75Frames = null;\r\nconst HUB75_FRAMES = 19, HUB75_FPS = 10;\r\n(function () {\r\n  const im = new Image();\r\n  im.onload = () => { hub75Frames = im; };\r\n  im.onerror = () => { hub75Frames = null; };\r\n  im.src = ASSET_BASE + 'face_hub75_frames.png' + ASSET_VER;\r\n})();\r\n\r\n// \u2500\u2500 Firmware face library (MAX7219 / NeoPixel modules) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n// faces.json = the REAL bitmap face engine exported from the ESP32 firmware:\r\n// eye/nose/mouth presets decoded to upright pixel grids (the firmware's 180\u00B0\r\n// module rotation already undone) + the exact animation specs. Each cluster\r\n// panel gets its matching sprite drawn into the precise canvas region it\r\n// samples (renderLED's mapping is linear, so module cells reproduce sprite\r\n// pixels 1:1). Loaded at boot; falls back to the procedural face if missing.\r\nlet FACES = null;                                   // parsed faces.json\r\nlet boopUntil = -1;                                 // heart-eyes until this clock time\r\n// customer-picked preset indices live in state.faceEyes/faceNose/faceMouth;\r\n// wrap-safe lookup (share links may carry any integer)\r\nconst facePick = (list, v) => list[(((v | 0) % list.length) + list.length) % list.length];\r\nlet moduleRects = {};                               // label \u2192 canvas rect (set per frame in renderLED)\r\n// Mouth-side strips: each physical 8px module is mounted horizontally flipped\r\n// relative to the tile run, so every tile's art is flipped in place while the\r\n// tile ORDER stays centre\u2192corner (settled empirically against Blair's real\r\n// head via a 4-way ?mouthorder A/B \u2014 this was mode 3).\r\nfunction orientStrip(g) {\r\n  return g.map(row => {\r\n    const out = [];\r\n    for (let i = 0; i < row.length; i += 8) out.push(...row.slice(i, i + 8).reverse());\r\n    return out;\r\n  });\r\n}\r\n\r\n// blink state (firmware: row-wipe, 40ms/step, random 2\u20138s interval)\r\nconst BLINK_PAIRS = [[0, 7], [1, 6], [2, 5], [3, 4]];\r\nlet blinkStart = -1, nextBlink = 3;\r\nfunction eyeWithBlink(t, grid) {\r\n  if (blinkStart < 0 && t >= nextBlink) blinkStart = t;\r\n  if (blinkStart < 0) return grid;\r\n  const step = Math.floor((t - blinkStart) / 0.04);\r\n  if (step > 7) { blinkStart = -1; nextBlink = t + 2 + Math.random() * 6; return grid; }\r\n  const n = step <= 3 ? step + 1 : 7 - step;        // close outside-in, reopen inside-out\r\n  const cleared = new Set();\r\n  for (let i = 0; i < n; i++) { cleared.add(BLINK_PAIRS[i][0]); cleared.add(BLINK_PAIRS[i][1]); }\r\n  return grid.map((row, y) => cleared.has(y) ? row.map(() => 0) : row);\r\n}\r\n// nose \"breathing\" (firmware: dilate 1px, resting 5s / expanded 2s)\r\nfunction dilate4(g) {\r\n  const h = g.length, w = g[0].length;\r\n  return g.map((row, y) => row.map((v, x) =>\r\n    v || (y > 0 && g[y-1][x]) || (y < h-1 && g[y+1][x]) || (x > 0 && g[y][x-1]) || (x < w-1 && g[y][x+1]) ? 1 : 0));\r\n}\r\n// mouth talk (firmware: per-tile vertical dilation 0\u20133 steps, progressive mode \u2014\r\n// centre opens most; mic level simulated with a speech-ish oscillation)\r\nfunction mouthWithTalk(t, grid) {\r\n  const level = Math.max(0, Math.sin(t * 2.2)) * (0.55 + 0.45 * Math.sin(t * 6.1));\r\n  if (level < 0.15) return grid;\r\n  const g = grid.map(r => r.slice());\r\n  for (let tile = 0; tile < 10; tile++) {\r\n    const steps = Math.min(3, Math.round(level * 3 * (1 - Math.abs(tile - 4.5) / 5.5)));\r\n    for (let s = 0; s < steps; s++) {\r\n      const snap = g.map(r => r.slice());\r\n      for (let y = 0; y < 8; y++) for (let x = tile * 8; x < tile * 8 + 8; x++)\r\n        g[y][x] = snap[y][x] || (y > 0 && snap[y-1][x]) || (y < 7 && snap[y+1][x]) ? 1 : 0;\r\n    }\r\n  }\r\n  return g;\r\n}\r\n// draw a 0/1 grid stretched into a canvas rect (integer-snapped, no AA bleed)\r\nfunction drawGrid(grid, r, colA = 0, colB = null) {\r\n  const c = faceCtx; c.fillStyle = '#ffffff';\r\n  const b = colB ?? grid[0].length, gw = b - colA, gh = grid.length;\r\n  const sx = (r.x1 - r.x0) / gw, sy = (r.y1 - r.y0) / gh;\r\n  for (let gy = 0; gy < gh; gy++) for (let gx = 0; gx < gw; gx++) {\r\n    if (!grid[gy][colA + gx]) continue;\r\n    const x = Math.round(r.x0 + gx * sx), y = Math.round(r.y0 + gy * sy);\r\n    c.fillRect(x, y, Math.max(1, Math.round(r.x0 + (gx + 1) * sx) - x),\r\n                     Math.max(1, Math.round(r.y0 + (gy + 1) * sy) - y));\r\n  }\r\n}\r\n// Build each cluster's sprite as a per-panel grid \u2014 the panels take their\r\n// content BY TILE like the firmware, not by sampling the shared canvas (cluster\r\n// regions overlap in the flattened canvas, which would cross-contaminate).\r\n// Twins mirror the main texture, so the RIGHT half is enough: eye R twin shows\r\n// leftEye (= rightEye mirrored), mouth R twin shows mouth L (symmetric).\r\n// The sprites are ALSO drawn onto the face canvas so the spill light's dominant\r\n// colour and the canvas debug view track the real face.\r\nlet panelGrids = null;   // label \u2192 2D 0/1 grid, read directly by renderLED\r\nconst slice = (g, a, b) => g.map(row => row.slice(a, b));\r\nfunction renderFirmwareFace(t) {\r\n  panelGrids = null;\r\n  if (!FACES) return false;\r\n  const P = FACES.presets, grids = {};\r\n  const rEye = moduleRects['eye R'];\r\n  if (rEye) {\r\n    // boop! clicking the visor tip flashes heart eyes, like the real head's sensor\r\n    if (t < boopUntil && P.animatedEyes?.length) {\r\n      const anim = P.animatedEyes.find(a => /heart/i.test(a.name)) || P.animatedEyes[0];\r\n      grids['eye R'] = anim.frames[Math.floor(t / 0.25) % anim.frameCount];\r\n    } else {\r\n      grids['eye R'] = eyeWithBlink(t, facePick(P.eyes, state.faceEyes).rightEye);\r\n    }\r\n  }\r\n  const breathing = (t % 7) >= 5;   // resting 5s, expanded 2s\r\n  const rNose = moduleRects['nose'];\r\n  if (rNose) { const g = facePick(P.noses, state.faceNose).grid; grids['nose'] = breathing ? dilate4(g) : g; }\r\n  const rNoseR = moduleRects['nose R'];   // legacy split nose: one 8\u00D78 per side\r\n  if (rNoseR) { const g = facePick(P.nosesSingle, state.faceNose).grid;\r\n    grids['nose R'] = breathing ? dilate4(g) : g; }\r\n  const rMid = moduleRects['mouth mid'], rR = moduleRects['mouth R'];\r\n  if (rMid || rR) {\r\n    const mouth = mouthWithTalk(t, facePick(P.mouths, state.faceMouth).grid);\r\n    const midW = rMid ? rMid.cols : 0;              // slice the 80px grid centred at 40\r\n    if (rMid) grids['mouth mid'] = slice(mouth, 40 - midW / 2, 40 + midW / 2);\r\n    if (rR) grids['mouth R'] = orientStrip(\r\n      slice(mouth, 40 + midW / 2, Math.min(80, 40 + midW / 2 + rR.cols)));\r\n  }\r\n  if (!Object.keys(grids).length) return false;\r\n  panelGrids = grids;\r\n  for (const [label, g] of Object.entries(grids))   // canvas copy (spill colour/debug)\r\n    if (moduleRects[label]) drawGrid(g, moduleRects[label]);\r\n  return true;\r\n}\r\n\r\n// Full-color face effects \u2014 only the PF 3.0 controller (Studio-HUB75 + D-Lux\r\n// NeoPixel) can do these; they colour each lit face pixel. A curated set: the\r\n// point is to SHOW capability, not to be a face editor (that's the Face Maker).\r\nconst FACE_EFFECTS = ['solid', 'gradient', 'rainbow', 'plasma', 'scanline'];\r\nfunction effectColor(eff, x, y, t) {\r\n  switch (eff) {\r\n    case 'gradient': {                       // teal \u2192 purple, top to bottom\r\n      const f = y / 32;\r\n      return `rgb(${Math.round(46 + f * 109)},${Math.round(236 - f * 141)},${Math.round(197 + f * 30)})`;\r\n    }\r\n    case 'rainbow':                          // diagonal band that flows across the face\r\n      return `hsl(${Math.round((x / 128 * 300 + y / 32 * 40 + t * 110) % 360)},100%,55%)`;\r\n    case 'plasma': {\r\n      const v = Math.sin(x * 0.18 + t) + Math.sin(y * 0.28 + t * 1.3) + Math.sin((x + y) * 0.14 + t * 0.7);\r\n      return `hsl(${((Math.round(v * 60 + t * 30)) % 360 + 360) % 360},95%,56%)`;\r\n    }\r\n    case 'scanline': {                        // bright teal bar sweeping across\r\n      const bar = (t * 46) % 150 - 8;\r\n      return `hsl(174,100%,${Math.max(34, 66 - Math.abs(x - bar) * 3.5)}%)`;\r\n    }\r\n    case 'solid':\r\n    default:\r\n      return '#2EECC5';                       // brand teal\r\n  }\r\n}\r\n// which face effect drives the current build (null = monochrome / static image)\r\nfunction faceEffect() {\r\n  if (!state.display || state.display === 'MAX7219') return null;         // mono, recoloured by override\r\n  if (state.display === 'HUB75' && state.collection === 'starter') return null;  // WF1: static upload only\r\n  return state.facePreset || 'solid';                                    // PF 3.0 full-colour\r\n}\r\n\r\nfunction drawFace(t) {\r\n  panelGrids = null;   // only the firmware-face path below repopulates it\r\n  const c = faceCtx;\r\n  c.fillStyle = '#000'; c.fillRect(0, 0, 128, 32);\r\n  const eff = faceEffect();\r\n  const paint = (x, y, w = 1, h = 1) => {\r\n    if (x < 0 || x >= 128 || y < 0 || y >= 32) return;\r\n    c.fillStyle = eff ? effectColor(eff, x, y, t) : '#ffffff';   // white \u2192 override recolours (MAX)\r\n    c.fillRect(x, y, w, h);\r\n  };\r\n  // face is bilaterally symmetric about x=63.5 \u2014 draw the right half, mirror the left\r\n  const mir = (x, y, w = 1, h = 1) => { paint(x, y, w, h); paint(127 - x - (w - 1), y, w, h); };\r\n  // repaint the currently-lit pixels through paint() so the effect colouring\r\n  // (MAX = white\u2192override; NeoPixel/Studio = effect) applies to a stamped mask\r\n  const recolorMask = () => {\r\n    const m = c.getImageData(0, 0, 128, 32).data;\r\n    c.fillStyle = '#000'; c.fillRect(0, 0, 128, 32);\r\n    for (let y = 0; y < 32; y++) for (let x = 0; x < 128; x++) {\r\n      const i = (y * 128 + x) * 4;\r\n      if (m[i] + m[i + 1] + m[i + 2] >= 40) paint(x, y);\r\n    }\r\n  };\r\n\r\n  // \u2500\u2500 HUB75: the customer's face design \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n  // Starter (WF1) = static image in its own colours; Studio (PF 3.0) = same shape\r\n  // recoloured by the selected effect (e.g. gradient). Uploaded image wins over\r\n  // the shipped default; if neither exists we fall through to the procedural face.\r\n  if (state.display === 'HUB75') {\r\n    const starter = state.collection === 'starter';\r\n    // Studio (PF 3.0): animate the face + recolour it with the effect. Starter\r\n    // (WF1) and any uploaded image stay static.\r\n    if (!starter && !faceBitmap && hub75Frames && eff) {\r\n      const f = Math.floor(t * HUB75_FPS) % HUB75_FRAMES;\r\n      c.imageSmoothingEnabled = false;\r\n      c.drawImage(hub75Frames, f * 128, 0, 128, 32, 0, 0, 128, 32);\r\n      recolorMask();\r\n      return;\r\n    }\r\n    const img = faceBitmap || hub75DefaultFace;\r\n    if (img) {\r\n      c.imageSmoothingEnabled = false;\r\n      c.drawImage(img, 0, 0, 128, 32);\r\n      if (eff) recolorMask();                 // Studio fallback: static shape + effect\r\n      return;\r\n    }\r\n  }\r\n\r\n  // \u2500\u2500 MAX7219 / NeoPixel: the REAL firmware face (faces.json sprites) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n  // Each cluster gets its firmware sprite with the firmware's animations (blink /\r\n  // nose-breathe / talking mouth). Drawn white; recolorMask applies the NeoPixel\r\n  // effect, renderLED's override recolours MAX7219 mono.\r\n  if (state.display === 'MAX7219' || state.display === 'NeoPixel') {\r\n    if (renderFirmwareFace(t)) { if (eff) recolorMask(); return; }\r\n  }\r\n\r\n  // \u2500\u2500 Procedural fallback (faces.json missing / no module layout) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n  // Bold angular slash eyes + a wide sawtooth grin, with blink + talk. paint()\r\n  // handles colour (MAX = white\u2192mono override; NeoPixel = the full-colour effect).\r\n\r\n  // Eyes \u2014 a pointed angular slash slanting up toward the outer corner, mirrored.\r\n  const closed = Math.sin(t * 0.9) > 0.96;    // occasional quick blink\r\n  const EX0 = 78, EX1 = 111;                   // right eye x-span (mirrored for left)\r\n  for (let x = EX0; x <= EX1; x++) {\r\n    const p = (x - EX0) / (EX1 - EX0);          // 0 = inner (nose side) \u2026 1 = outer\r\n    if (closed) { mir(x, 8, 1, 2); continue; }  // blink \u2192 thin bar\r\n    const cy = 10 - 6 * p;                      // slant up toward the outer corner\r\n    const half = 1 + 2.6 * Math.sin(Math.PI * p);   // taper to points at both ends\r\n    for (let y = Math.round(cy - half); y <= Math.round(cy + half); y++) mir(x, y);\r\n  }\r\n\r\n  // Nose \u2014 a small central accent\r\n  mir(66, 16); mir(66, 17);\r\n\r\n  // Mouth \u2014 a bold toothed grin spanning the FULL width so every mouth module\r\n  // lights (mouth L samples canvas x0-60, mid x49-79, R x68-128). Follows the\r\n  // module layout: higher at the corners, dipping in the middle; a filled band\r\n  // (not a thin line) so it covers the modules' full 8-row height. Teeth sharpen\r\n  // as it \"talks\".\r\n  const talk = 0.5 + 0.5 * Math.abs(Math.sin(t * 2.5));\r\n  const MX0 = 3, MX1 = 124, TEETH = 13;\r\n  for (let x = MX0; x <= MX1; x++) {\r\n    const u = (x - MX0) / (MX1 - MX0);\r\n    const base = 24 + Math.round(6 * Math.sin(u * Math.PI));   // 24 at corners \u2192 30 centre\r\n    const tri = Math.abs(((u * TEETH) % 1) * 2 - 1);           // 0..1 triangle wave = teeth\r\n    const top = base - 5 - Math.round(tri * 3 * talk);         // toothed upper edge\r\n    for (let y = top; y <= base; y++) paint(x, y);\r\n  }\r\n}\r\n\r\nlet lastLEDKey = null;\r\nfunction spawnLED() {\r\n  for (const p of ledPanels) root.remove(p.group);\r\n  ledPanels = [];\r\n  if (!state.display) { lastLEDKey = null; return; }   // head-base-only: no electronics\r\n  const fmt = state.display === 'HUB75' ? 'hub75' : 'modules';\r\n  const cfg = manifest.led?.[state.style]?.[fmt];\r\n  if (!cfg) { lastLEDKey = null; return; }\r\n  // grow-in only when the panel set actually changes (spawnLED reruns on every\r\n  // applyState \u2014 an ear swap shouldn't re-animate the display)\r\n  const ledKey = `${state.style}/${fmt}`;\r\n  const fresh = ledKey !== lastLEDKey;\r\n  lastLEDKey = ledKey;\r\n  for (const pc of cfg.panels) {\r\n    const canvas = document.createElement('canvas');\r\n    const tex = new THREE.CanvasTexture(canvas);\r\n    tex.magFilter = THREE.NearestFilter;\r\n    // twin shows the same canvas horizontally mirrored \u2014 via a flipped texture,\r\n    // NOT negative scale (that flips winding, which would break the one-sided\r\n    // lit face below)\r\n    const texFlip = new THREE.CanvasTexture(canvas);\r\n    texFlip.magFilter = THREE.NearestFilter;\r\n    texFlip.wrapS = THREE.RepeatWrapping; texFlip.repeat.x = -1;\r\n    const mk = (mirrorTex) => {\r\n      const h = pc.w * pc.rows / pc.cols;\r\n      const geo = new THREE.PlaneGeometry(pc.w, h);\r\n      // lit pixels face the VISOR only; from inside the head the panel reads\r\n      // as a dark PCB back (its own BackSide mesh) instead of glowing in reverse\r\n      const plane = new THREE.Mesh(geo,\r\n        new THREE.MeshBasicMaterial({ map: mirrorTex ? texFlip : tex, transparent: true }));\r\n      plane.rotation.x = Math.PI / 2;\r\n      plane.renderOrder = 1;   // after opaque parts, before the visor\r\n      plane.layers.enable(BLOOM_LAYER);   // only the panels bloom\r\n      const back = new THREE.Mesh(geo,\r\n        new THREE.MeshBasicMaterial({ color: 0x0b0b10, side: THREE.BackSide }));\r\n      back.rotation.x = Math.PI / 2;\r\n      back.renderOrder = 1;\r\n      const g = new THREE.Group();\r\n      g.add(plane); g.add(back);\r\n      return g;\r\n    };\r\n    const r = pc.rotation || [0, 0, 0];\r\n    const main = mk(false);\r\n    main.position.set(...pc.position);\r\n    main.rotation.set(r[0]*Math.PI/180, r[1]*Math.PI/180, r[2]*Math.PI/180);\r\n    root.add(main);\r\n    if (fresh) animateSwapIn(main);   // panels grow in with their display swap\r\n    ledPanels.push({ group: main, canvas, ctx: canvas.getContext('2d'), tex, cfg: pc });\r\n    if (pc.mirrored) {\r\n      const twin = mk(true);\r\n      twin.position.set(-pc.position[0], pc.position[1], pc.position[2]);\r\n      twin.rotation.set(r[0]*Math.PI/180, -r[1]*Math.PI/180, -r[2]*Math.PI/180);\r\n      root.add(twin);\r\n      if (fresh) animateSwapIn(twin);\r\n      ledPanels.push({ group: twin, canvas, ctx: null, tex: texFlip, cfg: pc, twin: true });\r\n    }\r\n  }\r\n}\r\n\r\nfunction renderLED(t) {\r\n  if (!ledPanels.length) return;\r\n  // panel bounding box FIRST \u2014 drawFace's firmware-face path needs each\r\n  // cluster's canvas rect (the linear model\u2192canvas mapping below)\r\n  let minX = 1e9, maxX = -1e9, minZ = 1e9, maxZ = -1e9, sumY = 0, nP = 0;\r\n  for (const p of ledPanels) {\r\n    const hw = p.cfg.w / 2, hh = (p.cfg.w * p.cfg.rows / p.cfg.cols) / 2;\r\n    const x = p.twin ? -p.cfg.position[0] : p.cfg.position[0];\r\n    minX = Math.min(minX, x - hw); maxX = Math.max(maxX, x + hw);\r\n    minZ = Math.min(minZ, p.cfg.position[2] - hh); maxZ = Math.max(maxZ, p.cfg.position[2] + hh);\r\n    sumY += p.cfg.position[1]; nP++;\r\n  }\r\n  // face centre + dominant lit colour \u2192 drive the NeoPixel spill light\r\n  ledCenter.set((minX + maxX) / 2, sumY / nP, (minZ + maxZ) / 2);\r\n  // each non-twin cluster's canvas region (twins mirror the main canvas)\r\n  moduleRects = {};\r\n  {\r\n    const sX = Math.max(1e-6, maxX - minX), sZ = Math.max(1e-6, maxZ - minZ);\r\n    for (const p of ledPanels) {\r\n      if (p.twin || !p.cfg.label) continue;\r\n      const hw = p.cfg.w / 2, hh = (p.cfg.w * p.cfg.rows / p.cfg.cols) / 2;\r\n      moduleRects[p.cfg.label] = {\r\n        x0: (p.cfg.position[0] - hw - minX) / sX * 128,\r\n        x1: (p.cfg.position[0] + hw - minX) / sX * 128,\r\n        y0: (maxZ - (p.cfg.position[2] + hh)) / sZ * 32,\r\n        y1: (maxZ - (p.cfg.position[2] - hh)) / sZ * 32,\r\n        cols: p.cfg.cols, rows: p.cfg.rows };\r\n    }\r\n  }\r\n  drawFace(t);\r\n  const src = faceCtx.getImageData(0, 0, 128, 32).data;\r\n  let cr = 0, cg = 0, cb = 0, cn = 0;\r\n  for (let i = 0; i < src.length; i += 4) {\r\n    if (src[i] + src[i + 1] + src[i + 2] > 60) { cr += src[i]; cg += src[i + 1]; cb += src[i + 2]; cn++; }\r\n  }\r\n  if (cn) ledDominant.setRGB((cr / cn) / 255, (cg / cn) / 255, (cb / cn) / 255);\r\n  const spanX = Math.max(1e-6, maxX - minX), spanZ = Math.max(1e-6, maxZ - minZ);\r\n  const px = 10, gap = 3, cell = px + gap;\r\n  for (const p of ledPanels) {\r\n    if (p.twin) { p.tex.needsUpdate = true; continue; }   // shares main canvas\r\n    const { cols, rows } = p.cfg;\r\n    const W = cols * cell, H = rows * cell;\r\n    if (p.canvas.width !== W || p.canvas.height !== H) { p.canvas.width = W; p.canvas.height = H; }\r\n    const hw = p.cfg.w / 2, hh = (p.cfg.w * rows / cols) / 2;\r\n    p.ctx.fillStyle = '#000'; p.ctx.fillRect(0, 0, W, H);\r\n    const override = ledOverride();\r\n    // firmware face: this cluster's sprite grid indexes cells directly, exactly\r\n    // like the firmware's per-tile bitmaps (no shared-canvas cross-bleed)\r\n    const pg = panelGrids ? panelGrids[p.cfg.label] : null;\r\n    const eff = faceEffect();\r\n    for (let r = 0; r < rows; r++) {\r\n      for (let cI = 0; cI < cols; cI++) {\r\n        const mx = p.cfg.position[0] - hw + (cI + 0.5) / cols * 2 * hw;\r\n        const mz = p.cfg.position[2] + hh - (r + 0.5) / rows * 2 * hh;\r\n        const u = Math.min(127, Math.max(0, Math.floor((mx - minX) / spanX * 128)));\r\n        const v = Math.min(31,  Math.max(0, Math.floor((maxZ - mz) / spanZ * 32)));\r\n        const i = (v * 128 + u) * 4;\r\n        if (pg) {\r\n          const gy = Math.min(pg.length - 1, Math.floor(r / rows * pg.length));\r\n          const gx = Math.min(pg[0].length - 1, Math.floor(cI / cols * pg[0].length));\r\n          if (!pg[gy][gx]) continue;\r\n          p.ctx.fillStyle = override || (eff ? effectColor(eff, u, v, t) : '#ffffff');\r\n        } else {\r\n        if (src[i] + src[i+1] + src[i+2] < 40) continue;\r\n        p.ctx.fillStyle = override || `rgb(${src[i]},${src[i+1]},${src[i+2]})`;\r\n        }\r\n        p.ctx.beginPath();\r\n        p.ctx.arc(cI*cell + cell/2, r*cell + cell/2, px/2, 0, Math.PI*2);\r\n        p.ctx.fill();\r\n      }\r\n    }\r\n    p.tex.needsUpdate = true;\r\n  }\r\n}\r\n\r\n// Spill light \u2014 the lit pixels cast their colour onto the shell/visor. On for any\r\n// panel display now; HUB75/MAX7219 at half NeoPixel intensity. MAX7219 is mono so\r\n// the spill takes its override colour; HUB75/NeoPixel use the dominant face colour.\r\nfunction updateSpill(t) {\r\n  if (!ledPanels.length) { spill.intensity = 0; return; }\r\n  const ov = ledOverride();\r\n  if (ov) spill.color.set(ov); else spill.color.copy(ledDominant);\r\n  spill.intensity = SPILL_INTENSITY * glowScale() * (0.88 + 0.12 * Math.sin(t * 3));\r\n  spill.position.set(ledCenter.x, ledCenter.y - 0.02, ledCenter.z);   // just in front of the face\r\n}\r\n\r\n// \u2550\u2550 Selection logic \u2550\u2550\r\nfunction frameFor(style, display) {\r\n  const variant = `frame_${style}_${display}`;\r\n  const hit = Object.keys(manifest.parts)\r\n    .find(n => n.toLowerCase() === variant.toLowerCase());\r\n  return hit || Object.keys(manifest.parts)\r\n    .find(n => n.toLowerCase() === `frame_${style}`.toLowerCase()) || null;\r\n}\r\n// modern styles need a real LED layout for the display to count; legacy heads\r\n// are offered per their collection (frames/layouts are still being added).\r\n// Styles sold with a HUB75 (P3) display \u2014 Studio/Starter HUB75 heads exist only\r\n// for these; iris/nyx are MAX7219-only. (Legacy mk3/mk5 are here too.)\r\nconst HUB75_STYLES = new Set(['mk3', 'mk5', 'cipher', 'vex']);\r\nconst NO_MAX7219 = new Set(['mk3']);   // MK3 MAX7219 disabled for now\r\nconst displayAvailable = (style, d) =>\r\n  d === 'HUB75' ? HUB75_STYLES.has(style)\r\n  : d === 'MAX7219' && NO_MAX7219.has(style) ? false\r\n                : (isLegacy(style) || !!manifest.led?.[style]?.modules);\r\n\r\nfunction snapsFor(cat) { return manifest.adjust?.[cat]?.snaps || null; }\r\n\r\nlet applyGen = 0;\r\nasync function applyState() {\r\n  // guard against rapid successive calls racing: only the latest run paints\r\n  const gen = ++applyGen;\r\n  // collection gating: keep style / shape / display / fins legal\r\n  const styles = collectionStyles(state.collection);\r\n  if (styles.length && !styles.includes(state.style)) state.style = styles[0];\r\n  const shapes = shapesFor(state.style);\r\n  state.shape = shapes.length ? (shapes.includes(state.shape) ? state.shape : shapes[0]) : null;\r\n  const legacy = isLegacy(state.style);\r\n  if (legacy) state.ear2 = 'none';        // legacy heads have no secondary ears\r\n  // modern heads always ship WITH a fin \u2014 no fin-less variant exists on Shopify\r\n  // (every ST/DL/HB head SKU carries a fin segment), so 'none' is not orderable\r\n  if (!legacy && !visorOnly() && (!state.fin || state.fin === 'none'))\r\n    state.fin = partsByCat('fin').find(n => !NOT_SELLABLE.has(n)) || state.fin;\r\n  if (ART_SUBMIT_MODE || CHEEKART_MODE) state.cheekMode = ((CHEEKART_MODE || SHOP_ART_MODE) && CHEEKART_NP) ? 'neopixel' : 'bubble';   // the whole point is the cheek\r\n  // accessories whose backing product is still draft aren't orderable yet\r\n  if (hasEars() && !accLive(earHandleFor('ear', state.ear))) state.ear = 'none';\r\n  if (hasEar2() && !accLive(earHandleFor('ear2', state.ear2))) state.ear2 = 'none';\r\n  // custom colors can't ride a head style the color add-on doesn't cover (or a\r\n  // draft add-on) \u2014 clear them so the preview never shows an unbuyable scheme\r\n  if (Object.keys(state.colors || {}).length && !colorBoothLive()) state.colors = {};\r\n  const disp = allowedDisplays();\r\n  state.display = disp.includes(state.display) ? state.display : (disp[0] ?? null);\r\n\r\n  const loading = document.getElementById('cfg-loading');\r\n  loading.style.opacity = 1;\r\n  const jobs = [];\r\n  // legacy assembly (head+frame+visor) rides a single per-style translation so\r\n  // it lands where the studio ears mount; modern parts self-assemble.\r\n  const legShift = legacy ? (manifest.legacy_shift?.[state.style]?.offset || [0, 0, 0]) : null;\r\n  // group shift + the part's own individual correction (frame/visor rotations\r\n  // ride on their part entries via buildPart's fallback); the head's own\r\n  // rotation lives in legacy_shift.rotation\r\n  const asmOpt = (nm, isHead) => {\r\n    if (!legShift) return {};\r\n    const own = (nm && manifest.parts[nm]?.offset) || [0, 0, 0];\r\n    const o = { offset: [legShift[0] + own[0], legShift[1] + own[1], legShift[2] + own[2]] };\r\n    const headRot = isHead ? manifest.legacy_shift?.[state.style]?.rotation : null;\r\n    if (headRot) o.rotation = headRot;\r\n    return o;\r\n  };\r\n  const headName = headShell(state.style, state.shape);\r\n  const frameName = frameFor(state.style, state.display);\r\n  const visorName = Object.keys(manifest.parts)\r\n    .find(n => n.toLowerCase() === `visor_${state.style}`) || null;\r\n  // 'Visor only' shows just the visor; the modern visor-off preview removes\r\n  // visor + gasket together (they come off as one piece on the real head)\r\n  const vOnly = visorOnly();\r\n  const noVisor = !legacy && !vOnly && state.visorOff;\r\n  jobs.push(setSlot('base', vOnly ? null : headName, asmOpt(headName, true)));\r\n  jobs.push(setSlot('frame', vOnly ? null : frameName, asmOpt(frameName)));\r\n  // gasket = the style's own gasket if that part exists (ZR is the only legacy\r\n  // head with one; mk3/mk5/mk2 have none \u2192 null). Hidden when the visor is off\r\n  // (visor+gasket come off together). In visor-only the modern visor ships alone\r\n  // (no gasket), but the legacy ZR visor bundles its gasket \u2014 so show it there.\r\n  jobs.push(setSlot('gasket', (noVisor || (vOnly && !legacy)) ? null : (Object.keys(manifest.parts)\r\n    .find(n => n.toLowerCase() === `gasket_${state.style}`) || null)));\r\n  jobs.push(setSlot('visor', noVisor ? null : visorName, asmOpt(visorName)));\r\n  // ears self-assemble at their studio position for BOTH modern and legacy\r\n  // (legacy heads are moved onto the ears, not the reverse). Cheeks hidden on legacy.\r\n  const earSnaps = snapsFor('ear');\r\n  jobs.push(setSlot('ear', vOnly ? 'none' : state.ear,\r\n    earSnaps?.[state.earSnap] ? { offset: earSnaps[state.earSnap] } : {}));\r\n  const ear2Snaps = snapsFor('ear2');\r\n  jobs.push(setSlot('ear2', (legacy || vOnly) ? 'none' : state.ear2,\r\n    ear2Snaps?.[state.ear2Snap] ? { offset: ear2Snaps[state.ear2Snap] } : {}));\r\n  const finSnaps = snapsFor('fin');\r\n  const finFixed = manifest.adjust?.fin?.exclude?.includes(state.fin);\r\n  jobs.push(setSlot('fin', effectiveFin(),\r\n    (!finFixed && finSnaps?.[state.finSnap]) ? { rotation: finSnaps[state.finSnap] } : {}));\r\n  // cheeks: bubble (plate + tinted dome, rides the head's legacy_shift) when\r\n  // chosen/legacy; otherwise the auto studs/vents that come with the fin\r\n  if (bubbleActive()) {\r\n    const plate = cheekPlatePart(), dome = bubbleDomePart();\r\n    jobs.push(setSlot('cheek', plate, asmOpt(plate)));\r\n    jobs.push(setSlot('bubble', dome, asmOpt(dome)));\r\n  } else {\r\n    jobs.push(setSlot('cheek', autoCheek()));\r\n    jobs.push(setSlot('bubble', null));\r\n  }\r\n  await Promise.all(jobs);\r\n  if (gen !== applyGen) return;   // superseded by a later click \u2014 don't paint\r\n  applyColors();       // Color Booth repaint \u2014 parts always build in stock colors\r\n  spawnLED();\r\n  await rebuildLogoDecals();\r\n  await rebuildCheekRings();\r\n  if (gen !== applyGen) return;\r\n  recenterSpin();      // spin axis through the head's centre (no on-camera sweep)\r\n  applyVisorLook(vOnly);   // studio reflections + lifted bg so a lone visor is visible\r\n  updateOverlapNote(); // fin\u2194ear clash \u2192 point at the adjusters\r\n  updateMannequin();   // re-seat + re-xray against the fresh parts\r\n  // a lone visor is far smaller than a head \u2014 reframe when crossing that boundary\r\n  if (frameCamera.lastVOnly !== vOnly) { frameCamera.done = false; frameCamera.lastVOnly = vOnly; }\r\n  frameCamera();\r\n  renderUI();\r\n  loading.style.opacity = 0;\r\n}\r\n\r\nfunction frameCamera() {\r\n  // aim at the shell's centre (on the spin axis, so the spin angle doesn't\r\n  // matter): parts coming and going never re-aim the view\r\n  const core = headCoreBox();\r\n  if (core.isEmpty()) return;\r\n  const center = root.localToWorld(core.getCenter(new THREE.Vector3()));\r\n  const size = core.getSize(new THREE.Vector3());   // root space: Z is up\r\n  // aim slightly BELOW centre so the head rides higher in the frame (less\r\n  // empty headroom above it); tighter dist = the head fills more of the view\r\n  controls.target.copy(center);\r\n  controls.target.y -= size.z * 0.10;\r\n  if (!frameCamera.done) {\r\n    const dist = Math.max(size.x, size.y, size.z) * 1.55;\r\n    camera.position.set(center.x + dist*0.45, center.y + dist*0.22, center.z + dist*0.85);\r\n    frameCamera.done = true;\r\n  }\r\n}\r\n\r\n// \u2550\u2550 What's offerable \u2550\u2550 the ONE place both panel modes ask whether an option\r\n// can be bought right now: soon = its product is still a draft, sold = every\r\n// variant it leads to is sold out. (Which styles / displays / fins a build can\r\n// have at all is structural: collectionStyles, allowedDisplays and\r\n// displayAvailable gate guided and Advanced identically \u2014 they share sections.)\r\nfunction headFamily(coll, style) {\r\n  const st = STYLE_CODE[style];\r\n  if (coll === 'visor') return `VS-${st}`;\r\n  if (coll === 'baseonly') return `HB-${st}`;\r\n  return `${COLL_CODE[coll]}-${st}`;\r\n}\r\nfunction optionStatus(kind, v) {\r\n  const coll = state.collection, st = STYLE_CODE[state.style];\r\n  const earCode = () => EAR_CODE[earBase(v || '')];\r\n  switch (kind) {\r\n    case 'collection': return { sold: allSold(COLL_CODE[v]) };\r\n    case 'style':      return { soon: !styleLive(v), sold: allSold(headFamily(coll, v)) };\r\n    case 'display':    return { sold: allSold(`${COLL_CODE[coll]}-${st}-${DISPLAY_CODE[v]}`) };\r\n    case 'maxColor':   return { sold: allSold(`${COLL_CODE[coll]}-${st}-MAX-${v}`) };\r\n    case 'fin':        return { sold: isSold(skuWith({ fin: v })) };\r\n    case 'shape':      return { sold: isSold(skuWith({ shape: v })) };\r\n    case 'ear':        return { soon: !accLive(earHandleFor('ear', v)), sold: !!earCode() && allSold(`ER-${earCode()}`) };\r\n    case 'ear2':       return { soon: !accLive(earHandleFor('ear2', v)), sold: !!earCode() && allSold(`ER-${earCode()}2`) };\r\n    case 'earSize':    return { sold: isSold(skuWith({ ear: v }, skuEars)) };\r\n    case 'earMaterial':return { sold: isSold(skuWith({ earMaterial: v }, skuEars)) };\r\n    case 'cheek':      return { sold: isSold(v === 'lit'\r\n                         ? skuWith({ cheekMode: 'bubble', logoImage: 'lit' }, skuBubbleCheek)\r\n                         : skuWith({ cheekMode: v }, skuBubbleCheek)) };\r\n  }\r\n  return {};\r\n}\r\nfunction tagOpt(b, text, cls) {\r\n  const tag = document.createElement('span');\r\n  tag.className = cls; tag.textContent = text;\r\n  b.appendChild(tag);\r\n}\r\n// the cart lines this build WANTS \u2014 shared by Add to Cart and the sold-out check\r\nfunction cartSpecs() {\r\n  const cm = effectiveCheekMode();\r\n  const wantCheek = cm === 'neopixel' || (cm === 'bubble' && (!isLegacy(state.style) || !!state.logoImage));\r\n  return [\r\n    { label: visorOnly() ? 'Visor' : 'Head', want: true, sku: skuHead() },\r\n    { label: neoActive() ? 'NeoPixel cheeks' : 'Bubble cheeks', want: wantCheek, sku: skuBubbleCheek() },\r\n    { label: 'Ears', want: !!hasEars(), sku: hasEars() ? skuEars() : null },\r\n    { label: 'Secondary ears', want: !!hasEar2(), sku: hasEar2() ? skuEar2() : null },\r\n    { label: 'Custom colors', want: customColorsActive(), sku: customColorsActive() ? skuColorAddon() : null },\r\n  ];\r\n}\r\nconst soldLines = () => cartSpecs().filter(s => s.want && isSold(s.sku));\r\n\r\n// \u2550\u2550 UI \u2550\u2550\r\nfunction optButton(wrap, label, on, off, cb) {\r\n  const b = document.createElement('button');\r\n  b.className = 'cfg-opt' + (on ? ' on' : '') + (off ? ' off' : '');\r\n  b.textContent = label;\r\n  if (!off) b.onclick = cb;\r\n  wrap.appendChild(b);\r\n  return b;\r\n}\r\n// an option button that knows its own SOON / SOLD OUT state. Sold out always\r\n// disables; SOON disables only where asked (draft heads stay explorable as a\r\n// teaser, draft accessories can't be picked).\r\nfunction statusButton(wrap, label, on, kind, v, cb, { soonOff = false, off = false } = {}) {\r\n  const s = optionStatus(kind, v);\r\n  const b = optButton(wrap, label, on, off || !!s.sold || (soonOff && !!s.soon), cb);\r\n  if (s.sold) tagOpt(b, 'SOLD OUT', 'sold-tag');\r\n  else if (s.soon) tagOpt(b, 'SOON', 'soon-tag');\r\n  return b;\r\n}\r\n\r\nfunction renderUI() {\r\n  const C = document.getElementById('opt-collection'); C.innerHTML = '';\r\n  for (const [key, c] of Object.entries(COLLECTIONS)) {\r\n    const unavailable = collectionStyles(key).length === 0;\r\n    statusButton(C, c.label, key === state.collection, 'collection', key,\r\n      () => { state.collection = key; applyState(); }, { off: unavailable });\r\n  }\r\n  document.getElementById('note-collection').textContent =\r\n    collectionStyles(state.collection).length\r\n      ? COLLECTIONS[state.collection].note\r\n      : 'Coming soon.';\r\n\r\n  const styles = collectionStyles(state.collection);\r\n  const S = document.getElementById('opt-style'); S.innerHTML = '';\r\n  for (const s of styles) {   // draft product \u2192 SOON teaser tag; sold out \u2192 disabled\r\n    const b = statusButton(S, s.toUpperCase(), s === state.style, 'style', s,\r\n      () => { state.style = s; applyState(); });\r\n    const sil = shapeSil(s, 0.2, 'st-sil');   // the same side outline guided shows\r\n    if (sil) b.prepend(sil);\r\n  }\r\n\r\n  // COMING SOON teaser: draft head products stay explorable but can't be bought\r\n  const live = headLive();\r\n  const soonBox = document.getElementById('cfg-soon');\r\n  const addBtn = document.getElementById('cfg-add');\r\n  soonBox.style.display = live ? 'none' : 'block';\r\n  addBtn.classList.toggle('soon', !live);\r\n  addBtn.disabled = !live;\r\n  addBtn.textContent = live ? 'Add to Cart' : '\uD83D\uDD1C Coming Soon';\r\n  if (!live) {   // teaser text is JS-injected only when a draft product is truly selected\r\n    document.getElementById('cfg-soon-big').textContent = '\uD83D\uDD1C Coming Soon';\r\n    document.getElementById('cfg-soon-note').textContent =\r\n      `The ${state.style.toUpperCase()} ${COLLECTIONS[state.collection].label} head is almost here \u2014 ` +\r\n      'this preview is a sneak peek. Save your build and check back!';\r\n  }\r\n  // SOLD OUT: a build that lands on a sold-out variant can't be added\r\n  const sold = live ? soldLines() : [];\r\n  if (sold.length) {\r\n    soonBox.style.display = 'block';\r\n    addBtn.disabled = true;\r\n    addBtn.textContent = 'Sold Out';\r\n    document.getElementById('cfg-soon-big').textContent = 'Sold out';\r\n    document.getElementById('cfg-soon-note').textContent =\r\n      `${sold.map(s => s.label).join(' and ')} ${sold.length > 1 ? 'are' : 'is'} sold out in this ` +\r\n      'combination right now. Pick another option to keep going.';\r\n  }\r\n  // Etsy submission mode: the CTA submits ONLY the cheek art (no head)\r\n  document.getElementById('etsy-row').style.display = ART_SUBMIT_MODE ? 'block' : 'none';\r\n  if (ART_SUBMIT_MODE) {\r\n    soonBox.style.display = 'none';\r\n    addBtn.classList.remove('soon');\r\n    addBtn.disabled = false;\r\n    addBtn.textContent = '\uD83C\uDFA8 Submit Cheek Art \u2014 Free';\r\n  }\r\n  // Cheek-Studio popup mode: the CTA hands the art back to the product page\r\n  if (CHEEKART_MODE) {\r\n    soonBox.style.display = 'none';\r\n    addBtn.classList.remove('soon');\r\n    addBtn.disabled = false;\r\n    addBtn.textContent = '\u2713 Use This Design';\r\n  }\r\n\r\n  const vOnly = visorOnly();\r\n  const shapes = shapesFor(state.style);\r\n  // one visor covers both mk3 shapes \u2014 the shape picker is a head-shell choice\r\n  document.getElementById('sec-shape').style.display = (shapes.length && !vOnly) ? '' : 'none';\r\n  const SH = document.getElementById('opt-shape'); SH.innerHTML = '';\r\n  for (const sh of shapes) {\r\n    const b = statusButton(SH, sh.replace(/_/g, ' '), sh === state.shape, 'shape', sh,\r\n      () => { state.shape = sh; applyState(); });\r\n    const sil = shapeSil(`${state.style}_${sh}`, 0.2, 'st-sil');\r\n    if (sil) b.prepend(sil);\r\n  }\r\n\r\n  const dAllowed = COLLECTIONS[state.collection].displays;\r\n  document.getElementById('sec-display').style.display = dAllowed.length ? '' : 'none';\r\n  const D = document.getElementById('opt-display'); D.innerHTML = '';\r\n  for (const d of dAllowed)\r\n    statusButton(D, DISPLAY_LABEL[d] || d, d === state.display, 'display', d,\r\n      () => { state.display = d; applyState(); }, { off: !displayAvailable(state.style, d) });\r\n  document.getElementById('note-display').textContent =\r\n    state.display === 'MAX7219' ? 'Classic single-color matrix display.'\r\n    : state.display === 'NeoPixel' ? 'Full-color addressable modules.'\r\n    : state.display === 'HUB75' ? 'Dual full-color high-density panels.' : '';\r\n\r\n  // MAX7219 is a single-color display \u2014 the customer picks the LED color\r\n  document.getElementById('sec-maxcolor').style.display = state.display === 'MAX7219' ? '' : 'none';\r\n  const MC = document.getElementById('opt-maxcolor'); MC.innerHTML = '';\r\n  if (state.display === 'MAX7219')\r\n    for (const [code, name] of MAX_COLORS)\r\n      statusButton(MC, name, code === state.maxColor, 'maxColor', code,\r\n        () => { state.maxColor = code; renderUI(); });\r\n\r\n  // Face component picker \u2014 MAX7219/NeoPixel builds pick their default face\r\n  // from the firmware's preset library (eyes/nose/mouth mix freely)\r\n  const partsOn = (state.display === 'MAX7219' || state.display === 'NeoPixel') && FACES;\r\n  document.getElementById('sec-faceparts').style.display = partsOn ? '' : 'none';\r\n  if (partsOn) {\r\n    const P = FACES.presets;\r\n    // draw 1 canvas pixel per grid cell \u2014 the CSS size + image-rendering:pixelated\r\n    // does the upscale uniformly (per-dot rounding at fractional scales jumbles\r\n    // the art, which is what happened to the 80px mouth in a 120px canvas)\r\n    const draw = (id, grid) => {\r\n      const cv = document.getElementById(id);\r\n      if (cv.width !== grid[0].length || cv.height !== grid.length) {\r\n        cv.width = grid[0].length; cv.height = grid.length;\r\n      }\r\n      const c = cv.getContext('2d');\r\n      c.fillStyle = '#000'; c.fillRect(0, 0, cv.width, cv.height);\r\n      c.fillStyle = state.display === 'MAX7219' ? (MAX_HEX[state.maxColor] || '#fff') : '#2EECC5';\r\n      for (let y = 0; y < grid.length; y++) for (let x = 0; x < grid[0].length; x++)\r\n        if (grid[y][x]) c.fillRect(x, y, 1, 1);\r\n    };\r\n    const norm = (v, n) => (((v | 0) % n) + n) % n;\r\n    // the mouth chip must match what the head SHOWS: the art is stored per-tile\r\n    // flipped for the physical side mounts, so flip the side tiles (cols 0-32 /\r\n    // 48-80) exactly like renderFirmwareFace does; the centre 16px stays raw\r\n    const mouthAsShown = (g) => {\r\n      const L = orientStrip(slice(g, 0, 32)), M = slice(g, 32, 48), R = orientStrip(slice(g, 48, 80));\r\n      return g.map((_, y) => [...L[y], ...M[y], ...R[y]]);\r\n    };\r\n    draw('fp-eyes',  facePick(P.eyes,  state.faceEyes).leftEye);\r\n    draw('fp-nose',  facePick(P.noses, state.faceNose).grid);\r\n    draw('fp-mouth', mouthAsShown(facePick(P.mouths, state.faceMouth).grid));\r\n    document.getElementById('fp-eyes-n').textContent  = `${norm(state.faceEyes,  P.eyes.length)  + 1} / ${P.eyes.length}`;\r\n    document.getElementById('fp-nose-n').textContent  = `${norm(state.faceNose,  P.noses.length) + 1} / ${P.noses.length}`;\r\n    document.getElementById('fp-mouth-n').textContent = `${norm(state.faceMouth, P.mouths.length)+ 1} / ${P.mouths.length}`;\r\n  }\r\n\r\n  // Face Effect \u2014 only PF 3.0 displays (Studio-HUB75 + D-Lux NeoPixel) can do\r\n  // full-colour gradients/effects\r\n  const faceOn = faceEffect();\r\n  document.getElementById('sec-face').style.display = faceOn ? '' : 'none';\r\n  const FE = document.getElementById('opt-face'); FE.innerHTML = '';\r\n  if (faceOn) {\r\n    for (const e of FACE_EFFECTS)\r\n      optButton(FE, e, e === (state.facePreset || 'solid'), false,\r\n        () => { state.facePreset = e; renderUI(); });\r\n    document.getElementById('note-face').textContent =\r\n      'Powered by the ProtoFrenz 3.0 controller \u2014 gradients, effects and draw-your-own faces.';\r\n  }\r\n\r\n  // Face Maker upload \u2014 HUB75 Starter (WF1) shows a single static image\r\n  const starterFace = state.display === 'HUB75' && state.collection === 'starter';\r\n  document.getElementById('sec-facemaker').style.display = starterFace ? '' : 'none';\r\n  if (starterFace) {\r\n    document.getElementById('facemaker-link').href = FACE_MAKER_URL;\r\n    document.getElementById('note-facemaker').textContent = faceBitmap\r\n      ? 'The WF1 controller displays one static image. Upload a new one to replace it.'\r\n      : 'The Starter WF1 controller displays one static image. Design it in the Face Maker, then upload it here to preview.';\r\n  }\r\n\r\n  // legacy heads: no modular fins / secondary ears. Cheeks are never a choice\r\n  // (studs/vents come with the fin). Primary ears show for everyone.\r\n  const legacy = isLegacy(state.style);\r\n  for (const id of ['sec-fin', 'sec-ear2'])\r\n    document.getElementById(id).style.display = (legacy || vOnly) ? 'none' : '';\r\n  // visor-only: a bare visor takes no ears/cheeks and the fit checker is moot\r\n  document.getElementById('sec-ear').style.display = vOnly ? 'none' : '';\r\n  document.getElementById('sec-fit').style.display = vOnly ? 'none' : '';\r\n  document.getElementById('sec-finish').style.display = vOnly ? 'none' : '';\r\n  // modern heads: preview the head without its visor+gasket (ships with both);\r\n  // legacy visors don't detach with a gasket, so the toggle is modern-only\r\n  document.getElementById('sec-visor').style.display = (!legacy && !vOnly) ? '' : 'none';\r\n  if (!legacy && !vOnly) {\r\n    const V = document.getElementById('opt-visor'); V.innerHTML = '';\r\n    optButton(V, 'Show', !state.visorOff, false,\r\n      () => { state.visorOff = false; applyState(); });\r\n    optButton(V, 'Hide', state.visorOff, false,\r\n      () => { state.visorOff = true; applyState(); });\r\n    document.getElementById('note-visor').textContent = state.visorOff\r\n      ? 'Preview only \u2014 every head ships with its Visor + Magnetized Frame. Need a spare? See the Visor only collection.'\r\n      : 'The tinted Visor + Magnetized Frame. Hide it here to peek inside.';\r\n  }\r\n\r\n  // Cheeks: modern picks Standard (studs/vents) vs Bubble; legacy always has\r\n  // bubble cheeks. When bubble is on, a logo can be uploaded.\r\n  document.getElementById('sec-cheek').style.display = vOnly ? 'none' : '';\r\n  const CK = document.getElementById('opt-cheek'); CK.innerHTML = '';\r\n  const noteCheek = document.getElementById('cheek-note');\r\n  const em = effectiveCheekMode();\r\n  const setMode = (m) => { state.cheekMode = m; applyState(); };\r\n  // modern offers the studs (vents on the wild fin) that come with its fin;\r\n  // legacy always ships a bubble. Lit logo = that bubble with art on it (LIT\r\n  // SKU): picking it opens the Logo Studio, and Bubble cheeks takes the art off.\r\n  // NeoPixel rings appear only where a PF3 controller drives them.\r\n  if (!legacy) optButton(CK, { CHEEK_STUD: 'Studs', CHEEK_VENT: 'Vents' }[FIN_CHEEK[state.fin]] || 'Standard',\r\n    em === 'standard', false, () => setMode('standard'));\r\n  statusButton(CK, 'Bubble cheeks', em === 'bubble' && !state.logoImage, 'cheek', 'bubble',\r\n    () => { clearCheekArt(); setMode('bubble'); });\r\n  statusButton(CK, 'Lit logo', em === 'bubble' && !!state.logoImage, 'cheek', 'lit',\r\n    () => { setMode('bubble'); LS.show(); });\r\n  if (neoAvailable()) statusButton(CK, 'NeoPixel rings', em === 'neopixel', 'cheek', 'neopixel', () => setMode('neopixel'));\r\n  // guided mode keeps controller names out of the way (they live under\r\n  // Technical details there)\r\n  noteCheek.textContent = neoActive()\r\n    ? (G.on ? 'A full-color ring of 12 LEDs glows behind the tinted bubble. Design cheek art in the Logo Studio \u2014 the light areas glow, black blocks the light.'\r\n            : 'A full-color ring of 12 LEDs glows behind the tinted bubble, driven by your PF3 controller. Design cheek art in the Logo Studio \u2014 the light areas glow, black blocks the light.')\r\n    : legacy ? 'Bubble cheeks are included with MK3 / MK5.'\r\n    : !neoAvailable() && state.collection !== 'baseonly'\r\n      ? (G.on ? 'NeoPixel light-up cheeks come with Studio and D-LUX.'\r\n              : 'NeoPixel cheeks need a ProtoFrenz 3.0 controller (Studio / D-Lux / Legacy Rewired).') : '';\r\n  // the cheek designer is offered on bubble AND NeoPixel cheeks (white on NeoPixel)\r\n  document.getElementById('cheek-upload').style.display = (em === 'bubble' || em === 'neopixel') ? 'block' : 'none';\r\n  // second-cheek design mode \u2014 only meaningful once art is added\r\n  document.getElementById('cheek-mirror-row').style.display = hasCheekDesign() ? 'block' : 'none';\r\n  if (hasCheekDesign()) {\r\n    const LM = document.getElementById('opt-logo-mirror'); LM.innerHTML = '';\r\n    optButton(LM, 'Mirrored', state.logoMirror, false,\r\n      () => { state.logoMirror = true; rebuildLogoDecals(); renderUI(); });\r\n    optButton(LM, 'Same', !state.logoMirror, false,\r\n      () => { state.logoMirror = false; rebuildLogoDecals(); renderUI(); });\r\n    document.getElementById('note-logo-mirror').textContent = state.logoMirror\r\n      ? 'A true reflection \u2014 an arrow points toward the face on both cheeks.'\r\n      : 'Identical on both cheeks \u2014 best when the logo includes text.';\r\n  }\r\n\r\n  // fins are NOT optional: every modern head ships with one (no fin-less SKU)\r\n  const cats = [['ear', 'opt-ear', true], ['ear2', 'opt-ear2', true], ['fin', 'opt-fin', false]];\r\n  for (const [cat, elId, optional] of cats) {\r\n    const el = document.getElementById(elId); el.innerHTML = '';\r\n    if (optional) optButton(el, 'none', (state[cat] ?? 'none') === 'none', false,\r\n      () => { state[cat] = 'none'; applyState(); });\r\n    // ears alphabetised, ONE button per ear (the Size switch picks Full/Small);\r\n    // fins keep author order\r\n    let list = partsByCat(cat);\r\n    if (cat === 'ear') list = list.filter(n => !/_small$/i.test(n));\r\n    if (cat === 'ear' || cat === 'ear2')\r\n      list = list.slice().sort((a, b) => pretty(a).localeCompare(pretty(b)));\r\n    for (const n of list) {\r\n      if (NOT_SELLABLE.has(n)) continue;   // no Shopify product yet\r\n      // draft accessory product \u2192 visible but disabled, tagged SOON\r\n      const on = cat === 'ear'\r\n        ? (hasEars() && state.ear.replace(/_small$/i, '') === n) : state[cat] === n;\r\n      statusButton(el, (cat === 'fin' && FIN_LABEL[n]) || pretty(n), on, cat, n, () => {\r\n        if (cat === 'ear') {\r\n          const small = earSmallPref ?? /_small$/i.test(state.ear || '');\r\n          state.ear = small && manifest.parts[n + '_small'] ? n + '_small' : n;\r\n        } else state[cat] = n;\r\n        applyState();\r\n      }, { soonOff: true });\r\n    }\r\n  }\r\n\r\n  // ear material (PLA / TPU) \u2014 drives the ER-<code>-<mat><size> SKU; only\r\n  // relevant when ears are actually on the head\r\n  syncLedUI();\r\n  document.getElementById('ear-material-row').style.display = (hasEars() || hasEar2()) ? '' : 'none';\r\n  // ear size: Full / Small (Small only where that ear has a small part \u2014 bunny doesn't)\r\n  document.getElementById('ear-size-row').style.display = hasEars() ? '' : 'none';\r\n  const ES = document.getElementById('opt-ear-size'); ES.innerHTML = '';\r\n  if (hasEars()) {\r\n    const base = state.ear.replace(/_small$/i, ''), small = /_small$/i.test(state.ear);\r\n    const hasSmall = !!manifest.parts[base + '_small'];\r\n    statusButton(ES, 'Full', !small, 'earSize', base,\r\n      () => { earSmallPref = false; state.ear = base; applyState(); });\r\n    const sb = statusButton(ES, 'Small', small, 'earSize', base + '_small',\r\n      () => { earSmallPref = true; state.ear = base + '_small'; applyState(); }, { off: !hasSmall });\r\n    if (!hasSmall) sb.title = `${pretty(base)} ears come in one size`;\r\n  }\r\n  const EM = document.getElementById('opt-ear-material'); EM.innerHTML = '';\r\n  for (const mat of ['TPU', 'PLA'])\r\n    statusButton(EM, EAR_MATERIAL_LABEL[mat], mat === state.earMaterial, 'earMaterial', mat,\r\n      () => { state.earMaterial = mat; renderUI(); });\r\n\r\n  // adjust steppers appear only when snap tables exist (and never on\r\n  // fixed-angle parts like the untabbed fins)\r\n  for (const cat of ['ear', 'ear2', 'fin']) {\r\n    const snaps = snapsFor(cat);\r\n    const row = document.getElementById(`adj-${cat}`);\r\n    const fixed = manifest.adjust?.[cat]?.exclude?.includes(state[cat]);\r\n    const show = snaps?.length > 1 && (state[cat] ?? 'none') !== 'none' && !fixed;\r\n    row.style.display = show ? 'flex' : 'none';\r\n    if (show) {\r\n      const idx = state[cat + 'Snap'];\r\n      document.getElementById(`adj-${cat}-val`).textContent =\r\n        cat === 'fin' ? `${snaps[idx][0]}\u00B0` : `${idx + 1} of ${snaps.length}`;\r\n    }\r\n  }\r\n\r\n  renderBooth();\r\n  updatePrice();\r\n  renderGuided();     // guided steps sit over these same sections\r\n  padPanelForCTA();   // CTA height may have changed (price line, teaser, buttons)\r\n}\r\n\r\n// \u2550\u2550 Color Booth UI \u2014 one swatch row per zone present on the current build \u2550\u2550\r\nlet boothOpen = false;\r\nfunction renderBooth() {\r\n  const sec = document.getElementById('sec-finish');\r\n  const booth = document.getElementById('booth');\r\n  const toggle = document.getElementById('booth-toggle');\r\n  if (visorOnly()) { sec.style.display = 'none'; return; }\r\n  sec.style.display = '';\r\n  if (!COLOR_BOOTH_ON) {   // stock look only until the booth launches\r\n    sec.querySelector('h2').textContent = 'Finish';\r\n    document.getElementById('finish-note').textContent = 'Printed in black PLA+ with white ears \u2014 the ProtoFrenz look.';\r\n    toggle.style.display = 'none'; booth.style.display = 'none';\r\n    return;\r\n  }\r\n  const live = colorBoothLive();\r\n  toggle.textContent = boothOpen ? '\uD83C\uDFA8 Close the Color Booth' : '\uD83C\uDFA8 Open the Color Booth';\r\n  toggle.onclick = () => { boothOpen = !boothOpen; renderBooth(); };\r\n  booth.style.display = boothOpen ? '' : 'none';\r\n  if (!boothOpen) return;\r\n\r\n  const rows = document.getElementById('booth-rows'); rows.innerHTML = '';\r\n  for (const [zone, meta] of Object.entries(COLOR_ZONES)) {\r\n    if (!zoneOnBuild(zone)) continue;   // zone's region not on this build\r\n    const row = document.createElement('div'); row.className = 'booth-row';\r\n    const lab = document.createElement('div'); lab.className = 'booth-lab';\r\n    lab.innerHTML = `<span>${meta.label}</span><b>${zoneColor(zone).label}</b>`;\r\n    row.appendChild(lab);\r\n    const sw = document.createElement('div'); sw.className = 'booth-sw';\r\n    for (const c of zonePalette(zone)) {\r\n      const dot = document.createElement('i');\r\n      const disabled = !live && c.id !== meta.def;   // product not live: stock only\r\n      dot.className = (c.id === zoneColor(zone).id ? 'on' : '') +\r\n                      (disabled ? ' off' : '');\r\n      dot.style.background = c.hex;\r\n      dot.title = c.label;\r\n      if (!disabled) dot.onclick = () => {\r\n        if (c.id === meta.def) delete state.colors[zone];\r\n        else state.colors[zone] = c.id;\r\n        applyColors(); renderBooth(); updatePrice();\r\n      };\r\n      sw.appendChild(dot);\r\n    }\r\n    row.appendChild(sw);\r\n    rows.appendChild(row);\r\n  }\r\n  document.getElementById('booth-reset').onclick = () => {\r\n    state.colors = {}; applyColors(); renderBooth(); updatePrice();\r\n  };\r\n  const note = document.getElementById('booth-note');\r\n  if (!live) {\r\n    note.textContent = 'Custom colors aren\u2019t available for this head style just yet \u2014 every ' +\r\n      'build ships in the stock scheme for now.';\r\n  } else if (customColorsActive()) {\r\n    const sku = skuColorAddon(), cents = sku && window.PF_SKU2PRICE?.[sku];\r\n    note.textContent = `Your scheme: ${colorSummaryText()}. ` +\r\n      `${COLOR_TIER_LABEL[colorTier()]} customization` +\r\n      (cents != null ? ` adds $${(cents / 100).toFixed(0)} to the build.` : '.');\r\n  } else {\r\n    const p = (t) => { const c = window.PF_SKU2PRICE?.[`AC-COLOR-${t}-${AC_COLOR_STYLE[state.style]}`];\r\n                       return c != null ? `$${(c / 100).toFixed(0)}` : null; };\r\n    const p1 = p('1'), p2 = p('2'), pb = p('BRIEF');\r\n    note.textContent = 'Pick a color for any part \u2014 swatches are the filament colors we stock, ' +\r\n      'so what you see is what we print.' +\r\n      (p1 && p2 && pb ? ` One color ${p1} \u00B7 two colors ${p2} \u00B7 three or more ${pb}.` : '');\r\n  }\r\n}\r\n\r\ndocument.querySelectorAll('.cfg-step').forEach(b => b.onclick = () => {\r\n  // face preset steppers: wrap around, live face redraw only (no geometry change)\r\n  if (b.dataset.fp) {\r\n    const k = b.dataset.fp;\r\n    state[k] = (state[k] | 0) + Number(b.dataset.d);\r\n    renderUI();\r\n    return;\r\n  }\r\n  const cat = b.dataset.adj, d = Number(b.dataset.d);\r\n  const snaps = snapsFor(cat);\r\n  if (!snaps) return;\r\n  const k = cat + 'Snap';\r\n  state[k] = Math.min(snaps.length - 1, Math.max(0, state[k] + d));\r\n  applyState();\r\n});\r\n\r\n// \u2550\u2550 Save / share a build \u2550\u2550 the config encodes into a URL the customer can\r\n// bookmark or send; opening it restores the build. (The uploaded logo image\r\n// can't ride a URL, so a saved lit-cheek build restores everything but the\r\n// logo, which is re-uploaded.)\r\nconst BUILD_KEYS = ['collection', 'style', 'shape', 'display', 'maxColor', 'facePreset',\r\n  'faceEyes', 'faceNose', 'faceMouth',\r\n  'fin', 'finSnap', 'ear', 'earSnap', 'earMaterial', 'ear2', 'ear2Snap', 'cheekMode', 'ringStyle', 'ledColor', 'npBacking', 'visorOff', 'colors'];\r\nfunction encodeBuild() {\r\n  const b = {}; for (const k of BUILD_KEYS) b[k] = state[k];\r\n  return btoa(unescape(encodeURIComponent(JSON.stringify(b))))\r\n    .replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n}\r\nfunction decodeBuild(s) {\r\n  try {\r\n    return JSON.parse(decodeURIComponent(escape(atob(\r\n      String(s).replace(/-/g, '+').replace(/_/g, '/')))));\r\n  } catch { return null; }\r\n}\r\nfunction applyBuild(b) {   // set known fields then let applyState gate/correct\r\n  if (!b) return false;\r\n  // back-compat: pre-NeoPixel links carried a cheekBubble bool\r\n  if (!('cheekMode' in b) && ('cheekBubble' in b)) b.cheekMode = b.cheekBubble ? 'bubble' : 'standard';\r\n  for (const k of BUILD_KEYS) if (k in b) state[k] = b[k];\r\n  state.ledColor = cheekLedName(state.ledColor);\r\n  state.npBacking = npBackingKey(state.npBacking);\r\n  state.ringStyle = 'dots';   // the halo ring look was retired (Blair 2026-10-05)\r\n  // colors must be a plain object (shared links carry arbitrary JSON); junk\r\n  // ids inside it are ignored by cleanColors/zoneColor\r\n  if (!state.colors || typeof state.colors !== 'object' || Array.isArray(state.colors)) state.colors = {};\r\n  return true;\r\n}\r\nfunction buildLink() {\r\n  return location.origin + location.pathname + '?build=' + encodeBuild();\r\n}\r\n\r\n// \u2550\u2550 Cart \u2014 \"add to cart\": ONE complete-head SKU + a separate ear line item \u2550\u2550\r\nfunction buildCartItems() {\r\n  const cfgId = 'PF-' + Date.now().toString(36).toUpperCase();\r\n  const finName = state.fin && state.fin !== 'none' ? pretty(state.fin) : 'none';\r\n  const summary = visorOnly() ? {\r\n    'Collection': COLLECTIONS[state.collection].label,\r\n    'Style': state.style.toUpperCase(),\r\n    '_config': JSON.stringify(state),\r\n  } : {\r\n    'Collection': COLLECTIONS[state.collection].label,\r\n    'Style': state.style.toUpperCase(),\r\n    ...(state.shape ? { 'Shape': state.shape } : {}),\r\n    'Display': state.display ? (DISPLAY_LABEL[state.display] || state.display) : 'none',\r\n    ...(state.display === 'MAX7219' ? { 'LED color': (MAX_COLORS.find(c => c[0] === state.maxColor) || [, ''])[1] } : {}),\r\n    'Finish': 'black / white ears',\r\n    'Fin': isLegacy(state.style) ? 'n/a' : finName,\r\n    'Cheeks': neoActive() ? `NeoPixel ring${state.logoImage ? ` \u2014 white art (${state.logoImage})` : ''}`\r\n      : bubbleActive() ? (hasLogo() ? `lit bubble \u2014 logo (${state.logoImage})` : 'blank bubble')\r\n      : ({ CHEEK_STUD: 'studs', CHEEK_VENT: 'vents' }[FIN_CHEEK[state.fin]] || 'standard'),\r\n    'Ears': hasEars() ? `${pretty(state.ear)} (${state.earMaterial})` : 'none',\r\n    ...(hasEar2() ? { 'Secondary ears': `${pretty(state.ear2)} (${state.earMaterial})` } : {}),\r\n    ...(customColorsActive()\r\n      ? { 'Custom colors': `${COLOR_TIER_LABEL[colorTier()]} \u2014 ${colorSummaryText()}` } : {}),\r\n    '_config': JSON.stringify(state),\r\n  };\r\n\r\n  // head + (modern-only) bubble cheeks + primary/secondary ears + custom\r\n  // colors are separate lines\r\n  // `want` = the build includes this line. A wanted line whose SKU can't be\r\n  // built must BLOCK the add \u2014 skipping it silently ships an order whose\r\n  // summary promises parts that were never billed (the missing-ears reports).\r\n  const items = [], unmapped = [];\r\n  let first = true;\r\n  for (const s of cartSpecs()) {\r\n    if (!s.sku) { if (s.want) unmapped.push(`${s.label}: (no SKU for this build)`); continue; }\r\n    const id = resolveVariantId(s.sku);\r\n    const properties = { '_config_id': cfgId, ...(first ? summary : {}) };\r\n    if (id) { items.push({ id, quantity: 1, properties, _sku: s.sku }); first = false; }\r\n    else unmapped.push(`${s.label}: ${s.sku}`);\r\n  }\r\n  return { cfgId, items, unmapped };\r\n}\r\n\r\nfunction hasEars() { return !visorOnly() && state.ear && state.ear !== 'none'; }\r\nfunction hasEar2() { return !visorOnly() && state.ear2 && state.ear2 !== 'none' && !isLegacy(state.style); }\r\n\r\n// the raw SKU codes this build would add, independent of id resolution (preview)\r\nfunction buildCartSkus() {\r\n  const head = skuHead();\r\n  const out = [`${visorOnly() ? 'Visor' : 'Head'}: ${head || '(no SKU for this build)'}`];\r\n  const bc = skuBubbleCheek();\r\n  if (bc) out.push(`${neoActive() ? 'NeoPixel cheeks' : 'Bubble cheeks'}: ${bc}`);\r\n  if (hasEars()) out.push(`Ears: ${skuEars() || '(no ear SKU)'}`);\r\n  if (hasEar2()) out.push(`Secondary ears: ${skuEar2() || '(no ear2 SKU)'}`);\r\n  if (customColorsActive())\r\n    out.push(`Custom colors: ${skuColorAddon() || '(no color SKU)'} \u2014 ${colorSummaryText()}`);\r\n  return out;\r\n}\r\n\r\n// Dev payload view \u2014 LAZILY built, and only in preview mode, so the production\r\n// page never contains dev-tooling markup (crawlers read hidden DOM). In shopify\r\n// mode an unmapped build gets a plain-language alert instead of raw JSON.\r\nfunction showDevPayload(obj) {\r\n  if (CART_MODE === 'shopify') {\r\n    const what = [...new Set((obj.unmapped || []).map(u => String(u).split(':')[0]))].join(', ');\r\n    alert('Some of the selected options aren\u2019t available to order just yet' +\r\n      (what ? ` (${what})` : '') + ' \u2014 please adjust your build, or contact us and we\u2019ll sort it out.');\r\n    return;\r\n  }\r\n  let modal = document.getElementById('cfg-modal');\r\n  if (!modal) {\r\n    modal = document.createElement('div');\r\n    modal.id = 'cfg-modal';\r\n    modal.innerHTML = '<div class=\"box\"><h3>Cart payload (dev preview)</h3>' +\r\n      '<div class=\"cfg-note\">CART_MODE is \\'' + CART_MODE + '\\' \u2014 on Shopify this POSTs to ' +\r\n      '/cart/add.js with real variant ids.</div><pre id=\"cfg-payload\"></pre>' +\r\n      '<button class=\"cfg-opt\" id=\"cfg-modal-close\">Close</button></div>';\r\n    document.body.appendChild(modal);\r\n    modal.querySelector('#cfg-modal-close').onclick = () => modal.classList.remove('show');\r\n  }\r\n  modal.querySelector('#cfg-payload').textContent = JSON.stringify(obj, null, 2);\r\n  modal.classList.add('show');\r\n}\r\n\r\n// ops \"Edit\" (&edit=order): say 'ready' to the opener until it answers with the order's art, then load that as the\r\n// source image. Only the opener, only from the allowlisted origin, only an image data URL, only once.\r\nfunction cheekartAwaitOrderArt() {\r\n  let got = false, tries = 0;\r\n  const ping = () => {\r\n    if (got || tries++ >= 10 || !window.opener) return;\r\n    try { window.opener.postMessage({ source: 'pf-cheek-studio', type: 'ready' }, CHEEKART_TARGET); } catch (e) {}\r\n    setTimeout(ping, 1500);\r\n  };\r\n  window.addEventListener('message', (e) => {\r\n    if (got || e.origin !== CHEEKART_TARGET || e.source !== window.opener) return;\r\n    const d = e.data || {};\r\n    if (d.source !== 'pf-ops' || d.type !== 'load' || typeof d.dataUrl !== 'string' || !/^data:image\\/(png|jpeg|webp|gif);base64,/.test(d.dataUrl)) return;\r\n    got = true;\r\n    const pm = ((d.placement || '') + '').toLowerCase();\r\n    if (typeof d.ledColor === 'string') state.ledColor = cheekLedName(d.ledColor);\r\n    if (typeof d.backing === 'string') state.npBacking = npBackingKey(d.backing);\r\n    if (/same art/.test(pm)) state.logoMirror = false; else if (/mirror/.test(pm)) state.logoMirror = true;\r\n    LS.loadImage(d.dataUrl);\r\n    if (typeof d.maskDataUrl === 'string' && /^data:image\\/png;base64,/.test(d.maskDataUrl) && LS.maskLayer) {\r\n      const mi = new Image();\r\n      mi.onload = () => { const mc = LS.maskLayer.getContext('2d'); mc.clearRect(0, 0, LS.S, LS.S); mc.drawImage(mi, 0, 0, LS.S, LS.S); LS.render(); };\r\n      mi.src = d.maskDataUrl;\r\n    }\r\n    renderUI();\r\n  });\r\n  ping();\r\n}\r\n\r\n// \u2550\u2550 Production ticket (TIX-2) \u2550\u2550 open complete-head orders are capped (one\r\n// shared queue, D9): a capped head goes to the cart WITH the $0 production\r\n// ticket, one per ORDER, added first. A full queue opens the waitlist instead\r\n// and nothing is added. Head bases, visors and accessories need no ticket.\r\n// This reuses the THEME's helper (snippets/pf-build-slot.liquid renders it on\r\n// every theme page, this one included) so the configurator and the product\r\n// pages behave and word it identically. The helper only exists once the\r\n// ticket product is live: until then Add to Cart is exactly as before.\r\nconst ticketApi = () => (window.pfBuildSlot && typeof window.pfBuildSlot.ensureTicket === 'function')\r\n  ? window.pfBuildSlot : null;\r\n// capped SKU prefixes come from the theme (PF_BUILD_SLOT.prefixes) so the two\r\n// lists can't drift\r\nfunction cappedSku(sku) {\r\n  const pre = (window.PF_BUILD_SLOT && window.PF_BUILD_SLOT.prefixes) || [];\r\n  return !!sku && pre.some(p => p && sku.startsWith(p));\r\n}\r\n// undo this click's lines (all-or-nothing); true when the cart is back as it was\r\nasync function removeCartLines(keys) {\r\n  if (!keys.length) return true;\r\n  try {\r\n    const r = await fetch('/cart/update.js', { method: 'POST',\r\n      headers: { 'Content-Type': 'application/json' },\r\n      body: JSON.stringify({ updates: Object.fromEntries(keys.map(k => [k, 0])) }) });\r\n    return r.ok;\r\n  } catch { return false; }\r\n}\r\n\r\ndocument.getElementById('cfg-add').onclick = async () => {\r\n  // \u2500\u2500 Cheek-Studio popup: hand the print-ready PNG back to the product page \u2500\u2500\r\n  if (CHEEKART_MODE) {\r\n    const btn = document.getElementById('cfg-add');\r\n    if (!hasCheekDesign() || !logoFile) {\r\n      alert('Design your cheek art first \u2014 open the \uD83C\uDFA8 Logo Studio (or upload a logo).'); return;\r\n    }\r\n    const readUrl = (file) => new Promise((res) => { const r = new FileReader(); r.onload = () => res(r.result); r.onerror = () => res(''); r.readAsDataURL(file); });\r\n    const fr = { result: '' };\r\n    const send = () => {\r\n      const msg = {\r\n        source: 'pf-cheek-studio', type: 'art', dataUrl: fr.result,\r\n        placement: state.logoMirror ? 'mirrored \u2014 reflected on the second cheek' : 'same art on both cheeks',\r\n        plate: CHEEKART_PLATE || '',\r\n        ...(ledLitPossible() ? { ledColor: cheekLedName(state.ledColor) } : {}),\r\n        ...(neoActive() ? { backing: state.npBacking } : {}),\r\n      };\r\n      if (fr.mask) msg.maskDataUrl = fr.mask;\r\n      if (window.opener) {\r\n        window.opener.postMessage(msg, CHEEKART_TARGET);\r\n        btn.disabled = true; btn.textContent = '\u2713 Sent \u2014 closing\u2026';\r\n        setTimeout(() => window.close(), 700);\r\n      } else {\r\n        alert('This window wasn\u2019t opened from the cheeks product page \u2014 please start the Cheek Studio from there.');\r\n      }\r\n    };\r\n    readUrl(logoFile).then((art) => { fr.result = art; return LS.maskPending || null; })\r\n      .then(() => logoMaskFile ? readUrl(logoMaskFile) : '')\r\n      .then((mask) => { fr.mask = mask || ''; send(); });\r\n    return;\r\n  }\r\n  // \u2500\u2500 Etsy / Shopify art submission: post ONLY the $0 art item (no head) \u2500\u2500\r\n  if (ART_SUBMIT_MODE) {\r\n    const btn = document.getElementById('cfg-add');\r\n    const orderNum = (document.getElementById('etsy-order').value || '').trim();\r\n    if (!(SHOP_ART_MODE ? hasCheekDesign() : hasLogo()) || !logoFile) {   // NeoPixel art counts in the Shopify mode\r\n      alert('Design your cheek art first \u2014 open the \uD83C\uDFA8 Logo Studio (or upload a logo).'); return;\r\n    }\r\n    if (SHOP_ART_MODE ? !/^#?\\d{3,7}$/.test(orderNum) : !/^[\\d-]{6,16}$/.test(orderNum)) {\r\n      alert(SHOP_ART_MODE ? 'Please enter your order number \u2014 it\u2019s in your order confirmation email (like #1879).'\r\n                          : 'Please enter your Etsy order number \u2014 the long number on your Etsy receipt.'); return;\r\n    }\r\n    const id = resolveVariantId(ETSY_SKU);\r\n    const props = {\r\n      ...(SHOP_ART_MODE ? { 'For order': '#' + orderNum.replace(/^#/, '') } : { 'Etsy order': orderNum }),\r\n      'Logo placement': state.logoMirror ? 'mirrored \u2014 reflected on the second cheek' : 'same art on both cheeks',\r\n      ...(SHOP_ART_MODE && neoActive() ? { 'Cheek backing': NP_BACKINGS[state.npBacking] } : {}),\r\n      'Previewed on': `${state.style.toUpperCase()} (${cheekHeadCode().toUpperCase()} cheek)`,\r\n      '_config_id': (SHOP_ART_MODE ? 'PF-SHOP-' : 'PF-ETSY-') + Date.now().toString(36).toUpperCase(),\r\n      ...(SHOP_ART_MODE && SHOP_ART_TOKEN ? { '_Art token': SHOP_ART_TOKEN } : {}),   // hidden: proves the order is theirs\r\n    };\r\n    await (LS.maskPending || null);   // the light mask (if any) is ready before it rides along\r\n    if (CART_MODE === 'shopify' && id) {\r\n      btn.disabled = true; btn.textContent = 'Submitting\u2026';\r\n      try {\r\n        const fd = new FormData();\r\n        fd.append('id', id); fd.append('quantity', 1);\r\n        for (const [k, v] of Object.entries(props)) fd.append(`properties[${k}]`, v);\r\n        fd.append('properties[Logo]', logoFile, logoFile.name);\r\n        if (logoMaskFile) fd.append('properties[_Logo light mask]', logoMaskFile, logoMaskFile.name);   // hidden from the customer (2c)\r\n        const r = await fetch('/cart/add.js', { method: 'POST', body: fd });\r\n        if (!r.ok) throw await r.json().catch(() => ({}));\r\n        location.href = '/cart';\r\n      } catch (err) {\r\n        alert('Couldn\u2019t submit your art:\\n' + (err.description || err.message || 'please try again or contact us.'));\r\n        btn.disabled = false; btn.textContent = '\uD83C\uDFA8 Submit Cheek Art \u2014 Free';\r\n      }\r\n    } else {\r\n      showDevPayload({\r\n        mode: CART_MODE, sku: ETSY_SKU, variant_resolved: !!id, properties: props,\r\n        logo_file: `${logoFile.name} (${Math.round(logoFile.size / 1024)} KB)`,\r\n      });\r\n    }\r\n    return;\r\n  }\r\n  if (!headLive()) return;   // COMING SOON teaser \u2014 nothing to add\r\n  if (soldLines().length) return;   // sold out \u2014 the CTA says so\r\n  const btn = document.getElementById('cfg-add');\r\n  const { cfgId, items, unmapped } = buildCartItems();\r\n  if (CART_MODE === 'shopify' && items.length && !unmapped.length) {\r\n    btn.disabled = true; btn.textContent = 'Adding\u2026';\r\n    const added = [];   // line keys THIS click put in the cart \u2014 rolled back on any failure\r\n    let ticketAdded = false;   // only a ticket this click added is ever removed again\r\n    try {\r\n      // If a logo was uploaded, the bubble-cheek line item carries the actual\r\n      // FILE so Shopify stores it and links it on the order (native file\r\n      // property). Files need a multipart request, so that item is sent as\r\n      // FormData while the rest go as one JSON add. /cart/add.js is atomic per\r\n      // request, so the whole build is made all-or-nothing by removing what an\r\n      // earlier request added when a later one fails.\r\n      const post = async (opts) => {\r\n        const r = await fetch('/cart/add.js', { method: 'POST', ...opts });\r\n        const j = await r.json().catch(() => ({}));\r\n        if (!r.ok) throw Object.assign(j, { status: r.status });\r\n        for (const it of (j.items || [j])) if (it && it.key) added.push(it.key);\r\n        return j;\r\n      };\r\n      const asJSON = (list) => ({\r\n        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },\r\n        body: JSON.stringify({ items: list }) });\r\n      // production ticket FIRST (capped heads only; dormant until it's live).\r\n      // ensureTicket reads the live count, keeps an existing ticket at qty 1,\r\n      // or adds one alone (422 = full) \u2014 never a second ticket line.\r\n      const tix = ticketApi();\r\n      if (tix && items.some(it => cappedSku(it._sku))) {\r\n        const t = await tix.ensureTicket();\r\n        if (!t || !t.ok) {\r\n          if (t && t.full) tix.openWaitlist('configurator');\r\n          else alert('We couldn\u2019t reserve your production ticket just now. Please try again in a moment.');\r\n          return;\r\n        }\r\n        ticketAdded = !!t.added;\r\n      }\r\n      await (LS.maskPending || null);   // the light mask (if any) is ready before it rides along\r\n      const ci = (hasCheekDesign() && logoFile) ? items.findIndex(it => /CHEEK/.test(it._sku || '')) : -1;\r\n      if (ci >= 0) {\r\n        const rest = items.filter((_, i) => i !== ci);\r\n        if (rest.length) await post(asJSON(rest));\r\n        const c = items[ci], fd = new FormData();\r\n        fd.append('id', c.id); fd.append('quantity', c.quantity);\r\n        for (const [k, v] of Object.entries(c.properties || {})) fd.append(`properties[${k}]`, v);\r\n        fd.append('properties[Logo]', logoFile, logoFile.name);\r\n        if (logoMaskFile) fd.append('properties[_Logo light mask]', logoMaskFile, logoMaskFile.name);   // hidden from the customer (2c)\r\n        if (hasLogo()) fd.append('properties[Cheek LED color]', cheekLedName(state.ledColor));\r\n        if (neoActive()) fd.append('properties[Cheek backing]', NP_BACKINGS[state.npBacking]);\r\n        fd.append('properties[Logo placement]',\r\n          state.logoMirror ? 'mirrored \u2014 reflected on the second cheek' : 'same art on both cheeks');\r\n        await post({ body: fd });   // browser sets the multipart Content-Type\r\n      } else {\r\n        await post(asJSON(items));\r\n      }\r\n      location.href = '/cart';\r\n    } catch (err) {\r\n      let undone = await removeCartLines(added);\r\n      if (ticketAdded) {\r\n        try { await window.pfBuildSlot.removeTicket(); } catch { undone = false; }\r\n      }\r\n      alert('Couldn\u2019t add this build to the cart:\\n' +\r\n        (err.description || err.message || 'please try again or contact us.') +\r\n        (undone ? '\\n\\nNothing was added, so your cart is as it was.' : '\\n\\nSome items may not have been added.'));\r\n    } finally {\r\n      btn.disabled = false; btn.textContent = 'Add to Cart';\r\n    }\r\n  } else {\r\n    // preview: show the computed cart SKU codes for validation. Without\r\n    // PF_SKU2ID (only present on Shopify) nothing resolves to an id, so the\r\n    // codes surface here \u2014 this is the \"does the build map correctly\" view.\r\n    const codes = buildCartSkus();\r\n    showDevPayload({\r\n      config: cfgId,\r\n      mode: CART_MODE,\r\n      unmapped,\r\n      cart_line_items: codes,\r\n      logo_file: (hasCheekDesign() && logoFile)\r\n        ? `${logoFile.name} (${Math.round(logoFile.size / 1024)} KB) \u2192 attaches to the ${cheekHeadCode().toUpperCase()} cheek line`\r\n        : null,\r\n      cheek_led_color: hasLogo() ? cheekLedName(state.ledColor) : null,\r\n      cheek_backing: (neoActive() && hasCheekDesign()) ? NP_BACKINGS[state.npBacking] : null,\r\n      logo_placement: hasCheekDesign() ? (state.logoMirror ? 'mirrored' : 'same') : null,\r\n      resolved_line_items: items.map(i => ({ id: i.id, sku: i._sku })),\r\n    });\r\n  }\r\n};\r\n\r\n// \u2550\u2550 Boot \u2550\u2550\r\nasync function boot() {\r\n  const [res] = await Promise.all([\r\n    fetch(PART_MANIFEST_URL),\r\n    loadShopifyVariants(),   // populate PF_SKU2ID on Shopify (no-op locally)\r\n    // firmware face library \u2014 optional (procedural fallback if absent)\r\n    fetch(ASSET_BASE + 'faces.json' + ASSET_VER)\r\n      .then(r => r.ok ? r.json() : null).then(j => { FACES = j; }).catch(() => {}),\r\n  ]);\r\n  if (!res.ok) throw new Error(`manifest ${res.status} at ${res.url}`);\r\n  {   // the protected manifest is scrambled; a plain one (local glb_out) is JSON text\r\n    const mbuf = await res.arrayBuffer();\r\n    const raw = new TextDecoder().decode(mbuf);\r\n    manifest = JSON.parse(/^\\s*\\{/.test(raw) ? raw : new TextDecoder().decode(unscramble(mbuf, 'manifest')));\r\n  }\r\n  // migrate pre-format-key LED configs if present\r\n  for (const [style, cfg] of Object.entries(manifest.led || {})) {\r\n    if (cfg.panels) manifest.led[style] = { [cfg.type === 'HUB75' ? 'hub75' : 'modules']: cfg };\r\n  }\r\n  // deep-linkable styles = modern (gasket-derived) + legacy heads \u2014 ?base=mk3\r\n  // was silently ignored when this list was gaskets-only\r\n  const styles = [...new Set([...partsByCat('gasket').map(styleOf), ...legacyStyles()])];\r\n  state.style = styles.includes('cipher') ? 'cipher' : styles[0];\r\n  const rig = FILM_MODE || VID.cam || VID.pose || VID.demo || VID.record;\r\n  state.ear = rig ? (partsByCat('ear').find(n => !NOT_SELLABLE.has(n)) || partsByCat('ear')[0] || null) : 'none';\r\n  const fins = partsByCat('fin').filter(n => !NOT_SELLABLE.has(n));\r\n  state.fin = fins.includes('fin_short') ? 'fin_short' : (fins[0] || 'none');\r\n  // restore a saved build from a shared ?build= link (applyState then gates it)\r\n  const params = new URLSearchParams(location.search);\r\n  applyBuild(decodeBuild(params.get('build')));\r\n  // simple hand-writable deep links for product pages / marketing:\r\n  //   ?base=cipher            pre-selects the Cipher head\r\n  //   ?collection=dlux        pre-selects the D-Lux collection\r\n  // A full ?build= link wins if both are present; illegal values fall through\r\n  // applyState's gating to the defaults.\r\n  if (!params.get('build')) {\r\n    const coll = (params.get('collection') || '').toLowerCase();\r\n    if (COLLECTIONS[coll]) state.collection = coll;\r\n    const base = (params.get('base') || params.get('style') || '').toLowerCase();\r\n    if (styles.includes(base)) {\r\n      state.style = base;\r\n      // if the requested head isn't in the chosen/default collection, hop to one that has it\r\n      if (!collectionStyles(state.collection).includes(base)) {\r\n        const home = Object.keys(COLLECTIONS).find(c => collectionStyles(c).includes(base));\r\n        if (home) state.collection = home;\r\n      }\r\n    }\r\n  }\r\n  // Cheek-Studio popup: hint the previewed head from the PDP's picked variant\r\n  // (AC-CHEEK-MK3/MK5 SKUs \u2192 legacy heads; PF \u2192 modern default)\r\n  if (CHEEKART_MODE && CHEEKART_VARIANT && window.PF_SKU2ID) {\r\n    const sku = Object.keys(window.PF_SKU2ID).find(s => window.PF_SKU2ID[s] === CHEEKART_VARIANT) || '';\r\n    if (/MK3/.test(sku)) state.style = 'mk3';\r\n    else if (/MK5/.test(sku)) state.style = 'mk5';\r\n    if (/-NP$/i.test(sku)) CHEEKART_NP = true;\r\n    if (!CHEEKART_PLATE) CHEEKART_PLATE = /^AC-CHEEK-PF/i.test(sku) ? 'pf' : /^AC-CHEEK-(MK2|MK3|MK5|ZR)/i.test(sku) ? 'm16' : '';\r\n  }\r\n  initPanelMode();   // guided or Advanced, before the first paint\r\n  await applyState();\r\n  // Etsy submissions: land straight in the Logo Studio (their cheeks are bought;\r\n  // the art is all that's left to do); a personalized &order= link prefills the number\r\n  if (ART_SUBMIT_MODE) {\r\n    if (SHOP_ART_MODE) {\r\n      document.getElementById('etsy-note').innerHTML = '<b style=\"color:var(--ink)\">Your cheek art</b> \u2014 design it ' +\r\n        'here, check your order number below, and submit it free \u2014 it goes straight onto your order.';\r\n      const inp = document.getElementById('etsy-order');\r\n      inp.placeholder = 'Order number (in your confirmation email, like #1879)';\r\n      inp.maxLength = 8;\r\n    }\r\n    if (ETSY_PREFILL) document.getElementById('etsy-order').value =\r\n      SHOP_ART_MODE ? '#' + ETSY_PREFILL.replace(/[^0-9]/g, '') : ETSY_PREFILL;\r\n    setTimeout(() => LS.show(), 400);\r\n  }\r\n  if (CHEEKART_MODE) setTimeout(() => {\r\n    LS.show();\r\n    if (CHEEKART_EDIT_ORDER) cheekartAwaitOrderArt();   // ops \"Edit\": the order's current art, from the opener\r\n    else if (CHEEKART_EDIT) LS.restore();   // \"Edit design\": reload their last design\r\n  }, 400);\r\n  swapAnimsEnabled = true;   // part swaps grow in from here on (not the boot assembly)\r\n  // film/video mode is allowed to be heavyweight (per Blair): warm EVERY model in\r\n  // the background so manual or scripted swaps are always instant, never a dark beat\r\n  if (FILM_MODE || VID.cam || VID.pose || VID.demo || VID.record || VID.res)\r\n    setTimeout(() => { for (const n of Object.keys(manifest.parts)) loadGLTF(n).catch(() => {}); }, 1200);\r\n  // video/demo mode: set camera + pose, preload the choreography, roll\r\n  if (VID.cam || VID.pose || VID.demo || VID.record) startVideoMode();\r\n}\r\n\r\n// \u2550\u2550 Guided mode (CFG-1) \u2550\u2550 the first-visit panel: 1 what the head should do\r\n// (a tier) \u2192 2 its shape \u2192 3 its display \u2192 4 its look. It is a VIEW over the\r\n// same state and the same sections as Advanced customization: step 3 shows the\r\n// real display / LED color sections and step 4 the fins / ears / cheeks ones,\r\n// so both modes share every rule and switching between them keeps the build.\r\n// Legacy Rewired, head base only and visor only stay in Advanced.\r\nconst GUIDED_DEFAULT = true;   // D5 (Blair, Oct 22): first visits start guided\r\n// Step 1 cards: data only, so names / taglines / bullets change without code\r\n// (copy = D18; tier names stay Starter / Studio / D-LUX = D20)\r\nconst GUIDED_TIERS = [\r\n  { collection: 'starter', name: 'Starter', tag: 'Simple and colorful', display: 'HUB75',\r\n    bullets: ['Full-color face out of the box', 'Switch faces from a phone app',\r\n              'Design your own faces free in the Face Maker'],\r\n    tech: 'WF1 controller \u00B7 full-color LED panel (HUB75) \u00B7 LEDArt phone app, or face programs from the Face Maker.' },\r\n  { collection: 'studio', name: 'Studio', tag: 'Interactive', display: 'MAX7219',\r\n    bullets: ['Boop sensor: it reacts when you\u2019re booped', 'Mouth moves when you talk',\r\n              '134+ preset faces, edit them in your browser'],\r\n    tech: 'ProtoFrenz 3.0 WiFi controller \u00B7 single-color LED matrix (MAX7219) or full-color panel (HUB75), depending on the shape \u00B7 built-in web control page.' },\r\n  { collection: 'dlux', name: 'D-LUX & Legacy Rewired', tag: 'Maximum expression', display: 'NeoPixel',\r\n    also: [{ collection: 'rewired', styles: ['mk5'] }],   // Legacy Rewired heads shown as D-LUX shapes\r\n    bullets: ['NeoPixel LEDs: our brightest display, in millions of colors', 'Everything Studio does'],\r\n    tech: 'ProtoFrenz 3.0 WiFi controller \u00B7 per-pixel RGB (NeoPixel) display.' },\r\n];\r\nconst SHAPE_PROFILE = { cipher: 'Squared, angular profile', nyx: 'Pointed, narrow profile',\r\n  iris: 'Rounded, softer profile', vex: 'Triangular, sharp-edged profile',\r\n  mk3: 'The classic protogen profile', mk5: 'A larger, bolder classic', mk2: 'The original profile' };\r\n// side-profile outlines for the shape cards: [w, h, path], traced from Blair's\r\n// Photo Studio silhouettes (PHOTOS/Profiles, the 'free' angle, logo cropped\r\n// off) at one shared scale so the shapes compare by size too. MK3 has one per\r\n// head-shell shape. Re-trace if a head shell changes.\r\nconst SHAPE_SIL = {\r\n  cipher: [136, 107, 'M72 1 80 1 87 3 98 8 102 10 106 15 110 20 114 29 115 38 130 20 131 20 132 20 135 23 126 41 126 46 126 47 120 58 120 66 117 73 114 78 111 81 108 81 104 80 98 85 95 88 89 90 87 90 85 92 79 94 73 97 69 99 56 104 49 106 38 106 31 105 24 102 11 95 2 86 1 85 1 84 3 80 10 63 19 49 28 33 36 24 38 16 40 14 47 8 54 4Z'],\r\n  iris: [115, 103, 'M60 1 67 1 74 3 86 9 91 11 96 17 99 24 101 30 107 21 110 18 114 16 114 20 111 50 110 62 108 67 106 73 102 79 91 88 85 91 80 92 77 92 73 91 53 98 41 101 28 102 20 100 15 97 12 94 11 91 6 84 3 76 1 70 1 65 2 58 4 50 12 35 21 22 24 16 30 11 39 5 44 3 49 3 52 2Z'],\r\n  nyx: [111, 116, 'M105 1 108 1 110 3 100 50 101 51 102 54 103 57 104 68 104 72 102 85 85 98 76 101 74 98 68 101 57 107 44 112 38 114 32 115 25 114 20 113 18 110 17 107 9 99 3 92 1 87 1 83 6 62 11 49 15 38 17 32 18 29 23 22 25 7 29 8 29 15 29 18 36 15 42 14 47 13 52 12 63 12 70 14 74 15 81 20 87 25 91 31 96 40Z'],\r\n  vex: [126, 111, 'M65 1 73 1 81 3 92 8 97 11 102 17 107 25 109 32 110 44 120 44 123 46 124 47 125 52 123 58 119 66 113 74 108 79 104 83 102 84 98 83 91 89 85 91 81 92 73 97 33 108 26 110 20 110 17 106 10 93 2 77 1 73 3 67 16 41 23 30 28 23 31 17 32 14 38 9 45 4Z'],\r\n  mk2: [110, 94, 'M54 1 61 1 70 3 78 6 84 10 89 20 92 28 94 38 94 49 102 49 108 50 109 53 109 57 105 63 95 73 87 79 79 85 76 85 73 89 61 91 58 91 62 90 62 89 61 89 32 93 26 93 22 92 18 90 11 83 4 73 1 67 1 64 3 58 6 49 11 37 25 15 29 11 32 10 40 4 48 2Z'],\r\n  mk3_round: [122, 88, 'M66 1 72 2 78 3 84 5 89 9 96 16 100 25 102 32 103 41 113 42 120 44 121 47 120 50 115 56 108 64 98 72 93 77 89 79 86 80 79 80 53 85 52 84 53 83 52 83 24 87 17 87 15 86 11 83 6 70 3 56 1 46 1 44 11 32 17 26 28 17 32 12 39 7 44 4 48 3Z'],\r\n  mk3_sharp: [126, 88, 'M62 1 70 1 76 2 82 4 87 7 94 14 99 22 102 32 103 41 115 42 121 44 125 45 124 45 117 55 108 63 99 71 94 76 88 79 86 80 79 79 52 84 53 83 52 83 24 87 17 86 15 85 13 85 11 82 6 69 3 56 1 45 1 44 11 32 21 22 28 16 33 10 38 7 42 4 47 3 56 2Z'],\r\n  mk5: [120, 101, 'M62 1 74 1 85 3 92 6 98 11 102 16 104 21 106 29 115 21 116 21 119 23 110 34 108 42 109 44 101 55 99 61 96 67 91 73 90 73 86 74 85 76 78 81 73 83 51 97 46 99 39 100 15 91 11 90 4 82 2 78 1 75 13 47 22 28 26 22 26 20 26 18 31 11 32 9 38 9 40 8 43 8 51 3 56 2Z'],\r\n  zr: [115, 100, 'M56 1 65 1 72 3 81 6 85 9 91 14 93 19 97 29 98 38 105 34 114 27 111 38 105 53 106 58 105 62 101 73 97 78 95 79 93 79 92 79 91 79 88 81 79 86 75 88 73 88 71 91 69 91 58 95 47 98 39 99 33 99 25 98 19 96 13 91 4 75 2 69 1 62 2 54 3 48 9 34 17 23 18 19 23 12 32 6 37 3 40 3 49 3Z'],\r\n};\r\nfunction shapeSil(style, k = 0.44, cls = 'g-sil') {\r\n  // a head with shell shapes (MK3 round / sharp) shows the one picked, else its first\r\n  const s = SHAPE_SIL[style]\r\n    || SHAPE_SIL[`${style}_${(style === state.style && state.shape) || shapesFor(style)[0]}`];\r\n  if (!s) return null;\r\n  const wrap = gEl('span', cls);\r\n  wrap.innerHTML = `<svg viewBox=\"0 0 ${s[0]} ${s[1]}\" width=\"${Math.round(s[0] * k)}\" height=\"${Math.round(s[1] * k)}\" aria-hidden=\"true\"><path d=\"${s[2]}\"/></svg>`;\r\n  return wrap;\r\n}\r\nconst G = { on: false, step: 1, reached: 1, techOpen: false };\r\n// the tier a build belongs to: its own collection, or one a tier borrows shapes from\r\nconst tierHas = (t, coll, style) => t.collection === coll\r\n  || !!t.also?.some(a => a.collection === coll && a.styles.includes(style));\r\nconst guidedTier = () => GUIDED_TIERS.find(t => tierHas(t, state.collection, state.style)) || null;\r\n// a tier's shapes as {coll, style}: only ones you can buy today (drafts stay in\r\n// Advanced as teasers), plus the borrowed Legacy Rewired heads\r\nfunction tierShapes(t) {\r\n  const out = collectionStyles(t.collection).map(s => ({ coll: t.collection, style: s }));\r\n  for (const a of t.also || [])\r\n    for (const s of a.styles) if (collectionStyles(a.collection).includes(s)) out.push({ coll: a.collection, style: s });\r\n  return out.filter(o => styleLive(o.style, o.coll) || (o.coll === state.collection && o.style === state.style));\r\n}\r\n// cheapest variant in a SKU family that isn't sold out (null without price data)\r\nfunction fromCents(prefix) {\r\n  let min = null;\r\n  for (const [sku, c] of Object.entries(window.PF_SKU2PRICE || {}))\r\n    if ((sku === prefix || sku.startsWith(prefix + '-')) && !isSold(sku) && (min == null || c < min)) min = c;\r\n  return min;\r\n}\r\nconst fromText = (c) => c == null ? '' : money(c).replace(/[.,]00(?=\\D*$)/, '');\r\nfunction gEl(tag, cls, text) {\r\n  const e = document.createElement(tag);\r\n  if (cls) e.className = cls;\r\n  if (text != null) e.textContent = text;\r\n  return e;\r\n}\r\nfunction gCard(on, off, parts, onclick, tag) {\r\n  const b = gEl('button', 'g-card' + (on ? ' on' : '') + (off ? ' off' : ''));\r\n  for (const p of parts) if (p) b.appendChild(p);\r\n  if (tag) (b.querySelector('.g-kick, .g-name') || b.firstChild).appendChild(gEl('span', tag[1], tag[0]));\r\n  if (!off) b.onclick = onclick;\r\n  return b;\r\n}\r\nfunction renderGuided() {\r\n  const panel = document.getElementById('cfg-panel');\r\n  document.getElementById('mode-guided').classList.toggle('on', G.on);\r\n  document.getElementById('mode-advanced').classList.toggle('on', !G.on);\r\n  panel.classList.toggle('guided', G.on);\r\n  document.body.classList.toggle('mode-guided', G.on);\r\n  // guided keeps Add to Cart / Save out of the way until the buyer reaches Look\r\n  if (G.on) G.reached = Math.max(G.reached, G.step);\r\n  const wait = G.on && G.reached < 4;\r\n  if (document.body.classList.contains('cta-wait') !== wait) {\r\n    document.body.classList.toggle('cta-wait', wait); padPanelForCTA();\r\n  }\r\n  const shared = [...panel.querySelectorAll(':scope > .cfg-sec'), document.getElementById('cfg-disclaimer')];\r\n  if (!G.on) { shared.forEach(el => el.classList.remove('g-hide')); return; }\r\n  const tier = guidedTier();\r\n  if (!tier) G.step = 1;\r\n\r\n  const steps = document.getElementById('g-steps'); steps.innerHTML = '';\r\n  ['What it does', 'Shape', 'Display', 'Look'].forEach((n, i) => {\r\n    const b = gEl('button', G.step === i + 1 ? 'on' : '', `${i + 1} ${n}`);\r\n    b.disabled = i > 0 && !tier;\r\n    b.onclick = () => goStep(i + 1);\r\n    steps.appendChild(b);\r\n  });\r\n\r\n  const body = document.getElementById('g-body'); body.innerHTML = '';\r\n  if (G.step === 1) {\r\n    body.appendChild(gEl('h3', '', 'What should your protogen do?'));\r\n    body.appendChild(gEl('p', 'g-lead', 'Pick the kind of head you want. Watch the 3D view change, and you can switch anytime.'));\r\n    const cards = gEl('div', 'g-cards');\r\n    for (const t of GUIDED_TIERS) {\r\n      const sold = optionStatus('collection', t.collection).sold;\r\n      const off = sold || !tierShapes(t).length;\r\n      const ul = gEl('ul');\r\n      for (const line of t.bullets) ul.appendChild(gEl('li', '', line));\r\n      const prices = [t.collection, ...(t.also || []).map(a => a.collection)]\r\n        .map(c => fromCents(COLL_CODE[c])).filter(c => c != null);\r\n      const cents = prices.length ? Math.min(...prices) : null;\r\n      const from = cents == null ? null : gEl('span', 'g-from', 'From ');\r\n      if (from) from.appendChild(gEl('b', '', fromText(cents)));\r\n      cards.appendChild(gCard(guidedTier() === t, off,\r\n        [gEl('span', 'g-kick', t.name), gEl('span', 'g-tag', t.tag), ul, from],\r\n        () => {\r\n          if (guidedTier() !== t) { state.collection = t.collection; state.display = t.display; }\r\n          applyState();\r\n        }, sold ? ['SOLD OUT', 'sold-tag'] : null));\r\n    }\r\n    body.appendChild(cards);\r\n    const alt = gEl('div', 'g-alt', 'Looking for a head base, a replacement visor or another Legacy Rewired head? They\u2019re in ');\r\n    const link = gEl('button', 'g-link', 'Freeform');\r\n    link.onclick = () => setPanelMode(false);\r\n    alt.appendChild(link); alt.appendChild(document.createTextNode('.'));\r\n    body.appendChild(alt);\r\n  } else if (G.step === 2) {\r\n    const shapes = tierShapes(tier);\r\n    body.appendChild(gEl('h3', '', 'Pick a shape'));\r\n    body.appendChild(gEl('p', 'g-lead', `Showing the ${shapes.length} shapes that come as ${tier.name}. Tap one to see it in 3D.`));\r\n    const cards = gEl('div', 'g-cards g-shapes');\r\n    for (const { coll, style: s } of shapes) {\r\n      const borrowed = coll !== tier.collection;\r\n      const st = { soon: !styleLive(s, coll), sold: allSold(headFamily(coll, s)) };\r\n      const cents = fromCents(headFamily(coll, s));\r\n      const from = cents == null ? null : gEl('span', 'g-from', 'From ');\r\n      if (from) from.appendChild(gEl('b', '', fromText(cents)));\r\n      const prof = (SHAPE_PROFILE[s] || '') + (borrowed ? `, rebuilt as ${COLLECTIONS[coll].label}` : '');\r\n      cards.appendChild(gCard(s === state.style && coll === state.collection, st.sold,\r\n        [shapeSil(s), gEl('span', 'g-name', s.toUpperCase()), gEl('span', 'g-prof', prof), from],\r\n        () => {\r\n          if (state.collection !== coll) { state.collection = coll; state.display = tier.display; }\r\n          state.style = s; applyState();\r\n        },\r\n        st.sold ? ['SOLD OUT', 'sold-tag'] : st.soon ? ['SOON', 'soon-tag'] : null));\r\n    }\r\n    body.appendChild(cards);\r\n  } else if (G.step === 3) {\r\n    const choices = COLLECTIONS[state.collection].displays.filter(d => displayAvailable(state.style, d));\r\n    body.appendChild(gEl('h3', '', 'Pick a display'));\r\n    body.appendChild(gEl('p', 'g-lead', 'The display is your protogen\u2019s face. ' + (choices.length > 1\r\n      ? 'Tap one to see it light up in 3D.'\r\n      : `${COLLECTIONS[state.collection].label} ${state.style.toUpperCase()} comes with the one below.`) +\r\n      (state.display === 'MAX7219' ? ' Then pick its LED color.' : '')));\r\n  } else {\r\n    const legacy = isLegacy(state.style);\r\n    body.appendChild(gEl('h3', '', 'Make it yours'));\r\n    body.appendChild(gEl('p', 'g-lead', (legacy\r\n      ? 'Pick your antennae shape, ears and cheeks. The 3D view updates as you go.'\r\n      : 'Pick your fins, ears and cheeks. The 3D view updates as you go.') +\r\n      ' Heads are printed in black and ears in white, the ProtoFrenz look.'));\r\n  }\r\n\r\n  // which shared sections this step shows (renderUI already hid the ones\r\n  // this build doesn't have; g-hide only ever hides more)\r\n  const STEPS = { 3: new Set(['sec-display', 'sec-maxcolor']),\r\n    4: new Set(['sec-fin', 'sec-shape', 'sec-ear', 'sec-ear2', 'sec-cheek', 'cfg-disclaimer']) };\r\n  for (const el of shared) el.classList.toggle('g-hide', !STEPS[G.step]?.has(el.id));\r\n\r\n  // Technical details (controller + panel names) sits under the display step\r\n  const tech = document.getElementById('g-tech');\r\n  tech.classList.toggle('g-hide', G.step !== 3);\r\n  if (G.step === 3) {\r\n    document.getElementById('g-tech-caret').textContent = G.techOpen ? '\u25BE' : '\u25B8';\r\n    document.getElementById('g-tech-sum').textContent = '';\r\n    const tb = document.getElementById('g-tech-body');\r\n    tb.style.display = G.techOpen ? '' : 'none';\r\n    tb.textContent = tier.tech;\r\n  }\r\n\r\n  document.getElementById('g-back').style.visibility = G.step > 1 ? 'visible' : 'hidden';\r\n  const next = document.getElementById('g-next');\r\n  next.style.display = G.step < 4 ? '' : 'none';\r\n  next.textContent = ['Next: pick a shape \u203A', 'Next: pick a display \u203A', 'Next: make it yours \u203A'][G.step - 1] || '';\r\n  next.disabled = G.step === 1 && !tier;\r\n  document.getElementById('g-done').textContent = G.step === 4\r\n    ? 'That\u2019s your build! The price below covers everything you picked. Add it to your cart, or save a link to come back later.' : '';\r\n}\r\n// keep the top of the steps in view (desktop: the panel scrolls itself; mobile:\r\n// the page scrolls under the pinned 3D view)\r\nfunction scrollPanelTop() {\r\n  const panel = document.getElementById('cfg-panel');\r\n  if (panel.scrollHeight > panel.clientHeight + 8) { panel.scrollTo({ top: 0, behavior: 'smooth' }); return; }\r\n  // guided: bring the step chips up to just under the pinned 3D view\r\n  const pinned = G.on ? document.getElementById('cfg-viewer').offsetHeight : 0;\r\n  const top = document.getElementById(G.on ? 'guided-head' : 'cfg-panel').getBoundingClientRect().top;\r\n  if (G.on ? Math.abs(top - pinned - 8) > 24 : top < 0)\r\n    scrollTo({ top: Math.max(0, top + scrollY - pinned - 8), behavior: 'smooth' });\r\n}\r\nfunction goStep(n) {\r\n  G.step = Math.min(4, Math.max(1, n));\r\n  renderGuided(); padPanelForCTA(); scrollPanelTop();\r\n}\r\nfunction setPanelMode(guided) {\r\n  G.on = guided;\r\n  if (G.on && !guidedTier()) G.step = 1;   // came from an Advanced-only line\r\n  try { localStorage.setItem('pfPanelMode', G.on ? 'guided' : 'advanced'); } catch {}\r\n  renderUI(); scrollPanelTop();\r\n}\r\n// First paint: ?advanced / ?guided win, a shared ?build= link opens Advanced (it\r\n// may carry choices guided doesn't show), then the visitor's last pick, then D5.\r\n// Art / film / video rigs never show the mode switch.\r\nfunction initPanelMode() {\r\n  const allowed = !(FILM_MODE || CHEEKART_MODE || ART_SUBMIT_MODE || VID.demo || VID.cam || VID.record || VID.pose);\r\n  document.getElementById('mode-switch').style.display = allowed ? '' : 'none';\r\n  if (!allowed) { G.on = false; return; }\r\n  const p = new URLSearchParams(location.search);\r\n  // (the non-guided mode is shown as \"Freeform\"; ?freeform and the older ?advanced both open it)\r\n  let mode = (p.has('freeform') || p.has('advanced')) ? 'advanced' : p.has('guided') ? 'guided' : p.get('build') ? 'advanced' : null;\r\n  if (!mode) { try { mode = localStorage.getItem('pfPanelMode'); } catch {} }\r\n  if (mode !== 'guided' && mode !== 'advanced') mode = GUIDED_DEFAULT ? 'guided' : 'advanced';\r\n  G.on = mode === 'guided' && !!guidedTier();\r\n  // a link that already picked the line or the head starts at the shape step\r\n  if (G.on && (p.get('collection') || p.get('base') || p.get('style'))) G.step = 2;\r\n}\r\n\r\n// The fixed CTA bar overlays the bottom of the option panel \u2014 keep the panel's\r\n// bottom padding at least as tall as the CTA so the LAST section (Finish &\r\n// Colors) can always scroll fully clear of it. Measured live because the CTA's\r\n// height varies (price wrapping, teaser / sold-out banner).\r\nfunction padPanelForCTA() {\r\n  const cta = document.getElementById('cfg-cta');\r\n  document.getElementById('cfg-panel').style.paddingBottom = (cta.offsetHeight + 28) + 'px';\r\n}\r\nwindow.addEventListener('resize', padPanelForCTA);\r\n\r\n// Face & Fit box: collapsible; starts closed so the head is the first thing\r\n// you see. Hidden entirely in film/video rigs (clean frame).\r\n{\r\n  const pv = document.getElementById('preview-box');\r\n  document.getElementById('pv-head').onclick = () => {\r\n    pv.classList.toggle('closed');\r\n    document.getElementById('pv-caret').textContent = pv.classList.contains('closed') ? '\u25B8' : '\u25BE';\r\n  };\r\n  pv.classList.add('closed');\r\n  document.getElementById('pv-caret').textContent = '\u25B8';\r\n  renderBackdropUI();\r\n  if (FILM_MODE || VID.cam || VID.pose || VID.demo || VID.record) pv.style.display = 'none';\r\n}\r\ndocument.getElementById('mode-guided').onclick = () => setPanelMode(true);\r\ndocument.getElementById('mode-advanced').onclick = () => setPanelMode(false);\r\ndocument.getElementById('g-back').onclick = () => goStep(G.step - 1);\r\ndocument.getElementById('g-next').onclick = () => goStep(G.step + 1);\r\ndocument.getElementById('g-tech-toggle').onclick = () => {\r\n  G.techOpen = !G.techOpen; renderGuided(); padPanelForCTA();\r\n};\r\n\r\n// Save / share: copy a link that restores this exact build\r\ndocument.getElementById('cfg-save').onclick = async () => {\r\n  const btn = document.getElementById('cfg-save'), link = buildLink(), was = btn.textContent;\r\n  history.replaceState(null, '', link);   // link is now in the address bar to copy/bookmark\r\n  let ok = false;\r\n  try { await navigator.clipboard.writeText(link); ok = true; } catch {}\r\n  btn.textContent = ok ? '\u2713 Link copied \u2014 bookmark or share it' : '\uD83D\uDD17 Link is in the address bar \u2191';\r\n  setTimeout(() => { btn.textContent = was; }, 3000);\r\n};\r\nboot().catch(e => {\r\n  const local = location.protocol === 'file:';\r\n  document.getElementById('cfg-loading').innerHTML = local\r\n    ? 'Failed to load assets \u2014 serve this page over http (see serve.bat).'\r\n    : 'Failed to load assets.<br><small style=\"opacity:.7\">' +\r\n        (e && e.message ? e.message : e) + '<br>base: ' + ASSET_BASE + '</small>';\r\n  console.error('boot failed:', e, '| ASSET_BASE =', ASSET_BASE, '| MANIFEST_URL =', MANIFEST_URL);\r\n});\r\n\r\ncontrols.addEventListener('start', () => { spinRadSec = 0; });\r\n\r\n// \u2500\u2500 Boop \uD83D\uDC9A \u2014 clicking the visor's upper tip flashes heart eyes, just like the\r\n// real head's boop sensor. Firmware-face displays only (MAX7219 / NeoPixel). \u2500\u2500\r\nconst boop = () => { boopUntil = clock.getElapsedTime() + 2.6; };\r\n{\r\n  const ray = new THREE.Raycaster();\r\n  // ONE zone test for both the live click handler and the dev probe (fx/fy are\r\n  // canvas fractions 0..1). Fresh matrices + camera orient so it's correct even\r\n  // between frames / in hidden-tab test environments.\r\n  const boopTest = (fx, fy) => {\r\n    const v = slots.get('visor'); if (!v) return { hit: false };\r\n    v.group.updateWorldMatrix(true, true);\r\n    controls.update();\r\n    camera.updateMatrixWorld();\r\n    ray.setFromCamera(new THREE.Vector2(fx * 2 - 1, -(fy * 2 - 1)), camera);\r\n    const hit = ray.intersectObject(v.group, true)[0];\r\n    if (!hit) return { hit: false };\r\n    // model space: Z = up, \u2212Y = front \u2014 the boop zone is the visor's top-front tip\r\n    const p = hit.point.clone().applyMatrix4(worldToRoot());\r\n    const bb = new THREE.Box3().setFromObject(v.group).applyMatrix4(worldToRoot());\r\n    const upFrac = (p.z - bb.min.z) / Math.max(1e-6, bb.max.z - bb.min.z);\r\n    const frontFrac = (p.y - bb.min.y) / Math.max(1e-6, bb.max.y - bb.min.y);\r\n    return { hit: true, upFrac: +upFrac.toFixed(2), frontFrac: +frontFrac.toFixed(2),\r\n             boops: upFrac > 0.55 && frontFrac < 0.5 };\r\n  };\r\n  let down = null;\r\n  renderer.domElement.addEventListener('pointerdown', (e) => { down = [e.clientX, e.clientY]; });\r\n  renderer.domElement.addEventListener('pointerup', (e) => {\r\n    const was = down; down = null;\r\n    if (!was || Math.hypot(e.clientX - was[0], e.clientY - was[1]) > 6) return;   // drag = orbit, not a tap\r\n    if (!(state.display === 'MAX7219' || state.display === 'NeoPixel') || !FACES) return;\r\n    const r = renderer.domElement.getBoundingClientRect();\r\n    if (!r.width || !r.height) return;\r\n    const t = boopTest((e.clientX - r.left) / r.width, (e.clientY - r.top) / r.height);\r\n    if (t.boops) boop();\r\n  });\r\n  window.__cfgDev.boopAt = boopTest;   // dev probe\r\n}\r\nwindow.__cfgDev.boop = boop;\r\n\r\n// \u2500\u2500 Video/demo mode: ?cam ?pose ?demo ?record (parsed into VID up top) \u2500\u2500\u2500\u2500\u2500\u2500\r\nfunction applyVidCam() {\r\n  if (VID.pose !== null && VID.pose !== '') {\r\n    const named = { front: 0, right: -90, left: 90, back: 180 };\r\n    const deg = named[VID.pose.toLowerCase()] ?? (parseFloat(VID.pose) || 0);\r\n    spinner.rotation.z = deg * Math.PI / 180;\r\n  }\r\n  if (VID.cam) {\r\n    const [yaw = 0, pitch = 12, distIn = 0] = VID.cam.split(',').map(parseFloat);\r\n    const d = distIn || camera.position.distanceTo(controls.target) || 0.5;\r\n    const ya = (yaw || 0) * Math.PI / 180, pa = (pitch || 0) * Math.PI / 180;\r\n    camera.position.set(\r\n      controls.target.x + d * Math.cos(pa) * Math.sin(ya),\r\n      controls.target.y + d * Math.sin(pa),\r\n      controls.target.z + d * Math.cos(pa) * Math.cos(ya));\r\n    controls.update();\r\n  }\r\n}\r\n\r\n// choreography: \"0:effect.rainbow;3.5:effect.plasma;7:fin.tron;10:display.matrix,color.pink;12:boop\"\r\n// Every step is APPLIED ONCE up front (loads + caches all assets), state restored,\r\n// and only then does the timeline (and any recording) start \u2014 no mid-take loads.\r\nasync function runDemo() {\r\n  if (!VID.demo) return null;\r\n  const steps = VID.demo.split(';').map(s => {\r\n    const i = s.indexOf(':');\r\n    return { t: i < 0 ? 0 : (parseFloat(s.slice(0, i)) || 0),\r\n             ops: (i < 0 ? s : s.slice(i + 1)).split(',').map(o => o.trim()).filter(Boolean) };\r\n  }).filter(s => s.ops.length).sort((a, b) => a.t - b.t);\r\n  const DISPLAYS = { matrix: 'MAX7219', ledmatrix: 'MAX7219', max7219: 'MAX7219',\r\n                     panel: 'HUB75', fullcolorpanel: 'HUB75', fullcolor: 'HUB75',\r\n                     hub75: 'HUB75', neopixel: 'NeoPixel' };\r\n  const applyOp = async (op) => {\r\n    const dot = op.indexOf('.');\r\n    const key = (dot < 0 ? op : op.slice(0, dot)).toLowerCase();\r\n    const val = dot < 0 ? '' : op.slice(dot + 1);\r\n    const lv = val.toLowerCase();\r\n    switch (key) {\r\n      case 'effect':     state.facePreset = lv; break;\r\n      case 'fin':        state.fin = lv.startsWith('fin_') ? lv : 'fin_' + lv; break;\r\n      case 'ear':        state.ear = lv === 'none' ? 'none'\r\n                           : (lv.startsWith('ear_') ? lv : 'ear_' + lv.replace(/\\s+/g, '_')); break;\r\n      case 'ear2':       state.ear2 = lv === 'none' ? 'none' : 'ear2_' + lv; break;\r\n      case 'style':      state.style = lv; break;\r\n      case 'collection': state.collection = lv; break;\r\n      case 'display':    state.display = DISPLAYS[lv.replace(/[^a-z0-9]/g, '')] || val; break;\r\n      case 'color': { const m = MAX_COLORS.find(c => c[1].toLowerCase() === lv || c[0].toLowerCase() === lv);\r\n                      if (m) state.maxColor = m[0]; break; }\r\n      case 'eyes':       state.faceEyes = parseInt(val) - 1 || 0; break;   // 1-based like the UI\r\n      case 'nose':       state.faceNose = parseInt(val) - 1 || 0; break;\r\n      case 'mouth':      state.faceMouth = parseInt(val) - 1 || 0; break;\r\n      case 'cheeks':     state.cheekMode = (lv === 'off' || lv === 'standard') ? 'standard'\r\n                         : /^(neo|neopixel|np|ring)/.test(lv) ? 'neopixel' : 'bubble'; break;\r\n      case 'boop':       boop(); return;                    // no reload needed\r\n      default: console.warn('demo: unknown op', op); return;\r\n    }\r\n    await applyState();\r\n  };\r\n  // preload pass: run the whole sequence once, then restore the starting state\r\n  const start = JSON.stringify(state);\r\n  for (const s of steps) for (const op of s.ops) if (!/^boop/.test(op)) await applyOp(op);\r\n  Object.assign(state, JSON.parse(start));\r\n  await applyState();\r\n  return () => { for (const s of steps) for (const op of s.ops)\r\n    setTimeout(() => applyOp(op), s.t * 1000); };\r\n}\r\n\r\nasync function startVideoMode() {\r\n  applyVidCam();\r\n  const startTimeline = await runDemo();\r\n  if (VID.record > 0) {\r\n    const banner = document.createElement('div');\r\n    banner.style.cssText = 'position:absolute;top:12px;left:50%;transform:translateX(-50%);z-index:6;' +\r\n      'font:700 12px Epilogue,sans-serif;letter-spacing:.08em;color:#fff;background:rgba(220,40,60,0.92);' +\r\n      'border-radius:4px;padding:7px 14px;pointer-events:none';\r\n    banner.textContent = '\u25CF REC \u2014 keep this tab visible';   // DOM only: captureStream never sees it\r\n    viewer.appendChild(banner);\r\n    const stream = renderer.domElement.captureStream(30);\r\n    const mime = MediaRecorder.isTypeSupported('video/webm;codecs=vp9') ? 'video/webm;codecs=vp9' : 'video/webm';\r\n    const rec = new MediaRecorder(stream, { mimeType: mime, videoBitsPerSecond: 16_000_000 });\r\n    const chunks = [];\r\n    rec.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); };\r\n    rec.onstop = () => {\r\n      const url = URL.createObjectURL(new Blob(chunks, { type: 'video/webm' }));\r\n      const a = document.createElement('a');\r\n      a.href = url; a.download = `protofrenz-${Date.now().toString(36)}.webm`;\r\n      document.body.appendChild(a); a.click(); a.remove();\r\n      setTimeout(() => URL.revokeObjectURL(url), 5000);\r\n      banner.style.background = 'rgba(30,170,110,0.92)';\r\n      banner.textContent = '\u2713 take saved';\r\n      setTimeout(() => banner.remove(), 2500);\r\n    };\r\n    rec.start(1000);              // timesliced so long takes flush progressively\r\n    startTimeline?.();            // recording + choreography share t=0\r\n    setTimeout(() => rec.stop(), VID.record * 1000);\r\n    window.__cfgDev.recorder = rec;\r\n  } else {\r\n    startTimeline?.();\r\n  }\r\n}\r\nwindow.__cfgDev.startVideoMode = startVideoMode;\r\n\r\n// \u2500\u2500 Film rig (?controls=1): on-screen turntable controls + keyboard, for\r\n// shooting product video. H (or its button) hides ALL UI including this card\r\n// so the recording frame stays clean. \u2500\u2500\r\nif (FILM_MODE) {\r\n  const card = document.createElement('div');\r\n  card.style.cssText = 'position:absolute;left:12px;bottom:12px;z-index:5;font:12px Epilogue,sans-serif;' +\r\n    'color:#E8ECF4;background:rgba(12,11,20,0.85);border:1px solid #2A2841;border-radius:6px;' +\r\n    'padding:10px 12px;display:flex;flex-direction:column;gap:7px;user-select:none';\r\n  card.innerHTML =\r\n    '<div style=\"font-family:Syne,sans-serif;font-size:10px;letter-spacing:.2em;color:#2EECC5\">\uD83C\uDFAC FILM RIG</div>' +\r\n    '<div style=\"display:flex;gap:6px;align-items:center\">' +\r\n      '<button data-act=\"slower\" title=\"\u2190\">\u2212</button>' +\r\n      '<span id=\"film-speed\" style=\"min-width:72px;text-align:center;color:#2EECC5\"></span>' +\r\n      '<button data-act=\"faster\" title=\"\u2192\">+</button>' +\r\n      '<button data-act=\"pause\" title=\"space\" id=\"film-pause\">\u23F8</button>' +\r\n      '<button data-act=\"reset\" title=\"0\">\u21BA</button></div>' +\r\n    '<div style=\"display:flex;gap:6px\">' +\r\n      '<button data-act=\"level\" title=\"L\">Level</button>' +\r\n      '<button data-act=\"front\" title=\"F\">Front</button>' +\r\n      '<button data-act=\"side\" title=\"S\">Side</button>' +\r\n      '<button data-act=\"hide\" title=\"H\">Hide UI</button></div>' +\r\n    '<div style=\"font-size:10px;color:#6B7389\">keys: \u2190 \u2192 speed \u00B7 space pause \u00B7 0 reset \u00B7 L F S \u00B7 H hide</div>';\r\n  card.querySelectorAll('button').forEach(b => b.style.cssText =\r\n    'background:#1F1D2E;border:1px solid #2A2841;color:#E8ECF4;border-radius:3px;' +\r\n    'padding:5px 9px;font:12px Epilogue,sans-serif;cursor:pointer');\r\n  viewer.appendChild(card);\r\n  const speedEl = card.querySelector('#film-speed'), pauseEl = card.querySelector('#film-pause');\r\n  const degSec = () => (spinRadSec * 180 / Math.PI).toFixed(1);\r\n  const readout = () => { speedEl.textContent = spinRadSec ? `${degSec()}\u00B0/s` : 'paused';\r\n    pauseEl.textContent = spinRadSec ? '\u23F8' : '\u25B6'; };\r\n  let uiHidden = false, pausedFrom = SPIN_DEFAULT;\r\n  const dist = () => camera.position.distanceTo(controls.target);\r\n  const act = {\r\n    slower: () => { spinRadSec -= 0.024; },\r\n    faster: () => { spinRadSec += 0.024; },\r\n    pause:  () => { if (spinRadSec) { pausedFrom = spinRadSec; spinRadSec = 0; }\r\n                    else spinRadSec = pausedFrom || SPIN_DEFAULT; },\r\n    reset:  () => { spinRadSec = SPIN_DEFAULT; },\r\n    level:  () => { const d = dist(), v = camera.position.clone().sub(controls.target);\r\n                    v.y = 0; v.setLength(d); camera.position.copy(controls.target).add(v); },\r\n    front:  () => camera.position.set(controls.target.x, controls.target.y, controls.target.z + dist()),\r\n    side:   () => camera.position.set(controls.target.x + dist(), controls.target.y, controls.target.z),\r\n    hide:   () => { uiHidden = !uiHidden;\r\n      document.getElementById('cfg-panel').style.display = uiHidden ? 'none' : '';\r\n      document.getElementById('cfg-cta').style.display = uiHidden ? 'none' : '';\r\n      document.getElementById('cfg-app').style.gridTemplateColumns = uiHidden ? 'minmax(0,1fr)' : '';\r\n      card.style.opacity = uiHidden ? '0' : '1';           // out of frame while recording\r\n      card.style.pointerEvents = uiHidden ? 'none' : '';   // (H key brings it back)\r\n      resize(); },\r\n  };\r\n  card.querySelectorAll('[data-act]').forEach(b =>\r\n    b.onclick = () => { act[b.dataset.act](); readout(); });\r\n  const KEYMAP = { ArrowLeft: 'slower', ArrowRight: 'faster', ' ': 'pause', '0': 'reset',\r\n                   l: 'level', L: 'level', f: 'front', F: 'front', s: 'side', S: 'side', h: 'hide', H: 'hide' };\r\n  addEventListener('keydown', (e) => {\r\n    if (/INPUT|SELECT|TEXTAREA/.test(e.target.tagName)) return;\r\n    const a = KEYMAP[e.key]; if (!a) return;\r\n    if (e.key === ' ') e.preventDefault();\r\n    act[a](); readout();\r\n  });\r\n  readout();\r\n}\r\n\r\nconst clock = new THREE.Clock();\r\nlet lastTick = 0;\r\nfunction tick() {\r\n  requestAnimationFrame(tick);\r\n  const t = clock.getElapsedTime();\r\n  const dt = Math.min(0.1, t - lastTick); lastTick = t;   // clamp tab-switch gaps\r\n  spinner.rotation.z += spinRadSec * dt;\r\n  // part-swap grow-in: new parts scale 0.82\u21921 over 280ms (ease-out) so option\r\n  // changes read as intentional on camera instead of popping\r\n  for (let i = swapAnims.length - 1; i >= 0; i--) {\r\n    const a = swapAnims[i];\r\n    const p = Math.min(1, (t - a.t0) / 0.28);\r\n    const k = 1 - Math.pow(1 - p, 3);\r\n    a.group.scale.setScalar(0.82 + 0.18 * k);\r\n    if (p >= 1) { a.group.scale.setScalar(1); swapAnims.splice(i, 1); }\r\n  }\r\n  renderLED(t);\r\n  renderCheekRings(t);\r\n  updateSpill(t);\r\n  controls.update();\r\n  // Any lit-panel display (NeoPixel full, HUB75 / MAX7219 at half) OR NeoPixel\r\n  // cheek rings render through the selective-bloom composers; otherwise straight through\r\n  if (ledPanels.length || cheekRings.length) renderGlow();\r\n  else renderer.render(scene, camera);\r\n}\r\ntick();\r\n"],
  "mappings": "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",
  "names": ["key", "scene", "hex", "g", "k", "btn"]
}
