{
  "version": 3,
  "sources": ["/cdn/shop/t/4/assets/face-scan-landing.js?fs_v=20260701_agecrop"],
  "sourcesContent": ["(() => {\n  'use strict';\n\n  const ROOT_SELECTOR = '[data-face-scan]';\n  const UNLOCK_KEY = 'zellaforte_face_scan_unlocked_at';\n  const REFERRAL_KEY = 'zellaforte_face_scan_referral_code';\n  const MAX_SAMPLES = 8;\n  const ANALYSIS_INTERVAL = 900;\n  const FALLBACK_INTERVAL = 220;\n  const VIDEO_READY_TIMEOUT = 8000;\n  const STABLE_RESULT_TARGET = 3;\n  const FRAMING_MS = 2000; // hold a well-framed, lit face this long before the first sample\n  const PULSE_SCAN_SECONDS = 15;\n  const PULSE_SAMPLE_FPS = 15;\n  const PULSE_CANVAS_SIZE = 64;\n  const PULSE_MIN_BPM = 45;\n  const PULSE_MAX_BPM = 180;\n  const AGE_MODEL_DEFAULT_SIZE = 224;\n  const SKIN_MODEL_DEFAULT_SIZE = 512;\n  const SKIN_PARSE_INTERVAL = 1700;\n  const AGE_MODEL_MEAN = [0.485, 0.456, 0.406];\n  const AGE_MODEL_STD = [0.229, 0.224, 0.225];\n  const AGE_GOOGLE_NET_MEAN_BGR = [104, 117, 123];\n  const SKIN_CLASS_IDS = new Set([1, 10]);\n  const AGE_BINS = [\n    [0, 2],\n    [4, 6],\n    [8, 13],\n    [15, 20],\n    [25, 32],\n    [38, 43],\n    [48, 53],\n    [60, 72],\n  ];\n  const MEDIAPIPE_FACE_GROUPS = {\n    oval: [10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288, 397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136, 172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109],\n    leftEye: [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7],\n    rightEye: [263, 466, 388, 387, 386, 385, 384, 398, 362, 382, 381, 380, 374, 373, 390, 249],\n    leftBrow: [70, 63, 105, 66, 107, 55, 65, 52, 53, 46],\n    rightBrow: [336, 296, 334, 293, 300, 276, 283, 282, 295, 285],\n    nose: [168, 6, 197, 195, 5, 4, 45, 220, 115, 48, 64, 98, 97, 2, 326, 327, 294, 278, 344, 440, 275],\n    mouth: [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146],\n  };\n\n  class AgeEstimator {\n    constructor(config) {\n      this.config = config || {};\n      this.modelUrl = this.normalizeAssetUrl(this.config.ageModelUrl);\n      this.modelVersion = String(this.config.ageModelVersion || 'age-model-v1');\n      this.wasmRootUrl = this.normalizeAssetUrl(this.config.ortWasmRootUrl);\n      this.inputSize = Number(this.config.ageInputSize || AGE_MODEL_DEFAULT_SIZE);\n      this.requested = this.config.ageModelEnabled !== false;\n      this.enabled = this.requested && !!this.modelUrl;\n      this.processor = this.detectProcessor();\n      this.session = null;\n      this.inputName = '';\n      this.outputName = '';\n      this.loading = false;\n      this.ready = false;\n      this.failed = this.requested && !this.modelUrl;\n      this.running = false;\n      this.canvas = document.createElement('canvas');\n      this.canvas.width = this.inputSize;\n      this.canvas.height = this.inputSize;\n      this.ctx = this.canvas.getContext('2d', { willReadFrequently: true });\n    }\n\n    normalizeAssetUrl(url) {\n      const value = String(url || '').trim();\n      if (value.startsWith('//')) return `https:${value}`;\n      return value;\n    }\n\n    detectProcessor() {\n      const value = String(this.config.ageModelProcessor || '').trim().toLowerCase();\n      if (value) return value;\n      const url = this.modelUrl.toLowerCase();\n      if (url.includes('genderage') || url.includes('insightface')) return 'genderage';\n      return url.includes('googlenet') ? 'googlenet' : 'vit';\n    }\n\n    async load() {\n      if (!this.enabled || this.ready || this.loading) return this.ready;\n      this.loading = true;\n      this.failed = false;\n\n      try {\n        if (!window.ort) throw new Error('ONNX Runtime Web is not loaded');\n        const wasmRoot = this.wasmRootUrl && this.wasmRootUrl.endsWith('/') ? this.wasmRootUrl : `${this.wasmRootUrl}/`;\n        if (this.wasmRootUrl) window.ort.env.wasm.wasmPaths = wasmRoot;\n        window.ort.env.wasm.numThreads = 1;\n        window.ort.env.wasm.simd = true;\n\n        this.session = await window.ort.InferenceSession.create(this.modelUrl, {\n          executionProviders: ['wasm'],\n          graphOptimizationLevel: 'all',\n        });\n        this.inputName = this.session.inputNames[0] || 'pixel_values';\n        this.outputName = this.session.outputNames[0] || 'logits';\n        this.ready = true;\n        return true;\n      } catch (error) {\n        console.warn('[FaceScan] Age model could not be loaded.', error);\n        this.failed = true;\n        this.ready = false;\n        return false;\n      } finally {\n        this.loading = false;\n      }\n    }\n\n    async predict(video, box) {\n      if (!this.ready || this.running || !this.session || !video || !box) return null;\n      this.running = true;\n      try {\n        const tensor = this.createInputTensor(video, box, false);\n        if (!tensor) return null;\n        const outputs = await this.session.run({ [this.inputName]: tensor });\n        // genderage: mirror-flip TTA \u2014 average the raw age from the normal and\n        // mirrored crop (2 tiny inferences) to cut per-frame variance ~20-30%,\n        // then calibrate + build the \u00B12 range. Other processors keep single-pass.\n        if (this.processor === 'genderage') {\n          let raw = this.rawGenderageAge(outputs);\n          const mt = this.createInputTensor(video, box, true);\n          if (mt) {\n            const mo = await this.session.run({ [this.inputName]: mt });\n            const rawM = this.rawGenderageAge(mo);\n            if (Number.isFinite(raw) && Number.isFinite(rawM)) raw = (raw + rawM) / 2;\n            else if (!Number.isFinite(raw)) raw = rawM;\n          }\n          if (!Number.isFinite(raw)) return null;\n          return this.rangeFromAge(clamp(Math.round(this.calibrateAge(raw)), 13, 95), 2, 2);\n        }\n        return this.parseOutput(outputs);\n      } catch (error) {\n        console.warn('[FaceScan] Age model inference failed.', error);\n        this.failed = true;\n        this.ready = false;\n        return null;\n      } finally {\n        this.running = false;\n      }\n    }\n\n    createInputTensor(video, box, mirror) {\n      const rect = this.sourceRect(video, box);\n      if (!rect || !this.ctx || !window.ort) return null;\n\n      // clearRect leaves transparent-black pixels (r=g=b=0) \u2014 exactly the reference's\n      // warpAffine borderValue=0 padding for out-of-frame regions.\n      this.ctx.clearRect(0, 0, this.inputSize, this.inputSize);\n      // TTA: horizontally flip the whole input canvas so the second pass sees a\n      // mirror of the face; averaging the two ages cancels asymmetric-lighting error.\n      if (mirror) { this.ctx.save(); this.ctx.translate(this.inputSize, 0); this.ctx.scale(-1, 1); }\n      if (rect.pad) {\n        // Face stays CENTERED even at frame edges: draw only the intersection of the\n        // (possibly out-of-bounds) source square at its proper destination offset.\n        const sx = Math.max(0, rect.x);\n        const sy = Math.max(0, rect.y);\n        const ex = Math.min(video.videoWidth, rect.x + rect.width);\n        const ey = Math.min(video.videoHeight, rect.y + rect.height);\n        if (ex > sx && ey > sy) {\n          const scale = this.inputSize / rect.width;\n          this.ctx.drawImage(\n            video, sx, sy, ex - sx, ey - sy,\n            (sx - rect.x) * scale, (sy - rect.y) * scale, (ex - sx) * scale, (ey - sy) * scale\n          );\n        }\n      } else {\n        this.ctx.drawImage(video, rect.x, rect.y, rect.width, rect.height, 0, 0, this.inputSize, this.inputSize);\n      }\n      if (mirror) this.ctx.restore();\n      const image = this.ctx.getImageData(0, 0, this.inputSize, this.inputSize);\n      const pixels = image.data;\n      const planeSize = this.inputSize * this.inputSize;\n      const data = new Float32Array(planeSize * 3);\n\n      for (let index = 0; index < planeSize; index += 1) {\n        const pixelIndex = index * 4;\n        const r = pixels[pixelIndex];\n        const g = pixels[pixelIndex + 1];\n        const b = pixels[pixelIndex + 2];\n\n        if (this.processor === 'googlenet') {\n          data[index] = b - AGE_GOOGLE_NET_MEAN_BGR[0];\n          data[planeSize + index] = g - AGE_GOOGLE_NET_MEAN_BGR[1];\n          data[planeSize * 2 + index] = r - AGE_GOOGLE_NET_MEAN_BGR[2];\n        } else if (this.processor === 'genderage') {\n          // InsightFace genderage: raw RGB 0-255, NCHW (swapRB handled by reading RGB order)\n          data[index] = r;\n          data[planeSize + index] = g;\n          data[planeSize * 2 + index] = b;\n        } else {\n          data[index] = (r / 255 - AGE_MODEL_MEAN[0]) / AGE_MODEL_STD[0];\n          data[planeSize + index] = (g / 255 - AGE_MODEL_MEAN[1]) / AGE_MODEL_STD[1];\n          data[planeSize * 2 + index] = (b / 255 - AGE_MODEL_MEAN[2]) / AGE_MODEL_STD[2];\n        }\n      }\n\n      return new window.ort.Tensor('float32', data, [1, 3, this.inputSize, this.inputSize]);\n    }\n\n    sourceRect(video, box) {\n      const videoWidth = video.videoWidth;\n      const videoHeight = video.videoHeight;\n      if (!videoWidth || !videoHeight) return null;\n\n      const centerX = (box.x + box.width / 2) * videoWidth;\n      const centerY = (box.y + box.height / 2) * videoHeight;\n      if (this.processor === 'genderage') {\n        // Reproduce the InsightFace reference framing (attribute.py Attribute.get():\n        // source square = 1.5 \u00D7 max(w,h) of the detector box, face centered, out-of-\n        // frame black-padded, rotate=0). Our landmark box already carries ~+16% pad,\n        // so 1.3 \u00D7 recovers the training-time margin. NO clamping \u2014 createInputTensor\n        // black-pads the out-of-bounds region like warpAffine borderValue=0.\n        const side = Math.max(32, 1.3 * Math.max(box.width * videoWidth, box.height * videoHeight));\n        return { x: centerX - side / 2, y: centerY - side / 2, width: side, height: side, pad: true };\n      }\n      const targetSize = Math.max(box.width * videoWidth * 1.36, box.height * videoHeight * 1.18);\n      const size = Math.min(Math.max(32, targetSize), videoWidth, videoHeight);\n      const x = clamp(centerX - size / 2, 0, Math.max(0, videoWidth - size));\n      const y = clamp(centerY - size / 2, 0, Math.max(0, videoHeight - size));\n      return { x, y, width: size, height: size };\n    }\n\n    parseOutput(outputs) {\n      const tensor = outputs[this.outputName] || outputs[Object.keys(outputs)[0]];\n      if (!tensor || !tensor.data || !tensor.data.length) return null;\n      const values = Array.from(tensor.data);\n\n      // InsightFace genderage head: [genderLogit0, genderLogit1, age]; age is a 0..1 fraction \u2192 *100.\n      if (this.processor === 'genderage' && values.length >= 3) {\n        let raw = Number(values[values.length - 1]);\n        if (!Number.isFinite(raw)) return null;\n        if (raw > 0 && raw <= 1.5) raw *= 100;\n        return this.rangeFromAge(clamp(Math.round(raw), 13, 95), 2, 2);\n      }\n\n      if (values.length === AGE_BINS.length) return this.parseAgeBins(values);\n\n      const rawAge = Number(values[0]);\n      if (!Number.isFinite(rawAge)) return null;\n      const age = clamp(Math.round(rawAge), 13, 95);\n      return this.rangeFromAge(age, 3, 3);\n    }\n\n    // Softmax EXPECTATION over bin midpoints (not argmax) so the age is continuous and\n    // face-dependent instead of snapping to a single bin (the \"always 29\" bug).\n    parseAgeBins(values) {\n      const sum = values.reduce((total, value) => total + value, 0);\n      const alreadyProbabilities = values.every((value) => value >= 0 && value <= 1) && Math.abs(sum - 1) < 0.04;\n      const probabilities = alreadyProbabilities ? values : softmax(values);\n      const mids = AGE_BINS.map((bin) => (bin[0] + bin[1]) / 2);\n      let expected = 0;\n      probabilities.forEach((probability, index) => { expected += (mids[index] || 0) * probability; });\n      let variance = 0;\n      probabilities.forEach((probability, index) => {\n        const delta = (mids[index] || 0) - expected;\n        variance += delta * delta * probability;\n      });\n      const spread = Math.sqrt(Math.max(0, variance));\n      let topIndex = 0;\n      probabilities.forEach((probability, index) => { if (probability > probabilities[topIndex]) topIndex = index; });\n      return {\n        ageLow: Math.max(18, Math.round(expected - spread * 0.5)),\n        ageHigh: Math.max(24, Math.round(expected + spread * 0.5)),\n        confidence: probabilities[topIndex],\n        source: 'onnx-bin-expectation',\n      };\n    }\n\n    rangeFromAge(age, lowSpread, highSpread) {\n      return {\n        ageLow: Math.max(18, Math.round(age - lowSpread)),\n        ageHigh: Math.min(99, Math.round(age + highSpread)),\n        confidence: null,\n        source: 'onnx-regression',\n      };\n    }\n\n    // Raw genderage head: [genderLogit0, genderLogit1, age]; age is a 0..1 fraction \u2192 \u00D7100.\n    rawGenderageAge(outputs) {\n      const tensor = outputs[this.outputName] || outputs[Object.keys(outputs)[0]];\n      if (!tensor || !tensor.data || tensor.data.length < 3) return NaN;\n      let raw = Number(tensor.data[tensor.data.length - 1]);\n      if (!Number.isFinite(raw)) return NaN;\n      if (raw > 0 && raw <= 1.5) raw *= 100;\n      return raw;\n    }\n\n    /* Age calibration hook. genderage regresses toward its training mean (~38):\n       young adults read high, elderly read low. A DETERMINISTIC shift is left OFF\n       (k=0) on purpose: the raw error is non-uniform (measured young faces ranged\n       0 to +5y), so a blanket young-tilt would make already-accurate faces read\n       too young and skew friend duels. Consistency is handled honestly by TTA +\n       median instead. To enable a deliberate \"younger\" tilt for the funnel (a\n       product choice, not an accuracy claim), raise `k` (e.g. 0.3, pivot 44,\n       cap 3): display = raw \u2212 min(cap, k\u00B7(pivot\u2212raw)) for raw < pivot. */\n    calibrateAge(raw) {\n      // Owner-chosen gentle young-tilt for the funnel (2026-07-03): faces under 44\n      // read up to 3y younger (35\u2192~32). Applied to EVERY face, so friend duels stay\n      // fair (relative order preserved within a cohort). Set k=0 to return to raw.\n      var k = 0.3, pivot = 44, cap = 3;\n      if (!k || !Number.isFinite(raw) || raw >= pivot) return raw;\n      return raw - Math.min(cap, k * (pivot - raw));\n    }\n  }\n\n  class SkinParser {\n    constructor(config) {\n      this.config = config || {};\n      this.modelUrl = this.normalizeAssetUrl(this.config.skinModelUrl);\n      this.modelVersion = String(this.config.skinModelVersion || 'bisenet-resnet18-celebamaskhq-v1');\n      this.wasmRootUrl = this.normalizeAssetUrl(this.config.ortWasmRootUrl);\n      this.inputSize = Number(this.config.skinInputSize || SKIN_MODEL_DEFAULT_SIZE);\n      this.requested = this.config.skinModelEnabled !== false;\n      this.enabled = this.requested && !!this.modelUrl;\n      this.session = null;\n      this.inputName = '';\n      this.outputName = '';\n      this.loading = false;\n      this.ready = false;\n      this.failed = this.requested && !this.modelUrl;\n      this.running = false;\n      this.canvas = document.createElement('canvas');\n      this.canvas.width = this.inputSize;\n      this.canvas.height = this.inputSize;\n      this.ctx = this.canvas.getContext('2d', { willReadFrequently: true });\n    }\n\n    normalizeAssetUrl(url) {\n      const value = String(url || '').trim();\n      if (value.startsWith('//')) return `https:${value}`;\n      return value;\n    }\n\n    async load() {\n      if (!this.enabled || this.ready || this.loading) return this.ready;\n      this.loading = true;\n      this.failed = false;\n\n      try {\n        if (!window.ort) throw new Error('ONNX Runtime Web is not loaded');\n        const wasmRoot = this.wasmRootUrl && this.wasmRootUrl.endsWith('/') ? this.wasmRootUrl : `${this.wasmRootUrl}/`;\n        if (this.wasmRootUrl) window.ort.env.wasm.wasmPaths = wasmRoot;\n        window.ort.env.wasm.numThreads = 1;\n        window.ort.env.wasm.simd = true;\n\n        this.session = await window.ort.InferenceSession.create(this.modelUrl, {\n          executionProviders: ['wasm'],\n          graphOptimizationLevel: 'all',\n        });\n        this.inputName = this.session.inputNames[0] || 'input';\n        this.outputName = this.session.outputNames[0] || 'output';\n        this.ready = true;\n        return true;\n      } catch (error) {\n        console.warn('[FaceScan] Skin model could not be loaded.', error);\n        this.failed = true;\n        this.ready = false;\n        return false;\n      } finally {\n        this.loading = false;\n      }\n    }\n\n    async parse(video, box) {\n      if (!this.ready || this.running || !this.session || !video || !box) return null;\n      this.running = true;\n      try {\n        const tensor = this.createInputTensor(video, box);\n        if (!tensor) return null;\n        const outputs = await this.session.run({ [this.inputName]: tensor });\n        return this.parseOutput(outputs);\n      } catch (error) {\n        console.warn('[FaceScan] Skin model inference failed.', error);\n        this.failed = true;\n        this.ready = false;\n        return null;\n      } finally {\n        this.running = false;\n      }\n    }\n\n    createInputTensor(video, box) {\n      const rect = this.sourceRect(video, box);\n      if (!rect || !this.ctx || !window.ort) return null;\n\n      this.ctx.clearRect(0, 0, this.inputSize, this.inputSize);\n      this.ctx.drawImage(video, rect.x, rect.y, rect.width, rect.height, 0, 0, this.inputSize, this.inputSize);\n      const image = this.ctx.getImageData(0, 0, this.inputSize, this.inputSize);\n      const pixels = image.data;\n      const planeSize = this.inputSize * this.inputSize;\n      const data = new Float32Array(planeSize * 3);\n\n      for (let index = 0; index < planeSize; index += 1) {\n        const pixelIndex = index * 4;\n        const r = pixels[pixelIndex] / 255;\n        const g = pixels[pixelIndex + 1] / 255;\n        const b = pixels[pixelIndex + 2] / 255;\n        data[index] = (r - AGE_MODEL_MEAN[0]) / AGE_MODEL_STD[0];\n        data[planeSize + index] = (g - AGE_MODEL_MEAN[1]) / AGE_MODEL_STD[1];\n        data[planeSize * 2 + index] = (b - AGE_MODEL_MEAN[2]) / AGE_MODEL_STD[2];\n      }\n\n      return new window.ort.Tensor('float32', data, [1, 3, this.inputSize, this.inputSize]);\n    }\n\n    sourceRect(video, box) {\n      const videoWidth = video.videoWidth;\n      const videoHeight = video.videoHeight;\n      if (!videoWidth || !videoHeight) return null;\n\n      const x = clamp(Math.floor(box.x * videoWidth), 0, videoWidth - 1);\n      const y = clamp(Math.floor(box.y * videoHeight), 0, videoHeight - 1);\n      const width = clamp(Math.floor(box.width * videoWidth), 1, videoWidth - x);\n      const height = clamp(Math.floor(box.height * videoHeight), 1, videoHeight - y);\n      return { x, y, width, height };\n    }\n\n    parseOutput(outputs) {\n      const tensor = outputs[this.outputName] || outputs[Object.keys(outputs)[0]];\n      if (!tensor || !tensor.data || !tensor.data.length) return null;\n      const dims = tensor.dims || [];\n      const values = tensor.data;\n      if (dims.length === 3) return this.maskFromClassMap(values, dims[1], dims[2]);\n      if (dims.length !== 4) return null;\n\n      const shape = dims.map((value) => Number(value));\n      if (shape[1] > 1 && shape[2] > 8 && shape[3] > 8) {\n        return this.maskFromNchw(values, shape[1], shape[2], shape[3]);\n      }\n      if (shape[3] > 1 && shape[1] > 8 && shape[2] > 8) {\n        return this.maskFromNhwc(values, shape[1], shape[2], shape[3]);\n      }\n      return null;\n    }\n\n    maskFromClassMap(values, height, width) {\n      const mask = new Uint8Array(width * height);\n      let skinPixels = 0;\n      for (let index = 0; index < mask.length; index += 1) {\n        const classId = Math.round(Number(values[index] || 0));\n        if (SKIN_CLASS_IDS.has(classId)) {\n          mask[index] = 1;\n          skinPixels += 1;\n        }\n      }\n      return this.result(mask, width, height, skinPixels);\n    }\n\n    maskFromNchw(values, classes, height, width) {\n      const mask = new Uint8Array(width * height);\n      let skinPixels = 0;\n      const planeSize = height * width;\n      for (let index = 0; index < planeSize; index += 1) {\n        let bestClass = 0;\n        let bestScore = -Infinity;\n        for (let classIndex = 0; classIndex < classes; classIndex += 1) {\n          const score = Number(values[classIndex * planeSize + index]);\n          if (score > bestScore) {\n            bestScore = score;\n            bestClass = classIndex;\n          }\n        }\n        if (SKIN_CLASS_IDS.has(bestClass)) {\n          mask[index] = 1;\n          skinPixels += 1;\n        }\n      }\n      return this.result(mask, width, height, skinPixels);\n    }\n\n    maskFromNhwc(values, height, width, classes) {\n      const mask = new Uint8Array(width * height);\n      let skinPixels = 0;\n      for (let index = 0; index < width * height; index += 1) {\n        let bestClass = 0;\n        let bestScore = -Infinity;\n        const offset = index * classes;\n        for (let classIndex = 0; classIndex < classes; classIndex += 1) {\n          const score = Number(values[offset + classIndex]);\n          if (score > bestScore) {\n            bestScore = score;\n            bestClass = classIndex;\n          }\n        }\n        if (SKIN_CLASS_IDS.has(bestClass)) {\n          mask[index] = 1;\n          skinPixels += 1;\n        }\n      }\n      return this.result(mask, width, height, skinPixels);\n    }\n\n    result(mask, width, height, skinPixels) {\n      const ratio = skinPixels / Math.max(width * height, 1);\n      if (!mask.length || ratio < 0.12) return null;\n      return {\n        mask,\n        width,\n        height,\n        skinRatio: ratio,\n        source: 'bisenet-face-parsing',\n      };\n    }\n  }\n\n  class FaceScanLanding {\n    constructor(root) {\n      this.root = root;\n      this.config = this.readConfig();\n      if (document.body) document.body.classList.add('face-scan-app-shell');\n      this.video = root.querySelector('[data-face-scan-video]');\n      this.canvas = root.querySelector('[data-face-scan-canvas]');\n      this.ctx = this.canvas.getContext('2d', { alpha: true });\n      this.modal = root.querySelector('[data-face-scan-modal]');\n      this.form = root.querySelector('[data-face-scan-form]');\n      this.error = root.querySelector('[data-face-scan-error]');\n      this.cameraBlock = root.querySelector('[data-face-scan-camera-block]');\n      this.startPanel = root.querySelector('[data-face-scan-start-panel]');\n      this.startButton = root.querySelector('[data-face-scan-start]');\n      this.results = root.querySelector('[data-face-scan-results]');\n      this.cta = root.querySelector('[data-face-scan-cta]');\n      this.sharePanel = root.querySelector('[data-face-scan-share-panel]');\n      this.shareButton = root.querySelector('[data-face-scan-share]');\n      this.shareStatus = root.querySelector('[data-face-scan-share-status]');\n      this.pulsePanel = root.querySelector('[data-face-scan-pulse-panel]');\n      this.pulseButton = root.querySelector('[data-face-scan-pulse]');\n      this.pulseStatus = root.querySelector('[data-face-scan-pulse-status]');\n      this.vitalsResult = root.querySelector('[data-face-scan-vitals-result]');\n      this.vitalsOutput = {\n        pulse: root.querySelector('[data-face-scan-pulse-bpm]'),\n        balance: root.querySelector('[data-face-scan-balance-score]'),\n        signal: root.querySelector('[data-face-scan-signal-quality]'),\n      };\n      this.ctaLinks = Array.from(root.querySelectorAll('[data-face-scan-cta-link]'));\n      this.cameraStatus = root.querySelector('[data-face-scan-camera-status]');\n      this.engineStatus = root.querySelector('[data-face-scan-engine-status]');\n      this.qualityStatus = root.querySelector('[data-face-scan-quality-status]');\n      this.appStatus = root.querySelector('[data-face-scan-app-status]');\n      this.appQuality = root.querySelector('[data-face-scan-app-quality]');\n      this.progressBar = root.querySelector('[data-face-scan-progress]');\n      this.resultState = root.querySelector('[data-face-scan-result-state]');\n      this.infoSheet = root.querySelector('[data-face-scan-info-sheet]');\n      this.infoTitle = root.querySelector('[data-face-scan-info-title]');\n      this.infoCopy = root.querySelector('[data-face-scan-info-copy]');\n      this.infoButtons = Array.from(root.querySelectorAll('[data-face-scan-tooltip]'));\n      this.infoCloseButtons = Array.from(root.querySelectorAll('[data-face-scan-info-close]'));\n      this.output = {\n        age: root.querySelector('[data-face-scan-age]'),\n        ageNote: root.querySelector('[data-face-scan-age-note]'),\n        vitality: root.querySelector('[data-face-scan-vitality]'),\n        vitalityNote: root.querySelector('[data-face-scan-vitality-note]'),\n        glow: root.querySelector('[data-face-scan-glow]'),\n        glowNote: root.querySelector('[data-face-scan-glow-note]'),\n        tone: root.querySelector('[data-face-scan-tone]'),\n        toneNote: root.querySelector('[data-face-scan-tone-note]'),\n        texture: root.querySelector('[data-face-scan-texture]'),\n        textureNote: root.querySelector('[data-face-scan-texture-note]'),\n        light: root.querySelector('[data-face-scan-light]'),\n      };\n\n      this.stream = null;\n      this.faceLandmarker = null;\n      this.faceDetector = null;\n      this.mediaPipeReady = false;\n      this.usingFallback = false;\n      this.raf = 0;\n      this.lastVideoTime = -1;\n      this.lastAnalysisAt = 0;\n      this.lastSkinParsedAt = 0;\n      this.lastFallbackAt = 0;\n      this.analysisBusy = false;\n      this.cameraStarting = false;\n      this.cameraPromptTimer = 0;\n      this.latestFrame = null;\n      this.previousFrameBox = null;\n      this._stashCanvas = null;      // reused offscreen frame \u2192 the cloud age capture (FIX A)\n      this.lastGoodFrameAt = 0;      // when the last quality-passing frame was stashed\n      this._lastSampleBox = null;    // box of the last sampled frame \u2192 face-switch detection (FIX B)\n      this._lastSampleAt = 0;        // when the last frame was sampled \u2192 long-absence reset (FIX B)\n      this._framedAt = 0;            // when a well-framed+lit face was first held \u2192 pre-scan beat\n      this.samples = [];\n      this.resultLocked = false;\n      this.ageTrace = [];\n      this.analyzedCount = 0;\n      this.firstSampleAt = 0;\n      this.hasVisibleResult = false;\n      this.unlocked = this.isUnlocked();\n      this.stableFrames = 0;\n      this.pulseRunning = false;\n      this.pulseResult = null;\n      this.ageEstimator = new AgeEstimator(this.config);\n      this.skinParser = new SkinParser(this.config);\n      this.latestSkinMask = null;\n      this.sampleCanvas = document.createElement('canvas');\n      this.sampleCanvas.width = 128;\n      this.sampleCanvas.height = 128;\n      this.sampleCtx = this.sampleCanvas.getContext('2d', { willReadFrequently: true });\n      this.pulseCanvas = document.createElement('canvas');\n      this.pulseCanvas.width = PULSE_CANVAS_SIZE;\n      this.pulseCanvas.height = PULSE_CANVAS_SIZE;\n      this.pulseCtx = this.pulseCanvas.getContext('2d', { willReadFrequently: true });\n\n      this.handleResize = this.resizeCanvas.bind(this);\n      this.handleVisibility = this.handleVisibilityChange.bind(this);\n      this.handleBeforeUnload = this.stopCamera.bind(this);\n\n      this.bindForm();\n      this.bindShare();\n      this.bindPulse();\n      this.bindInfoSheets();\n      this.bindCtas();\n      this.init();\n    }\n\n    readConfig() {\n      const script = this.root.querySelector('[data-face-scan-config]');\n      if (!script) return { labels: {} };\n      try {\n        return JSON.parse(script.textContent || '{}');\n      } catch (error) {\n        return { labels: {} };\n      }\n    }\n\n    label(key, fallback) {\n      return (this.config.labels && this.config.labels[key]) || fallback || '';\n    }\n\n    async init() {\n      this.setCameraStatus(this.label('cameraStarting', 'Starting camera'));\n      this.setEngineStatus(this.label('scannerLoading', 'Loading scanner'));\n      this.setLockedState(!this.unlocked);\n      this.trackReferralStarted();\n\n      window.addEventListener('resize', this.handleResize);\n      document.addEventListener('visibilitychange', this.handleVisibility);\n      window.addEventListener('beforeunload', this.handleBeforeUnload);\n\n      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n        this.handleCameraError(this.label('cameraUnsupported', 'Camera not supported'));\n        return;\n      }\n\n      this.bindCameraStart();\n      this.startCamera();\n    }\n\n    bindCameraStart() {\n      if (!this.startButton || this.startButton.__faceScanBound) return;\n      this.startButton.__faceScanBound = true;\n      this.startButton.addEventListener('click', () => this.startCamera(true));\n    }\n\n    async startCamera(fromUserGesture = false) {\n      if (this.stream || this.root.dataset.camera === 'live') {\n        this.hideStartPanel();\n        return;\n      }\n      if (this.cameraStarting) return;\n      this.cameraStarting = true;\n      this.hideStartPanel();\n      if (this.cameraBlock) this.cameraBlock.hidden = true;\n      if (this.startButton) this.startButton.disabled = true;\n      this.setCameraStatus(this.label('cameraStarting', 'Starting camera'));\n\n      window.clearTimeout(this.cameraPromptTimer);\n      this.cameraPromptTimer = window.setTimeout(() => {\n        if (!this.stream && this.root.dataset.camera !== 'live') this.showStartPanel();\n      }, fromUserGesture ? 5000 : 1800);\n\n      try {\n        this.stream = await this.requestCameraStream();\n        this.video.srcObject = this.stream;\n        await this.playVideo();\n        await this.waitForVideo();\n        this.root.dataset.camera = 'live';\n        this.hideStartPanel();\n        this.setCameraStatus(this.label('cameraLive', 'Live'));\n        this.resizeCanvas();\n        this.startLoop();\n        this.initMediaPipe();\n        this.initAgeEstimator();\n        this.initSkinParser();\n        if (!this.unlocked) {\n          window.setTimeout(() => this.showModal(), 850);\n        }\n      } catch (error) {\n        if (!fromUserGesture && this.isUserGestureRecoverable(error)) {\n          this.showStartPanel();\n          this.setCameraStatus(this.label('cameraStartButton', 'Start camera'));\n        } else {\n          this.handleCameraError(this.label('cameraDenied', 'Camera blocked'));\n        }\n      } finally {\n        window.clearTimeout(this.cameraPromptTimer);\n        this.cameraStarting = false;\n        if (this.startButton) this.startButton.disabled = false;\n      }\n    }\n\n    showStartPanel() {\n      if (this.root.dataset.camera === 'live' || !this.startPanel) return;\n      this.startPanel.hidden = false;\n    }\n\n    hideStartPanel() {\n      if (this.startPanel) this.startPanel.hidden = true;\n    }\n\n    isUserGestureRecoverable(error) {\n      return error && ['AbortError', 'NotAllowedError', 'SecurityError'].includes(error.name);\n    }\n\n    async requestCameraStream() {\n      const preferred = {\n        video: {\n          facingMode: { ideal: 'user' },\n          width: { ideal: 1280 },\n          height: { ideal: 720 },\n        },\n        audio: false,\n      };\n\n      try {\n        return await navigator.mediaDevices.getUserMedia(preferred);\n      } catch (error) {\n        if (!this.shouldRetryCamera(error)) throw error;\n        return navigator.mediaDevices.getUserMedia({\n          video: true,\n          audio: false,\n        });\n      }\n    }\n\n    shouldRetryCamera(error) {\n      return error && ['OverconstrainedError', 'ConstraintNotSatisfiedError', 'NotFoundError'].includes(error.name);\n    }\n\n    async playVideo() {\n      if (!this.video || typeof this.video.play !== 'function') return;\n      try {\n        await this.video.play();\n      } catch (error) {\n        // Some browsers resolve metadata before play() succeeds; waitForVideo handles the final readiness check.\n      }\n    }\n\n    waitForVideo() {\n      return new Promise((resolve, reject) => {\n        if (this.video.readyState >= 2 && this.video.videoWidth) {\n          resolve();\n          return;\n        }\n        let timer = 0;\n        const cleanup = () => {\n          window.clearTimeout(timer);\n          this.video.removeEventListener('loadedmetadata', onReady);\n          this.video.removeEventListener('loadeddata', onReady);\n          this.video.removeEventListener('canplay', onReady);\n          this.video.removeEventListener('error', onError);\n        };\n        const onReady = () => {\n          if (!this.video.videoWidth) return;\n          cleanup();\n          resolve();\n        };\n        const onError = () => {\n          cleanup();\n          reject(new Error('Video failed to start'));\n        };\n        timer = window.setTimeout(() => {\n          cleanup();\n          reject(new Error('Video readiness timed out'));\n        }, VIDEO_READY_TIMEOUT);\n        this.video.addEventListener('loadedmetadata', onReady, { once: true });\n        this.video.addEventListener('loadeddata', onReady, { once: true });\n        this.video.addEventListener('canplay', onReady, { once: true });\n        this.video.addEventListener('error', onError, { once: true });\n      });\n    }\n\n    async initMediaPipe() {\n      try {\n        const moduleUrl = this.normalizeAssetUrl(this.config.visionBundleUrl);\n        const wasmRootUrl = this.normalizeAssetUrl(this.config.wasmRootUrl);\n        const modelUrl = this.normalizeAssetUrl(this.config.landmarkerModelUrl);\n        if (!moduleUrl || !wasmRootUrl || !modelUrl) throw new Error('Missing MediaPipe assets');\n\n        const visionModule = await import(moduleUrl);\n        const vision = await visionModule.FilesetResolver.forVisionTasks(wasmRootUrl);\n        this.faceLandmarker = await visionModule.FaceLandmarker.createFromOptions(vision, {\n          baseOptions: {\n            modelAssetPath: modelUrl,\n            delegate: 'CPU',\n          },\n          runningMode: 'VIDEO',\n          numFaces: 2,\n          minFaceDetectionConfidence: 0.55,\n          minFacePresenceConfidence: 0.55,\n          minTrackingConfidence: 0.55,\n        });\n        this.mediaPipeReady = true;\n        this.syncEngineStatus();\n      } catch (error) {\n        this.mediaPipeReady = false;\n        this.usingFallback = true;\n        this.initFallbackDetector();\n        this.syncEngineStatus();\n      }\n    }\n\n    async initAgeEstimator() {\n      if (!this.ageEstimator || !this.ageEstimator.enabled) {\n        this.syncEngineStatus();\n        return;\n      }\n      const load = this.ageEstimator.load();\n      this.syncEngineStatus();\n      await load;\n      this.syncEngineStatus();\n    }\n\n    async initSkinParser() {\n      if (!this.skinParser || !this.skinParser.enabled) {\n        this.syncEngineStatus();\n        return;\n      }\n      const load = this.skinParser.load();\n      this.syncEngineStatus();\n      await load;\n      this.syncEngineStatus();\n    }\n\n    syncEngineStatus() {\n      if (this.skinParser && this.skinParser.loading) {\n        this.setEngineStatus(this.label('skinModelLoading', 'Loading skin model'));\n        return;\n      }\n      if (this.ageEstimator && this.ageEstimator.loading) {\n        this.setEngineStatus(this.label('ageModelLoading', 'Loading age model'));\n        return;\n      }\n      if (this.skinParser && this.skinParser.ready) {\n        this.setEngineStatus(this.label('skinModelReady', 'Skin model active'));\n        return;\n      }\n      if (this.mediaPipeReady) {\n        this.setEngineStatus(this.label('skinRoiReady', 'Skin scan active'));\n        return;\n      }\n      if (this.skinParser && this.skinParser.failed) {\n        this.setEngineStatus(this.label('skinModelFallback', 'Skin model unavailable'));\n        return;\n      }\n      if (this.ageEstimator && this.ageEstimator.ready) {\n        this.setEngineStatus(this.label('ageModelReady', 'Age model active'));\n        return;\n      }\n      if (this.ageEstimator && this.ageEstimator.failed) {\n        this.setEngineStatus(this.label('ageModelFallback', 'Age model unavailable'));\n        return;\n      }\n      if (this.usingFallback) {\n        this.setEngineStatus(this.label('scannerFallback', 'Local fallback'));\n        return;\n      }\n      this.setEngineStatus(this.label('scannerLoading', 'Loading scanner'));\n    }\n\n    normalizeAssetUrl(url) {\n      const value = String(url || '');\n      if (value.startsWith('//')) return `https:${value}`;\n      return value;\n    }\n\n    initFallbackDetector() {\n      if ('FaceDetector' in window) {\n        try {\n          this.faceDetector = new window.FaceDetector({\n            fastMode: true,\n            maxDetectedFaces: 2,\n          });\n        } catch (error) {\n          this.faceDetector = null;\n        }\n      }\n    }\n\n    handleCameraError(message) {\n      this.root.dataset.camera = 'blocked';\n      this.setCameraStatus(message);\n      this.setEngineStatus(this.label('scannerFallback', 'Unavailable'));\n      if (this.cameraBlock) this.cameraBlock.hidden = false;\n      if (this.modal) this.modal.hidden = true;\n      this.updateShareState();\n    }\n\n    bindForm() {\n      if (!this.form) return;\n      this.form.addEventListener('submit', (event) => {\n        event.preventDefault();\n        this.submitLead();\n      });\n    }\n\n    bindShare() {\n      if (!this.shareButton || this.shareButton.__faceScanBound) return;\n      this.shareButton.__faceScanBound = true;\n      this.shareButton.addEventListener('click', () => this.shareResult());\n    }\n\n    bindPulse() {\n      if (!this.pulseButton || this.pulseButton.__faceScanBound) return;\n      this.pulseButton.__faceScanBound = true;\n      this.pulseButton.addEventListener('click', () => this.measurePulse());\n    }\n\n    bindInfoSheets() {\n      this.infoButtons.forEach((button) => {\n        if (button.__faceScanBound) return;\n        button.__faceScanBound = true;\n        button.addEventListener('click', (event) => {\n          event.preventDefault();\n          event.stopPropagation();\n          const title = button.closest('.face-scan-landing__metric-label, span')?.textContent\n            ?.replace(/\\bi\\b/g, '')\n            .trim() || this.label('scoreGuideTitle', 'Score');\n          this.showInfoSheet(title, button.getAttribute('data-face-scan-tooltip') || '');\n        });\n      });\n      this.infoCloseButtons.forEach((button) => {\n        if (button.__faceScanBound) return;\n        button.__faceScanBound = true;\n        button.addEventListener('click', () => this.hideInfoSheet());\n      });\n      document.addEventListener('keydown', (event) => {\n        if (event.key === 'Escape') this.hideInfoSheet();\n      });\n    }\n\n    showInfoSheet(title, copy) {\n      if (!this.infoSheet) return;\n      if (this.infoTitle) this.infoTitle.textContent = title || this.label('scoreGuideTitle', 'Score');\n      if (this.infoCopy) this.infoCopy.textContent = copy || '';\n      this.infoSheet.hidden = false;\n    }\n\n    hideInfoSheet() {\n      if (this.infoSheet) this.infoSheet.hidden = true;\n    }\n\n    bindCtas() {\n      this.ctaLinks.forEach((link) => {\n        if (link.__faceScanBound) return;\n        link.__faceScanBound = true;\n        link.addEventListener('click', () => {\n          this.trackCtaClick(link.getAttribute('data-face-scan-cta-link') || 'unknown');\n        });\n      });\n    }\n\n    showModal(force = false) {\n      if ((!force && this.unlocked) || this.root.dataset.camera !== 'live' || !this.modal) return;\n      this.modal.hidden = false;\n      const firstField = this.modal.querySelector('input[name=\"first_name\"]');\n      if (firstField) firstField.focus({ preventScroll: true });\n    }\n\n    hideModal() {\n      if (this.modal) this.modal.hidden = true;\n    }\n\n    async submitLead() {\n      if (!this.form) return;\n      const submit = this.form.querySelector('button[type=\"submit\"]');\n      const firstName = String(this.form.elements.first_name.value || '').trim();\n      const email = String(this.form.elements.email.value || '').trim();\n      const privacy = !!this.form.elements.privacy.checked;\n      const newsletter = !!this.form.elements.newsletter.checked;\n\n      if (!firstName) {\n        this.showError(this.label('errorName', 'Please enter your name.'));\n        return;\n      }\n      if (!this.isValidEmail(email)) {\n        this.showError(this.label('errorEmail', 'Please enter a valid email address.'));\n        return;\n      }\n      if (!privacy) {\n        this.showError(this.label('errorPrivacy', 'Please accept the privacy note.'));\n        return;\n      }\n      this.showError('');\n      if (submit) {\n        submit.disabled = true;\n        submit.textContent = this.label('submitting', 'Submitting');\n      }\n\n      try {\n        this.trackLead(firstName, email, newsletter);\n        /* List subscription sets marketing consent in Klaviyo \u2014 only allowed\n           when the newsletter box was ticked. Without it the lead is recorded\n           via trackLead (profile, no $consent) and the scan unlocks anyway. */\n        if (newsletter) {\n          await this.subscribeLead(firstName, email, newsletter);\n        }\n        this.markUnlocked();\n        this.hideModal();\n        this.setLockedState(false);\n      } catch (error) {\n        this.showError(error && error.message ? error.message : this.label('errorSubmit', 'Something went wrong. Please try again.'));\n      } finally {\n        if (submit) {\n          submit.disabled = false;\n          submit.textContent = this.label('submit', 'Show analysis');\n        }\n      }\n    }\n\n    trackLead(firstName, email, newsletter) {\n      try {\n        if (!window._learnq) return false;\n        const ageModelReady = !!(this.ageEstimator && this.ageEstimator.ready);\n        const ageModelVersion = this.ageEstimator ? this.ageEstimator.modelVersion : this.config.ageModelVersion || '';\n        const referralCode = this.getReferralCode();\n        const referredBy = this.getReferredByCode();\n        const ident = {\n          '$email': email,\n          '$first_name': firstName,\n          source: 'face_scan_landing',\n          scan_unlocked: true,\n          newsletter_optin: newsletter,\n          face_scan_tag: this.config.klaviyoTag || 'face-scan-lead',\n          face_scan_age_model_ready: ageModelReady,\n          face_scan_age_model_version: ageModelVersion,\n          face_scan_referral_code: referralCode,\n          face_scan_referred_by: referredBy,\n        };\n        if (newsletter) ident.$consent = ['email'];\n        window._learnq.push(['identify', ident]);\n        window._learnq.push(['track', 'Face Scan Lead', {\n          source: 'face_scan_landing',\n          scan_unlocked: true,\n          newsletter_optin: newsletter,\n          tag: this.config.klaviyoTag || 'face-scan-lead',\n          locale: this.config.locale || '',\n          url: window.location.href,\n          age_model_ready: ageModelReady,\n          age_model_version: ageModelVersion,\n          referral_code: referralCode,\n          referred_by: referredBy,\n        }]);\n        if (referredBy) {\n          window._learnq.push(['track', 'Face Scan Referral Unlocked', {\n            source: 'face_scan_landing',\n            referral_code: referralCode,\n            referred_by: referredBy,\n            locale: this.config.locale || '',\n            url: window.location.href,\n          }]);\n        }\n        return true;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    subscribeLead(firstName, email, newsletter) {\n      return new Promise((resolve, reject) => {\n        const listId = String(this.config.klaviyoListId || '').trim();\n        if (!listId) {\n          if (window._learnq) {\n            resolve();\n            return;\n          }\n          reject(new Error(this.label('errorConfig', 'Lead capture is not configured.')));\n          return;\n        }\n\n        const body = new URLSearchParams();\n        body.set('g', listId);\n        body.set('email', email);\n        body.set('$first_name', firstName);\n        body.set('first_name', firstName);\n        body.set('source', 'face_scan_landing');\n        body.set('tag', this.config.klaviyoTag || 'face-scan-lead');\n        body.set('scan_unlocked', 'true');\n        body.set('newsletter_optin', newsletter ? 'true' : 'false');\n        body.set('locale', this.config.locale || '');\n        body.set('face_scan_referral_code', this.getReferralCode());\n        body.set('face_scan_referred_by', this.getReferredByCode());\n        body.set('$fields', '$first_name,first_name,source,tag,scan_unlocked,newsletter_optin,locale,face_scan_referral_code,face_scan_referred_by');\n\n        const xhr = new XMLHttpRequest();\n        xhr.open('POST', 'https://a.klaviyo.com/ajax/subscriptions/subscribe', true);\n        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n        xhr.onreadystatechange = () => {\n          if (xhr.readyState !== 4) return;\n          if (xhr.status >= 200 && xhr.status < 300) {\n            resolve();\n          } else {\n            reject(new Error(this.label('errorSubmit', 'Something went wrong. Please try again.')));\n          }\n        };\n        xhr.onerror = () => reject(new Error(this.label('errorSubmit', 'Something went wrong. Please try again.')));\n        xhr.send(body.toString());\n      });\n    }\n\n    async shareResult() {\n      if (!this.unlocked) {\n        this.showModal();\n        return;\n      }\n      if (!this.hasVisibleResult) {\n        this.setShareStatus(this.label('shareWait', 'Wait until the live result is ready.'));\n        return;\n      }\n\n      const originalText = this.shareButton ? this.shareButton.textContent : '';\n      if (this.shareButton) {\n        this.shareButton.disabled = true;\n        this.shareButton.textContent = this.label('sharePreparing', 'Preparing share image');\n      }\n      this.setShareStatus(this.label('sharePreparing', 'Preparing share image'));\n\n      try {\n        const uploadBlob = await this.createShareImage();\n        if (this.shareButton) this.shareButton.textContent = this.label('shareUploading', 'Uploading snapshot');\n        this.setShareStatus(this.label('shareUploading', 'Uploading snapshot'));\n        const result = await this.uploadShareResult(uploadBlob);\n        const shareUrl = result.shareUrl || this.buildResultPageUrl(result.id);\n        const blob = await this.createShareImage(shareUrl);\n        const file = typeof File === 'undefined'\n          ? null\n          : new File([blob], 'zellaforte-face-vitality-scan.png', { type: 'image/png' });\n        const title = this.label('shareTitle', 'My Zellaforte Face Vitality Scan');\n        const text = this.label('shareText', 'Run your own live scan here:');\n        const data = { title, text, url: shareUrl };\n\n        const sharedFile = file ? await this.tryShareFile({ ...data, files: [file] }) : false;\n        if (sharedFile) {\n          this.trackShare('web_share_file', shareUrl, result.id);\n          this.setShareStatus(this.label('shareDone', 'Shared'));\n          return;\n        }\n\n        const copied = await this.copyShareUrl(shareUrl);\n\n        if (navigator.share) {\n          const sharedLink = await this.tryShareLink(data);\n          if (sharedLink) {\n            this.trackShare(copied ? 'copy_web_share_link' : 'web_share_link', shareUrl, result.id);\n            this.setShareStatus(this.label('shareLinkShared', 'Result link shared.'));\n            return;\n          }\n        }\n\n        this.trackShare(copied ? 'copy_link' : 'result_ready', shareUrl, result.id);\n        this.setShareStatus(\n          copied\n            ? this.label('shareCopied', 'Score image downloaded and referral link copied.')\n            : this.label('shareResultReady', 'Result link is ready.')\n        );\n      } catch (error) {\n        if (!error || error.name !== 'AbortError') {\n          this.setShareStatus(this.label('shareUploadError', 'Snapshot upload is not available yet.'));\n          await this.shareLocalFallback();\n        }\n      } finally {\n        if (this.shareButton) {\n          this.shareButton.disabled = false;\n          this.shareButton.textContent = originalText;\n        }\n      }\n    }\n\n    async shareLocalFallback() {\n      try {\n        const shareUrl = this.buildScanReferralUrl();\n        const blob = await this.createShareImage(shareUrl);\n        const file = typeof File === 'undefined'\n          ? null\n          : new File([blob], 'zellaforte-face-vitality-scan.png', { type: 'image/png' });\n        const title = this.label('shareTitle', 'My Zellaforte Face Vitality Scan');\n        const text = this.label('shareText', 'Run your own live scan here:');\n        const data = { title, text, url: shareUrl };\n        const sharedFile = file ? await this.tryShareFile({ ...data, files: [file] }) : false;\n        if (sharedFile) {\n          this.trackShare('fallback_web_share_file', shareUrl, '');\n          this.setShareStatus(this.label('shareDone', 'Shared'));\n          return;\n        }\n        this.downloadBlob(blob, 'zellaforte-face-vitality-scan.png');\n        const copied = await this.copyShareUrl(shareUrl);\n        this.trackShare(copied ? 'fallback_download_copy' : 'fallback_download', shareUrl, '');\n        this.setShareStatus(\n          copied\n            ? this.label('shareCopied', 'Score image downloaded and referral link copied.')\n            : this.label('shareDownloaded', 'Score image downloaded. Send it with the referral link shown on the image.')\n        );\n      } catch (error) {\n        this.setShareStatus(this.label('shareError', 'Sharing is not available. Please try again.'));\n      }\n    }\n\n    async tryShareFile(data) {\n      if (!navigator.share || typeof File === 'undefined') return false;\n      try {\n        if (navigator.canShare && !navigator.canShare({ files: data.files })) return false;\n        await navigator.share(data);\n        return true;\n      } catch (error) {\n        if (error && error.name === 'AbortError') throw error;\n        return false;\n      }\n    }\n\n    async tryShareLink(data) {\n      if (!navigator.share) return false;\n      try {\n        await navigator.share(data);\n        return true;\n      } catch (error) {\n        if (error && error.name === 'AbortError') throw error;\n        return false;\n      }\n    }\n\n    buildScanReferralUrl() {\n      const url = new URL(window.location.href);\n      const referralCode = this.getReferralCode();\n      url.searchParams.set('fs_ref', referralCode);\n      url.searchParams.set('utm_source', 'face_scan');\n      url.searchParams.set('utm_medium', 'share');\n      url.searchParams.set('utm_campaign', 'face_vitality_scan');\n      return url.toString();\n    }\n\n    buildResultPageUrl(resultId) {\n      const base = this.localizedPath(String(this.config.resultPageUrl || '/pages/about?view=face-scan-result').trim() || '/pages/about?view=face-scan-result');\n      const url = new URL(base, window.location.origin);\n      if (resultId) url.searchParams.set('id', resultId);\n      url.searchParams.set('fs_ref', this.getReferralCode());\n      url.searchParams.set('utm_source', 'face_scan');\n      url.searchParams.set('utm_medium', 'share');\n      url.searchParams.set('utm_campaign', 'face_vitality_scan');\n      return url.toString();\n    }\n\n    localizedPath(path) {\n      const value = String(path || '').trim();\n      if (!value.startsWith('/') || value.startsWith('//')) return value;\n      const currentPrefix = window.location.pathname.split('/').filter(Boolean)[0] || '';\n      if (!/^[a-z]{2}$/i.test(currentPrefix)) return value;\n      if (value.startsWith(`/${currentPrefix}/`)) return value;\n      return `/${currentPrefix}${value}`;\n    }\n\n    resultApiBaseUrl() {\n      const value = String(this.config.resultApiBaseUrl || '/face-scan/results').trim() || '/face-scan/results';\n      return new URL(value, window.location.origin);\n    }\n\n    getReferralCode() {\n      try {\n        const existing = window.localStorage.getItem(REFERRAL_KEY);\n        if (existing) return existing;\n        const code = `fs_${Math.random().toString(36).slice(2, 8)}${Date.now().toString(36).slice(-4)}`;\n        window.localStorage.setItem(REFERRAL_KEY, code);\n        return code;\n      } catch (error) {\n        return `fs_${Date.now().toString(36).slice(-8)}`;\n      }\n    }\n\n    getReferredByCode() {\n      try {\n        return new URL(window.location.href).searchParams.get('fs_ref') || '';\n      } catch (error) {\n        return '';\n      }\n    }\n\n    trackReferralStarted() {\n      const referredBy = this.getReferredByCode();\n      if (!referredBy) return false;\n      try {\n        if (!window._learnq) return false;\n        window._learnq.push(['track', 'Face Scan Referral Started', {\n          source: 'face_scan_landing',\n          referred_by: referredBy,\n          referral_code: this.getReferralCode(),\n          locale: this.config.locale || '',\n          url: window.location.href,\n        }]);\n        return true;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    trackShare(method, shareUrl, resultId) {\n      try {\n        if (!window._learnq) return false;\n        window._learnq.push(['track', 'Face Scan Share Created', {\n          source: 'face_scan_landing',\n          method,\n          result_id: resultId || '',\n          referral_code: this.getReferralCode(),\n          referred_by: this.getReferredByCode(),\n          url: window.location.href,\n          share_url: shareUrl,\n          locale: this.config.locale || '',\n        }]);\n        return true;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    trackCtaClick(target) {\n      try {\n        if (!window._learnq) return false;\n        window._learnq.push(['track', 'Face Scan CTA Click', {\n          source: 'face_scan_landing',\n          target,\n          referral_code: this.getReferralCode(),\n          referred_by: this.getReferredByCode(),\n          locale: this.config.locale || '',\n          url: window.location.href,\n        }]);\n        return true;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    setShareStatus(message) {\n      if (this.shareStatus) this.shareStatus.textContent = message || '';\n    }\n\n    async uploadShareResult(blob) {\n      const endpoint = this.resultApiBaseUrl();\n      const metrics = this.shareMetrics();\n      const formData = new FormData();\n      formData.set('snapshot', blob, 'zellaforte-face-vitality-scan.png');\n      formData.set('result', JSON.stringify({\n        source: 'face_scan_landing',\n        locale: this.config.locale || '',\n        age_model_version: this.ageEstimator ? this.ageEstimator.modelVersion : this.config.ageModelVersion || '',\n        referral_code: this.getReferralCode(),\n        referred_by: this.getReferredByCode(),\n        result_page_url: this.localizedPath(String(this.config.resultPageUrl || '/pages/about?view=face-scan-result')),\n        metrics,\n      }));\n\n      const response = await fetch(endpoint.toString(), {\n        method: 'POST',\n        body: formData,\n      });\n      if (!response.ok) throw new Error('Could not upload share result');\n      const data = await response.json();\n      const id = String(data.id || data.result_id || '').trim();\n      if (!id) throw new Error('Missing result id');\n      const shareUrl = data.url || data.share_url || this.buildResultPageUrl(id);\n      return {\n        id,\n        shareUrl: new URL(shareUrl, window.location.origin).toString(),\n        expiresAt: data.expires_at || '',\n      };\n    }\n\n    shareMetrics() {\n      return {\n        visual_age: this.output.age ? this.output.age.textContent : '--',\n        skin_vitality: this.output.vitality ? this.output.vitality.textContent : '--',\n        glow_balance: this.output.glow ? this.output.glow.textContent : '--',\n        tone_evenness: this.output.tone ? this.output.tone.textContent : '--',\n        texture_smoothness: this.output.texture ? this.output.texture.textContent : '--',\n        pulse: this.pulseResult && Number.isFinite(this.pulseResult.heartRateBpm) ? `${Math.round(this.pulseResult.heartRateBpm)} bpm` : '',\n        balance_score: this.pulseResult && Number.isFinite(this.pulseResult.balanceScore) ? `${Math.round(this.pulseResult.balanceScore)}/100` : '',\n        signal_quality: this.pulseResult && this.pulseResult.signalQuality ? this.pulseResult.signalQuality : '',\n        age_provider: 'local_onnx',\n        pulse_provider: this.pulseResult && this.pulseResult.provider ? this.pulseResult.provider : '',\n        pulse_method: this.pulseResult && this.pulseResult.method ? this.pulseResult.method : '',\n      };\n    }\n\n    async createShareImage(shareUrl = '') {\n      const canvas = document.createElement('canvas');\n      canvas.width = 1200;\n      canvas.height = 1600;\n      const ctx = canvas.getContext('2d');\n      const age = this.output.age ? this.output.age.textContent : '--';\n      const vitality = this.output.vitality ? this.output.vitality.textContent : '--';\n      const glow = this.output.glow ? this.output.glow.textContent : '--';\n      const tone = this.output.tone ? this.output.tone.textContent : '--';\n      const texture = this.output.texture ? this.output.texture.textContent : '--';\n      const pulse = this.pulseResult && Number.isFinite(this.pulseResult.heartRateBpm) ? `${Math.round(this.pulseResult.heartRateBpm)} bpm` : '';\n      const balance = this.pulseResult && Number.isFinite(this.pulseResult.balanceScore) ? `${Math.round(this.pulseResult.balanceScore)}/100` : '';\n      const signal = this.pulseResult && this.pulseResult.signalQuality ? this.pulseResult.signalQuality : '';\n      const displayUrl = shareUrl || this.buildScanReferralUrl();\n\n      ctx.fillStyle = '#FFF7ED';\n      ctx.fillRect(0, 0, canvas.width, canvas.height);\n      ctx.fillStyle = '#FA660A';\n      ctx.fillRect(0, 0, canvas.width, 132);\n      ctx.fillStyle = '#FFF7ED';\n      ctx.font = '800 58px Arial, sans-serif';\n      ctx.fillText('ZELLAFORTE', 72, 84);\n\n      ctx.fillStyle = '#431207';\n      ctx.font = '800 96px Arial, sans-serif';\n      ctx.fillText('Skin Vitality', 72, 250);\n      ctx.fillText('Snapshot.', 72, 348);\n      if (age && age !== '--') {\n        ctx.fillStyle = 'rgba(67, 18, 7, 0.68)';\n        ctx.font = '700 30px Arial, sans-serif';\n        ctx.fillText(`Visual age estimate: ${age}`, 72, 392);\n      }\n\n      this.drawShareMedia(ctx, 72, 420, 1056, 690);\n      const items = [\n        ['Skin vitality', vitality],\n        ['Glow balance', glow],\n        ['Tone evenness', tone],\n        ['Texture smoothness', texture],\n      ];\n      if (pulse) items.push(['Pulse', pulse], ['Vital balance', balance], ['Signal', signal]);\n      this.drawShareResultGrid(ctx, 72, pulse ? 1118 : 1160, 1056, pulse ? 390 : 252, items);\n\n      ctx.fillStyle = 'rgba(67, 18, 7, 0.72)';\n      ctx.font = '600 28px Arial, sans-serif';\n      ctx.fillText('Visual AI estimate only. No medical analysis.', 72, pulse ? 1526 : 1480);\n      ctx.font = '600 24px Arial, sans-serif';\n      ctx.fillText(displayUrl.replace(/^https?:\\/\\//, ''), 72, pulse ? 1562 : 1530);\n\n      return new Promise((resolve, reject) => {\n        canvas.toBlob((blob) => {\n          if (blob) resolve(blob);\n          else reject(new Error('Could not create share image'));\n        }, 'image/png', 0.92);\n      });\n    }\n\n    drawShareMedia(ctx, x, y, width, height) {\n      ctx.save();\n      ctx.fillStyle = '#F4E6DA';\n      roundedRect(ctx, x, y, width, height, 32);\n      ctx.fill();\n      ctx.clip();\n      this.drawMirroredCover(ctx, this.video, x, y, width, height);\n      this.drawMirroredCover(ctx, this.canvas, x, y, width, height);\n      ctx.restore();\n\n      ctx.save();\n      ctx.strokeStyle = 'rgba(250, 102, 10, 0.92)';\n      ctx.lineWidth = 5;\n      roundedRect(ctx, x, y, width, height, 32);\n      ctx.stroke();\n      ctx.restore();\n    }\n\n    drawMirroredCover(ctx, source, x, y, width, height) {\n      if (!source) return;\n      const sourceWidth = source.videoWidth || source.width || 0;\n      const sourceHeight = source.videoHeight || source.height || 0;\n      if (!sourceWidth || !sourceHeight) return;\n      const scale = Math.max(width / sourceWidth, height / sourceHeight);\n      const drawWidth = sourceWidth * scale;\n      const drawHeight = sourceHeight * scale;\n      const dx = x + (width - drawWidth) / 2;\n      const dy = y + (height - drawHeight) / 2;\n\n      ctx.save();\n      ctx.translate(x + width, y);\n      ctx.scale(-1, 1);\n      ctx.drawImage(source, dx - x, dy - y, drawWidth, drawHeight);\n      ctx.restore();\n    }\n\n    drawShareResultGrid(ctx, x, y, width, height, items) {\n      const colWidth = width / 2;\n      const rows = Math.ceil(items.length / 2);\n      const rowHeight = height / rows;\n      ctx.save();\n      ctx.strokeStyle = 'rgba(67, 18, 7, 0.18)';\n      ctx.lineWidth = 2;\n      items.forEach(([label, value], index) => {\n        const col = index % 2;\n        const row = Math.floor(index / 2);\n        const cellX = x + col * colWidth;\n        const cellY = y + row * rowHeight;\n        ctx.fillStyle = 'rgba(255, 255, 255, 0.48)';\n        ctx.fillRect(cellX, cellY, colWidth, rowHeight);\n        ctx.strokeRect(cellX, cellY, colWidth, rowHeight);\n        ctx.fillStyle = 'rgba(67, 18, 7, 0.62)';\n        ctx.font = '800 22px Arial, sans-serif';\n        ctx.fillText(label.toUpperCase(), cellX + 32, cellY + 46);\n        ctx.fillStyle = '#431207';\n        ctx.font = '800 62px Arial, sans-serif';\n        ctx.fillText(value, cellX + 32, cellY + 108);\n      });\n      ctx.restore();\n    }\n\n    async copyShareUrl(url) {\n      try {\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n          await navigator.clipboard.writeText(url);\n          return true;\n        }\n      } catch (error) {\n        // Fall through to the textarea copy path.\n      }\n\n      try {\n        const textarea = document.createElement('textarea');\n        textarea.value = url;\n        textarea.setAttribute('readonly', '');\n        textarea.style.position = 'fixed';\n        textarea.style.top = '-9999px';\n        textarea.style.opacity = '0';\n        document.body.appendChild(textarea);\n        textarea.select();\n        const copied = document.execCommand('copy');\n        textarea.remove();\n        return copied;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    downloadBlob(blob, filename) {\n      const url = URL.createObjectURL(blob);\n      const link = document.createElement('a');\n      link.href = url;\n      link.download = filename;\n      document.body.appendChild(link);\n      link.click();\n      link.remove();\n      window.setTimeout(() => URL.revokeObjectURL(url), 1000);\n    }\n\n    isValidEmail(value) {\n      return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(String(value || '').trim());\n    }\n\n    showError(message) {\n      if (!this.error) return;\n      this.error.textContent = message || '';\n      this.error.hidden = !message;\n    }\n\n    isUnlocked() {\n      try {\n        const value = Number(window.localStorage.getItem(UNLOCK_KEY) || 0);\n        if (!value) return false;\n        const days = Number(this.config.unlockDays || 30);\n        return Date.now() - value < days * 24 * 60 * 60 * 1000;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    markUnlocked() {\n      this.unlocked = true;\n      try {\n        window.localStorage.setItem(UNLOCK_KEY, String(Date.now()));\n      } catch (error) {\n        // Storage can fail in private browsing; the current session still unlocks.\n      }\n    }\n\n    setLockedState(locked) {\n      if (!this.results) return;\n      this.results.dataset.locked = locked ? 'true' : 'false';\n      this.updateResultVisibility();\n    }\n\n    setCameraStatus(text) {\n      if (this.cameraStatus) this.cameraStatus.textContent = text;\n    }\n\n    setEngineStatus(text) {\n      if (this.engineStatus) this.engineStatus.textContent = text;\n    }\n\n    setQualityStatus(text) {\n      if (this.qualityStatus) this.qualityStatus.textContent = text;\n    }\n\n    setLightStatus(good) {\n      if (!this.output.light) return;\n      this.output.light.textContent = good\n        ? this.label('lightGood', 'Light: good')\n        : this.label('lightPoor', 'Turn toward the light');\n      this.output.light.setAttribute('data-light', good ? 'good' : 'poor');\n      this.output.light.hidden = false;\n    }\n\n    setAppStatus(status, quality, progress = 0) {\n      if (this.appStatus) this.appStatus.textContent = status || '';\n      if (this.appQuality) this.appQuality.textContent = quality || '';\n      if (this.progressBar) this.progressBar.style.transform = `scaleX(${clamp(progress, 0, 1)})`;\n    }\n\n    updateResultVisibility() {\n      if (!this.results) return;\n      const locked = this.results.dataset.locked === 'true';\n      if (this.resultState) {\n        if (locked) {\n          this.resultState.textContent = this.label('locked', 'Result locked');\n        } else {\n          this.resultState.textContent = this.hasVisibleResult ? this.label('unlocked', 'Live result') : this.label('ageCalibrating', 'Calibrating');\n        }\n      }\n      if (this.cta) this.cta.hidden = locked || !this.hasVisibleResult;\n      this.updateShareState();\n      if (this.hasVisibleResult) {\n        this.setAppStatus(\n          locked ? this.label('scanReady', 'Scan ready') : this.label('resultReady', 'Result ready'),\n          this.label('stableShort', 'Stable'),\n          1\n        );\n      }\n    }\n\n    updateShareState() {\n      const canShare = !!(\n        this.unlocked\n        && this.hasVisibleResult\n        && this.root.dataset.camera === 'live'\n      );\n      if (this.sharePanel) this.sharePanel.hidden = !canShare;\n      if (this.shareButton) this.shareButton.disabled = !canShare;\n      this.updatePulseState();\n    }\n\n    updatePulseState() {\n      const canShow = !!(\n        this.config.pulseEnabled !== false\n        && this.unlocked\n        && this.hasVisibleResult\n        && this.root.dataset.camera === 'live'\n      );\n      if (this.pulsePanel) this.pulsePanel.hidden = !canShow;\n      if (this.pulseButton) {\n        this.pulseButton.disabled = !canShow || this.pulseRunning || !!this.pulseResult;\n        this.pulseButton.hidden = !!this.pulseResult;\n      }\n    }\n\n    startLoop() {\n      if (this.raf) return;\n      const tick = () => {\n        this.render();\n        this.raf = window.requestAnimationFrame(tick);\n      };\n      this.raf = window.requestAnimationFrame(tick);\n    }\n\n    stopLoop() {\n      if (!this.raf) return;\n      window.cancelAnimationFrame(this.raf);\n      this.raf = 0;\n    }\n\n    async render() {\n      if (!this.video || !this.video.videoWidth || !this.ctx) return;\n      this.resizeCanvas();\n      const now = performance.now();\n      let frame = null;\n\n      if (this.mediaPipeReady && this.faceLandmarker && this.video.currentTime !== this.lastVideoTime) {\n        try {\n          const result = this.faceLandmarker.detectForVideo(this.video, now);\n          const faces = result && result.faceLandmarks ? result.faceLandmarks : [];\n          frame = this.frameFromLandmarks(faces);\n          this.lastVideoTime = this.video.currentTime;\n        } catch (error) {\n          this.mediaPipeReady = false;\n          this.usingFallback = true;\n          this.initFallbackDetector();\n          this.syncEngineStatus();\n        }\n      }\n\n      if (!frame && this.usingFallback) {\n        frame = await this.fallbackFrame(now);\n      }\n\n      if (frame && frame.box) {\n        frame.motion = this.previousFrameBox ? this.boxMotion(frame.box, this.previousFrameBox) : 0;\n        this.previousFrameBox = { ...frame.box };\n        this.latestFrame = frame;\n      } else if (frame) {\n        this.previousFrameBox = null;\n        this.latestFrame = frame;\n      }\n      this.draw(this.latestFrame, now);\n\n      if (this.latestFrame && now - this.lastAnalysisAt > ANALYSIS_INTERVAL && !this.analysisBusy) {\n        this.lastAnalysisAt = now;\n        this.analysisBusy = true;\n        Promise.resolve(this.analyze(this.latestFrame)).finally(() => {\n          this.analysisBusy = false;\n        });\n      }\n    }\n\n    frameFromLandmarks(faces) {\n      if (!faces || !faces.length) {\n        this.setQualityStatus(this.label('noFace', 'No face'));\n        this.stableFrames = 0;\n        this.setAppStatus(this.label('scanRunning', 'Scanning'), this.label('noFace', 'No face'), 0);\n        return { type: 'empty', faceCount: 0 };\n      }\n\n      const candidates = faces\n        .map((landmarks) => ({\n          landmarks,\n          box: this.boxFromLandmarks(landmarks),\n        }))\n        .filter((candidate) => candidate.box && this.faceBoxArea(candidate.box) > 0.025)\n        .sort((a, b) => this.faceBoxArea(b.box) - this.faceBoxArea(a.box));\n\n      if (!candidates.length) {\n        this.setQualityStatus(this.label('noFace', 'No face'));\n        this.stableFrames = 0;\n        this.setAppStatus(this.label('scanRunning', 'Scanning'), this.label('noFace', 'No face'), 0);\n        return { type: 'empty', faceCount: 0 };\n      }\n\n      const primary = candidates[0];\n      const primaryArea = this.faceBoxArea(primary.box);\n      const activeFaceCount = candidates.filter((candidate) => {\n        const area = this.faceBoxArea(candidate.box);\n        return area > primaryArea * 0.4 && candidate.box.width > 0.16 && candidate.box.height > 0.2;\n      }).length;\n\n      if (activeFaceCount > 1) {\n        this.setQualityStatus(this.label('multipleFaces', 'One face only'));\n        this.stableFrames = 0;\n        this.setAppStatus(this.label('scanRunning', 'Scanning'), this.label('multipleFaces', 'One face only'), 0);\n      }\n\n      return {\n        type: 'mediapipe',\n        faceCount: activeFaceCount,\n        points: primary.landmarks,\n        box: primary.box,\n        groups: MEDIAPIPE_FACE_GROUPS,\n      };\n    }\n\n    async fallbackFrame(now) {\n      if (now - this.lastFallbackAt < FALLBACK_INTERVAL) return this.latestFrame;\n      this.lastFallbackAt = now;\n\n      if (!this.faceDetector) {\n        return { type: 'empty', faceCount: 0 };\n      }\n\n      try {\n        const faces = await this.faceDetector.detect(this.video);\n        if (!faces || !faces.length) {\n          this.setQualityStatus(this.label('noFace', 'No face'));\n          return { type: 'empty', faceCount: 0 };\n        }\n\n        const candidates = faces\n          .map((face) => {\n            const sourceBox = face.boundingBox;\n            return {\n              x: sourceBox.x / this.video.videoWidth,\n              y: sourceBox.y / this.video.videoHeight,\n              width: sourceBox.width / this.video.videoWidth,\n              height: sourceBox.height / this.video.videoHeight,\n            };\n          })\n          .filter((box) => this.faceBoxArea(box) > 0.025)\n          .sort((a, b) => this.faceBoxArea(b) - this.faceBoxArea(a));\n        if (!candidates.length) {\n          this.setQualityStatus(this.label('noFace', 'No face'));\n          return { type: 'empty', faceCount: 0 };\n        }\n\n        const box = candidates[0];\n        const primaryArea = this.faceBoxArea(box);\n        const activeFaceCount = candidates.filter((candidate) => {\n          const area = this.faceBoxArea(candidate);\n          return area > primaryArea * 0.4 && candidate.width > 0.16 && candidate.height > 0.2;\n        }).length;\n        return {\n          type: 'fallback',\n          faceCount: activeFaceCount,\n          box,\n          points: this.approximateLandmarks(box).points,\n          groups: this.approximateLandmarks(box).groups,\n        };\n      } catch (error) {\n        return { type: 'empty', faceCount: 0 };\n      }\n    }\n\n    boxFromLandmarks(points) {\n      if (!points || !points.length) return null;\n      let minX = 1;\n      let minY = 1;\n      let maxX = 0;\n      let maxY = 0;\n      points.forEach((point) => {\n        minX = Math.min(minX, point.x);\n        minY = Math.min(minY, point.y);\n        maxX = Math.max(maxX, point.x);\n        maxY = Math.max(maxY, point.y);\n      });\n      const padX = (maxX - minX) * 0.08;\n      const padY = (maxY - minY) * 0.08;\n      return {\n        x: clamp(minX - padX, 0, 1),\n        y: clamp(minY - padY, 0, 1),\n        width: clamp(maxX - minX + padX * 2, 0, 1),\n        height: clamp(maxY - minY + padY * 2, 0, 1),\n      };\n    }\n\n    faceBoxArea(box) {\n      if (!box) return 0;\n      return Math.max(0, box.width) * Math.max(0, box.height);\n    }\n\n    boxMotion(current, previous) {\n      if (!current || !previous) return 0;\n      const currentCenterX = current.x + current.width / 2;\n      const currentCenterY = current.y + current.height / 2;\n      const previousCenterX = previous.x + previous.width / 2;\n      const previousCenterY = previous.y + previous.height / 2;\n      const centerDelta = Math.hypot(currentCenterX - previousCenterX, currentCenterY - previousCenterY);\n      const sizeDelta = Math.abs(current.width - previous.width) + Math.abs(current.height - previous.height);\n      return centerDelta + sizeDelta * 0.55;\n    }\n\n    approximateLandmarks(box) {\n      const point = (x, y) => ({ x: box.x + box.width * x, y: box.y + box.height * y, z: 0 });\n      const ellipse = (cx, cy, rx, ry, count, start = 0, end = Math.PI * 2) => {\n        const points = [];\n        for (let index = 0; index < count; index += 1) {\n          const t = start + ((end - start) * index) / Math.max(count - 1, 1);\n          points.push(point(cx + Math.cos(t) * rx, cy + Math.sin(t) * ry));\n        }\n        return points;\n      };\n      const groups = {};\n      const points = [];\n      const addGroup = (name, items) => {\n        groups[name] = [];\n        items.forEach((item) => {\n          groups[name].push(points.length);\n          points.push(item);\n        });\n      };\n\n      addGroup('oval', ellipse(0.5, 0.52, 0.42, 0.47, 34, -Math.PI * 0.88, Math.PI * 0.88));\n      addGroup('leftEye', ellipse(0.34, 0.43, 0.105, 0.045, 14));\n      addGroup('rightEye', ellipse(0.66, 0.43, 0.105, 0.045, 14));\n      addGroup('leftBrow', [point(0.22, 0.34), point(0.29, 0.31), point(0.39, 0.31), point(0.47, 0.34)]);\n      addGroup('rightBrow', [point(0.53, 0.34), point(0.61, 0.31), point(0.71, 0.31), point(0.78, 0.34)]);\n      addGroup('nose', [point(0.5, 0.38), point(0.5, 0.48), point(0.48, 0.58), point(0.44, 0.64), point(0.5, 0.66), point(0.56, 0.64), point(0.52, 0.58)]);\n      addGroup('mouth', ellipse(0.5, 0.77, 0.17, 0.06, 18));\n      return { points, groups };\n    }\n\n    // memoised \u2014 a scan is short-lived, the OS preference won't change mid-scan\n    reduceMotion() {\n      if (this._reduceMotion === undefined) {\n        this._reduceMotion = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);\n      }\n      return this._reduceMotion;\n    }\n\n    draw(frame, now) {\n      const ctx = this.ctx;\n      const width = this.canvas.width;\n      const height = this.canvas.height;\n      ctx.clearRect(0, 0, width, height);\n      if (!this.reduceMotion()) this.drawScannerSweep(ctx, width, height, now);\n\n      if (!frame || frame.type === 'empty' || !frame.points || !frame.points.length) {\n        this.drawSearching(ctx, width, height, now);\n        return;\n      }\n\n      const mapped = frame.points.map((point) => this.mapPoint(point.x, point.y));\n      // ONE tracking frame (brightens once sampling begins = \"locked\") + a light contour +\n      // a thinned point cloud with no per-point shadow \u2014 calmer and far cheaper than the\n      // old 468 glowing dots + a second full-width sweep.\n      this.drawFaceBox(ctx, frame.box, this.samples.length > 0);\n      Object.keys(frame.groups || {}).forEach((name) => {\n        this.drawPath(ctx, mapped, frame.groups[name], name === 'nose');\n      });\n      const step = frame.type === 'fallback' ? 1 : 4;\n      for (let index = 0; index < mapped.length; index += step) {\n        if (mapped[index]) this.drawPoint(ctx, mapped[index].x, mapped[index].y, 2.4);\n      }\n    }\n\n    drawScannerSweep(ctx, width, height, now) {\n      const y = (Math.sin(now / 1100) * 0.5 + 0.5) * height;\n      const gradient = ctx.createLinearGradient(0, y - 40, 0, y + 40);\n      gradient.addColorStop(0, 'rgba(250, 102, 10, 0)');\n      gradient.addColorStop(0.5, 'rgba(250, 102, 10, 0.22)');\n      gradient.addColorStop(1, 'rgba(250, 102, 10, 0)');\n      ctx.fillStyle = gradient;\n      ctx.fillRect(0, y - 40, width, 80);\n    }\n\n    drawSearching(ctx, width, height, now) {\n      const cx = width / 2;\n      const cy = height / 2;\n      const pulse = this.reduceMotion() ? 0 : Math.sin(now / 800) * 0.01;\n      const radius = Math.min(width, height) * (0.16 + pulse);\n      ctx.save();\n      ctx.strokeStyle = 'rgba(250, 102, 10, 0.58)';\n      ctx.lineWidth = 1.4 * this.dpr();\n      ctx.setLineDash([8 * this.dpr(), 10 * this.dpr()]);\n      ctx.beginPath();\n      ctx.ellipse(cx, cy, radius * 0.78, radius, 0, 0, Math.PI * 2);\n      ctx.stroke();\n      ctx.restore();\n    }\n\n    drawFaceBox(ctx, box, locked) {\n      if (!box) return;\n      const topLeft = this.mapPoint(box.x, box.y);\n      const bottomRight = this.mapPoint(box.x + box.width, box.y + box.height);\n      const x = topLeft.x;\n      const y = topLeft.y;\n      const width = bottomRight.x - topLeft.x;\n      const height = bottomRight.y - topLeft.y;\n      // corners lengthen + brighten the moment sampling starts \u2014 a subtle \"locked on\" beat\n      const corner = Math.min(width, height) * (locked ? 0.16 : 0.11);\n\n      ctx.save();\n      ctx.strokeStyle = locked ? 'rgba(250, 102, 10, 0.95)' : 'rgba(250, 102, 10, 0.55)';\n      ctx.lineWidth = (locked ? 2 : 1.3) * this.dpr();\n      ctx.beginPath();\n      [\n        [x, y, x + corner, y],\n        [x, y, x, y + corner],\n        [x + width, y, x + width - corner, y],\n        [x + width, y, x + width, y + corner],\n        [x, y + height, x + corner, y + height],\n        [x, y + height, x, y + height - corner],\n        [x + width, y + height, x + width - corner, y + height],\n        [x + width, y + height, x + width, y + height - corner],\n      ].forEach(([x1, y1, x2, y2]) => {\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n      });\n      ctx.stroke();\n      ctx.restore();\n    }\n\n    drawPath(ctx, mapped, indices, open) {\n      if (!indices || indices.length < 2) return;\n      ctx.save();\n      ctx.strokeStyle = 'rgba(255, 247, 237, 0.66)';\n      ctx.lineWidth = 0.85 * this.dpr();\n      ctx.beginPath();\n      indices.forEach((index, position) => {\n        const point = mapped[index];\n        if (!point) return;\n        if (position === 0) ctx.moveTo(point.x, point.y);\n        else ctx.lineTo(point.x, point.y);\n      });\n      if (!open) ctx.closePath();\n      ctx.stroke();\n      ctx.restore();\n    }\n\n    drawPoint(ctx, x, y, radius) {\n      const size = radius * this.dpr();\n      ctx.save();\n      // flat two-tone dot \u2014 no per-point shadowBlur (the single most expensive canvas op,\n      // \u00D7468/frame before; dropping it is a large perf win with a calmer look)\n      ctx.fillStyle = 'rgba(250, 102, 10, 0.85)';\n      ctx.beginPath();\n      ctx.arc(x, y, size, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.fillStyle = 'rgba(255, 247, 237, 0.85)';\n      ctx.beginPath();\n      ctx.arc(x, y, size * 0.42, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.restore();\n    }\n\n    updateAgeNote() {\n      if (!this.output.ageNote) return;\n      const unavailable = this.ageEstimator && this.ageEstimator.failed;\n      this.output.ageNote.textContent = unavailable\n        ? this.label('ageUnavailable', 'Age currently unavailable')\n        : this.label('ageNoteLocal', 'Local estimate');\n    }\n\n    videoRectFromBox(box, widthScale, heightScale, yOffsetFactor) {\n      const videoWidth = this.video.videoWidth;\n      const videoHeight = this.video.videoHeight;\n      const faceWidth = box.width * videoWidth;\n      const faceHeight = box.height * videoHeight;\n      const targetWidth = Math.min(videoWidth, Math.max(faceWidth * widthScale, faceHeight * 0.8));\n      const targetHeight = Math.min(videoHeight, Math.max(faceHeight * heightScale, targetWidth * 0.75));\n      const centerX = (box.x + box.width / 2) * videoWidth;\n      const centerY = (box.y + box.height / 2) * videoHeight + targetHeight * yOffsetFactor;\n      const x = clamp(centerX - targetWidth / 2, 0, Math.max(0, videoWidth - targetWidth));\n      const y = clamp(centerY - targetHeight / 2, 0, Math.max(0, videoHeight - targetHeight));\n      return { x, y, width: targetWidth, height: targetHeight };\n    }\n\n    async measurePulse() {\n      if (!this.unlocked) {\n        this.showModal();\n        return;\n      }\n      if (this.pulseRunning) return;\n      if (!this.latestFrame || !this.latestFrame.box || this.root.dataset.camera !== 'live') {\n        this.setPulseStatus(this.label('pulsePosition', 'Center your face first.'));\n        return;\n      }\n\n      const originalText = this.pulseButton ? this.pulseButton.textContent : '';\n      this.pulseRunning = true;\n      this.updatePulseState();\n      if (this.pulseButton) this.pulseButton.textContent = this.label('pulseMeasuring', 'Measuring');\n      if (this.vitalsResult) this.vitalsResult.hidden = true;\n\n      try {\n        const result = await this.captureLocalPulseResult();\n        if (!result || !Number.isFinite(result.heartRateBpm)) throw new Error('Pulse unavailable');\n        this.pulseResult = result;\n        this.renderPulseResult(result);\n        this.trackPulse('completed', result);\n      } catch (error) {\n        this.setPulseStatus(this.label('pulseError', 'Pulse estimate is not available. Try again in steady front light.'));\n        this.trackPulse('failed', null);\n      } finally {\n        this.pulseRunning = false;\n        if (this.pulseButton) this.pulseButton.textContent = originalText;\n        this.updatePulseState();\n      }\n    }\n\n    captureLocalPulseResult() {\n      return new Promise((resolve, reject) => {\n        const samples = [];\n        const intervalMs = 1000 / PULSE_SAMPLE_FPS;\n        const targetFrames = PULSE_SCAN_SECONDS * PULSE_SAMPLE_FPS;\n        const startedAt = performance.now();\n        let previousBox = null;\n\n        const capture = () => {\n          if (!this.latestFrame || !this.latestFrame.box || !this.video.videoWidth) {\n            reject(new Error('No face'));\n            return;\n          }\n          const quality = this.assessQuality(this.latestFrame);\n          if (!quality.good) {\n            reject(new Error('Unstable face'));\n            return;\n          }\n          const sample = this.capturePulseSample(this.latestFrame.box, previousBox);\n          previousBox = this.latestFrame.box;\n          if (!sample) {\n            reject(new Error('Could not capture pulse sample'));\n            return;\n          }\n          samples.push(sample);\n          const elapsed = performance.now() - startedAt;\n          const remaining = Math.max(0, PULSE_SCAN_SECONDS - Math.floor(elapsed / 1000));\n          this.setPulseStatus(this.label('pulseCountdown', 'Hold still: {{ seconds }}s').replace('{{ seconds }}', String(remaining)));\n\n          if (samples.length >= targetFrames) {\n            try {\n              resolve(this.estimateLocalPulse(samples));\n            } catch (error) {\n              reject(error);\n            }\n            return;\n          }\n          window.setTimeout(capture, intervalMs);\n        };\n\n        this.setPulseStatus(this.label('pulseCountdown', 'Hold still: {{ seconds }}s').replace('{{ seconds }}', String(PULSE_SCAN_SECONDS)));\n        capture();\n      });\n    }\n\n    capturePulseSample(box, previousBox) {\n      if (!this.pulseCtx || !this.video.videoWidth || !this.video.videoHeight) return null;\n      const rect = this.videoRectFromBox(box, 1.0, 1.0, 0);\n      this.pulseCtx.clearRect(0, 0, PULSE_CANVAS_SIZE, PULSE_CANVAS_SIZE);\n      this.pulseCtx.drawImage(this.video, rect.x, rect.y, rect.width, rect.height, 0, 0, PULSE_CANVAS_SIZE, PULSE_CANVAS_SIZE);\n      const image = this.pulseCtx.getImageData(0, 0, PULSE_CANVAS_SIZE, PULSE_CANVAS_SIZE).data;\n      let r = 0;\n      let g = 0;\n      let b = 0;\n      let luma = 0;\n      let count = 0;\n\n      for (let y = 0; y < PULSE_CANVAS_SIZE; y += 1) {\n        for (let x = 0; x < PULSE_CANVAS_SIZE; x += 1) {\n          const nx = x / PULSE_CANVAS_SIZE;\n          const ny = y / PULSE_CANVAS_SIZE;\n          const forehead = nx > 0.36 && nx < 0.64 && ny > 0.18 && ny < 0.32;\n          const leftCheek = nx > 0.18 && nx < 0.39 && ny > 0.48 && ny < 0.68;\n          const rightCheek = nx > 0.61 && nx < 0.82 && ny > 0.48 && ny < 0.68;\n          if (!forehead && !leftCheek && !rightCheek) continue;\n          const index = (y * PULSE_CANVAS_SIZE + x) * 4;\n          const pr = image[index];\n          const pg = image[index + 1];\n          const pb = image[index + 2];\n          const pluma = 0.2126 * pr + 0.7152 * pg + 0.0722 * pb;\n          if (pluma < 35 || pluma > 245) continue;\n          r += pr;\n          g += pg;\n          b += pb;\n          luma += pluma;\n          count += 1;\n        }\n      }\n      if (count < 160) return null;\n      const movement = previousBox ? this.faceBoxMovement(box, previousBox) : 0;\n      return {\n        t: performance.now(),\n        r: r / count,\n        g: g / count,\n        b: b / count,\n        luma: luma / count,\n        movement,\n      };\n    }\n\n    estimateLocalPulse(samples) {\n      const durationSeconds = (samples[samples.length - 1].t - samples[0].t) / 1000;\n      const fps = (samples.length - 1) / Math.max(durationSeconds, 1);\n      if (samples.length < 90 || fps < 10) throw new Error('Insufficient pulse frames');\n\n      const brightness = samples.reduce((sum, item) => sum + item.luma, 0) / samples.length;\n      const movementAvg = samples.reduce((sum, item) => sum + item.movement, 0) / samples.length;\n      if (brightness < 50 || brightness > 225 || movementAvg > 0.055) throw new Error('Weak pulse signal');\n\n      const r = normalizeSeries(samples.map((item) => item.r));\n      const g = normalizeSeries(samples.map((item) => item.g));\n      const b = normalizeSeries(samples.map((item) => item.b));\n      const chromX = r.map((value, index) => value - g[index]);\n      const chromY = r.map((value, index) => value + g[index] - 2 * b[index]);\n      const chrom = combineProjection(chromX, chromY, -1);\n      const posX = r.map((value, index) => value - b[index]);\n      const posY = g.map((value, index) => value - b[index]);\n      const pos = combineProjection(posX, posY, 1);\n      const chromEstimate = estimateBpmFromSignal(chrom, fps);\n      const posEstimate = estimateBpmFromSignal(pos, fps);\n      const agreement = Math.abs(chromEstimate.bpm - posEstimate.bpm);\n      const snr = (chromEstimate.snr + posEstimate.snr) / 2;\n      if (agreement > 18 || snr < 1.25) throw new Error('Weak pulse signal');\n\n      const heartRateBpm = Math.round((chromEstimate.bpm * chromEstimate.snr + posEstimate.bpm * posEstimate.snr) / Math.max(chromEstimate.snr + posEstimate.snr, 0.001));\n      const segmentStability = this.pulseSegmentStability(chrom, fps);\n      const fpsScore = clamp((fps - 10) / 14, 0, 1);\n      const movementScore = clamp(1 - movementAvg / 0.055, 0, 1);\n      const agreementScore = clamp(1 - agreement / 18, 0, 1);\n      const snrScore = clamp((snr - 1.25) / 2.4, 0, 1);\n      const stabilityScore = Number.isFinite(segmentStability) ? clamp(1 - segmentStability / 12, 0, 1) : 0.45;\n      const qualityScore = 100 * (snrScore * 0.38 + agreementScore * 0.24 + movementScore * 0.2 + fpsScore * 0.1 + stabilityScore * 0.08);\n      if (qualityScore < 46) throw new Error('Weak pulse signal');\n\n      const balanceScore = Math.round(clamp(42 + qualityScore * 0.46 + stabilityScore * 12 + movementScore * 8, 45, 96));\n      return {\n        heartRateBpm,\n        signalQuality: this.signalQualityLabel(qualityScore),\n        signalQualityScore: Math.round(qualityScore),\n        balanceScore,\n        confidence: Number((qualityScore / 100).toFixed(2)),\n        durationSeconds,\n        method: 'local_pos_chrom',\n        provider: 'local_pos_chrom',\n      };\n    }\n\n    pulseSegmentStability(signal, fps) {\n      const segmentLength = Math.max(45, Math.floor(signal.length / 3));\n      const estimates = [];\n      for (let start = 0; start + segmentLength <= signal.length; start += Math.floor(segmentLength * 0.75)) {\n        try {\n          estimates.push(estimateBpmFromSignal(signal.slice(start, start + segmentLength), fps).bpm);\n        } catch (error) {\n          // Short segments can fail in noisy conditions; final quality handles this.\n        }\n      }\n      if (estimates.length < 2) return 12;\n      const mean = estimates.reduce((sum, value) => sum + value, 0) / estimates.length;\n      const variance = estimates.reduce((sum, value) => sum + Math.pow(value - mean, 2), 0) / estimates.length;\n      return Math.sqrt(variance);\n    }\n\n    signalQualityLabel(score) {\n      if (score >= 72) return this.label('signalStrong', 'Strong');\n      if (score >= 54) return this.label('signalMedium', 'Medium');\n      return this.label('signalWeak', 'Weak');\n    }\n\n    faceBoxMovement(box, previousBox) {\n      const centerX = box.x + box.width / 2;\n      const centerY = box.y + box.height / 2;\n      const previousCenterX = previousBox.x + previousBox.width / 2;\n      const previousCenterY = previousBox.y + previousBox.height / 2;\n      const centerDelta = Math.hypot(centerX - previousCenterX, centerY - previousCenterY);\n      const sizeDelta = Math.abs(box.width - previousBox.width) + Math.abs(box.height - previousBox.height);\n      return centerDelta + sizeDelta * 0.65;\n    }\n\n    renderPulseResult(result) {\n      const value = Math.round(result.heartRateBpm);\n      if (this.vitalsOutput.pulse) this.vitalsOutput.pulse.textContent = this.label('pulseResult', '{{ bpm }} bpm').replace('{{ bpm }}', String(value));\n      if (this.vitalsOutput.balance) this.vitalsOutput.balance.textContent = this.label('balanceResult', '{{ score }}/100').replace('{{ score }}', String(result.balanceScore));\n      if (this.vitalsOutput.signal) this.vitalsOutput.signal.textContent = result.signalQuality || this.label('signalMedium', 'Medium');\n      if (this.vitalsResult) this.vitalsResult.hidden = false;\n      this.setPulseStatus(this.label('pulseDone', 'Wellness estimate only. Not a medical measurement.'));\n    }\n\n    setPulseStatus(message) {\n      if (this.pulseStatus) this.pulseStatus.textContent = message || '';\n    }\n\n    trackPulse(status, result) {\n      try {\n        if (!window._learnq) return false;\n        window._learnq.push(['track', 'Face Scan Pulse Analysis', {\n          source: 'face_scan_landing',\n          status,\n          provider: result && result.provider ? result.provider : 'local_pos_chrom',\n          heart_rate_bpm: result && Number.isFinite(result.heartRateBpm) ? Math.round(result.heartRateBpm) : '',\n          balance_score: result && Number.isFinite(result.balanceScore) ? Math.round(result.balanceScore) : '',\n          signal_quality: result && result.signalQuality ? result.signalQuality : '',\n          confidence: result && result.confidence ? result.confidence : '',\n          referral_code: this.getReferralCode(),\n          referred_by: this.getReferredByCode(),\n          locale: this.config.locale || '',\n          url: window.location.href,\n        }]);\n        return true;\n      } catch (error) {\n        return false;\n      }\n    }\n\n    async analyze(frame) {\n      if (!frame || !frame.box || !this.video.videoWidth) return;\n      /* result is locked: keep the camera alive (snapshot/share need it) but stop\n         re-sampling so the displayed age/scores no longer wobble. unlockResult()\n         (called on \"start new scan\") re-arms. */\n      if (this.resultLocked) return;\n\n      const quality = this.assessQuality(frame);\n      if (!quality.good) {\n        this.setQualityStatus(quality.message);\n        this.stableFrames = 0;\n        this._framedAt = 0;   // lost a clean frame \u2192 replay the framing beat\n        this.setAppStatus(this.label('scanRunning', 'Scanning'), quality.message, 0);\n        return;\n      }\n\n      /* FIX B \u2014 face-continuity guard: the rolling median has no identity check, so a\n         different person entering the frame (or the face leaving and another returning)\n         silently blends two people's ages. If the tracked face JUMPS (center moved a lot\n         or size ratio flipped \u2014 boxes are normalized [0,1]) or was absent for a while,\n         drop the accumulated samples so the new face scans clean. Pre-lock only. */\n      const box = frame.box;\n      const nowSample = Date.now();\n      if (this.samples.length) {\n        let switched = false;\n        if (this._lastSampleBox) {\n          const p = this._lastSampleBox;\n          const dc = Math.hypot((box.x + box.width / 2) - (p.x + p.width / 2), (box.y + box.height / 2) - (p.y + p.height / 2));\n          const curArea = box.width * box.height, prevArea = p.width * p.height;\n          const areaRatio = prevArea > 0 ? curArea / prevArea : 1;\n          if (dc > 0.3 || areaRatio > 2.2 || areaRatio < 0.45) switched = true;\n        }\n        if (this._lastSampleAt && (nowSample - this._lastSampleAt) > 2500) switched = true;\n        if (switched) {\n          this.samples = [];\n          this.ageTrace = [];\n          this.stableFrames = 0;\n          this._framedAt = 0;            // re-frame the new face before sampling it\n          this.lastGoodFrameAt = 0;      // the stashed frame belonged to the old face\n          this.hasVisibleResult = false; // don't let the funnel fire a result on a just-switched face\n          if (this.output && this.output.age) this.output.age.textContent = '--';\n        }\n      }\n\n      const skinMask = await this.skinMaskForFrame(frame);\n      const metrics = this.sampleFace(frame.box, skinMask);\n      if (!metrics) return;\n\n      this.setLightStatus(metrics.conditionsOk);\n      /* poor-light gate: once the window holds enough good samples, don't let\n         bad-light frames dilute them \u2014 pause instead of drifting the scores.\n         With a small window we still admit (a scan must never brick in dim light). */\n      if (!metrics.conditionsOk && this.samples.length >= 3) {\n        this._framedAt = 0;\n        this.setQualityStatus(this.label('lightPoor', 'Turn toward the light'));\n        this.setAppStatus(this.label('scanRunning', 'Scanning'), this.label('lightPoor', 'Turn toward the light'), this.stableFrames / STABLE_RESULT_TARGET);\n        return;\n      }\n\n      /* Feature 1 \u2014 pre-scan framing beat: once a well-framed, lit single face is held, wait\n         ~2s before the first sample so the median builds from a SETTLED face (not a mid-motion\n         first frame \u2014 a root cause of bad reads). Reuses the quality + light gates above; adds\n         no detector. Re-arms whenever framing/light is lost (poor-light + assessQuality-fail +\n         face-switch branches, and on restart). */\n      if (!this._framedAt) this._framedAt = nowSample;\n      if (nowSample - this._framedAt < FRAMING_MS) {\n        this.setQualityStatus(this.label('qualityGood', 'Good'));\n        this.setAppStatus(this.label('scanRunning', 'Scanning'), this.label('framingPrompt', 'Hold your face in the ring'), (nowSample - this._framedAt) / FRAMING_MS);\n        return;\n      }\n\n      const brightnessScore = clamp(100 - Math.abs(metrics.mean - 142) * 0.42, 55, 100);\n      const glowScore = clamp(104 - Math.abs(metrics.mean - 150) * 0.34 - metrics.lumaStd * 0.1, 50, 98);\n      /* tone/texture use the normalized inputs (gray-world + exposure rescale) so\n         color temperature and exposure don't masquerade as skin differences.\n         Constants are calibrated on REAL faces (5 portraits \u00D7 5 lighting variants,\n         2026-07-03 harness): colorStdNorm lands at ~13-86 and textureNorm (with the\n         luma-neighbor fix) at ~7-29 \u2014 the previous constants saturated both scores\n         at the 50 floor on every real face. Measured result with these values:\n         even/young skin \u2248 84-92, mid \u2248 65-80, extreme grain/shadow \u2248 50-60, and\n         lighting shifts move a given face \u22644pts. */\n      const toneScore = clamp(100 - metrics.colorStdNorm * 0.62 - metrics.lumaStdNorm * 0.04, 50, 98);\n      const textureScore = clamp(100 - metrics.textureNorm * 2.2 - metrics.shadowNorm * 3, 50, 98);\n      const vitality = Math.round(clamp(toneScore * 0.34 + textureScore * 0.3 + glowScore * 0.24 + brightnessScore * 0.12, 50, 97));\n      let ageRange = null;\n\n      if (this.ageEstimator && this.ageEstimator.ready) {\n        ageRange = await this.ageEstimator.predict(this.video, frame.box);\n        if (this.ageEstimator.failed) this.syncEngineStatus();\n      }\n\n      this.samples.push({\n        ageLow: ageRange ? ageRange.ageLow : null,\n        ageHigh: ageRange ? ageRange.ageHigh : null,\n        vitality,\n        glowScore,\n        toneScore,\n        textureScore,\n      });\n      if (this.samples.length > MAX_SAMPLES) this.samples.shift();\n      this.analyzedCount += 1;\n      if (!this.firstSampleAt) this.firstSampleAt = Date.now();\n      // FIX A \u2014 stash THIS quality-passing frame for the cloud age capture, and FIX B \u2014\n      // remember its box/time so the next pass can detect a face switch.\n      this.stashFrame();\n      this._lastSampleBox = { x: box.x, y: box.y, width: box.width, height: box.height };\n      this._lastSampleAt = nowSample;\n\n      /* convergence trace: the age the user currently SEES (window MEDIAN midpoint \u2014\n         robust to a single outlier frame). Recorded only once the window is FULL so\n         the 4 convergence readings aren't the early ramp; the lock waits until it\n         stops moving. */\n      if (this.samples.length >= MAX_SAMPLES) {\n        const wa = this.windowAge();\n        if (wa != null) { this.ageTrace.push(Math.round(wa)); if (this.ageTrace.length > 4) this.ageTrace.shift(); }\n      }\n\n      this.stableFrames = Math.min(STABLE_RESULT_TARGET, this.stableFrames + 1);\n      // Keep \"Scanning\" until the result actually LOCKS (the funnel then flips this pill to\n      // \"Analyse fertig \u2713\" on lock, line ~1621 shows scanReady when locked). Claiming\n      // \"ready\" at stable-frames \u2014 while the number is still being finalised behind the\n      // loader \u2014 contradicted what the user saw on screen.\n      this.setAppStatus(\n        this.label('scanRunning', 'Scanning'),\n        this.label('stableShort', 'Stable'),\n        this.stableFrames / STABLE_RESULT_TARGET\n      );\n      this.setQualityStatus(this.label('qualityGood', 'Good'));\n      this.updateOutputs();\n\n      /* lock only once the age estimate has CONVERGED, not on a raw timer.\n         Requirements: full sample window, full-window age coverage, and the\n         displayed age stable within \u00B11y across the last 4 readings (~3.5s).\n         The first owner test showed a timer-lock freezing an early, still-noisy\n         age that the rolling window would have corrected. Overtime valve at\n         ~20s of valid frames: settle on the window median rather than\n         calibrating forever in a jittery scene. */\n      if (this.config.lockResult !== false && this.samples.length >= MAX_SAMPLES\n          && this.stableFrames >= STABLE_RESULT_TARGET && this.analyzedCount >= MAX_SAMPLES + 4) {\n        const ageActive = this.ageEstimator && this.ageEstimator.ready && !this.ageEstimator.failed;\n        const ageWindow = this.samples.filter((s) => Number.isFinite(s.ageLow) && Number.isFinite(s.ageHigh));\n        const converged = this.ageTrace.length >= 4\n          && Math.max.apply(null, this.ageTrace) - Math.min.apply(null, this.ageTrace) <= 2;\n        const ageSettled = !ageActive || (ageWindow.length >= MAX_SAMPLES && converged);\n        /* overtime valves: frame-count OR 45s wall-clock (a permanently marginal\n           scene stops re-sampling before ~22 valid frames, so a clock is needed too) */\n        const overtime = this.analyzedCount >= 24 || (this.firstSampleAt && Date.now() - this.firstSampleAt > 45000);\n        if (ageSettled || overtime) this.resultLocked = true;\n      }\n    }\n\n    /* window MEDIAN of the per-sample age midpoints (the number the user sees) \u2014\n       one outlier frame no longer shifts the locked value. null if no age yet. */\n    windowAge() {\n      const w = this.samples\n        .filter((s) => Number.isFinite(s.ageLow) && Number.isFinite(s.ageHigh))\n        .map((s) => (s.ageLow + s.ageHigh) / 2)\n        .sort((a, b) => a - b);\n      if (!w.length) return null;\n      const mid = w.length >> 1;\n      return w.length % 2 ? w[mid] : (w[mid - 1] + w[mid]) / 2;\n    }\n\n    /* FIX A \u2014 copy the current (quality-passing) video frame into ONE reused offscreen\n       canvas at the same 720px/JPEG scale the funnel uses. This becomes the frame the\n       cloud age model receives, so it always reflects the SCANNED face \u2014 never whatever\n       is live at the instant the result fires (the capture race). */\n    stashFrame() {\n      const video = this.video;\n      if (!video || !video.videoWidth || !video.videoHeight) return;\n      const longSide = Math.max(video.videoWidth, video.videoHeight);\n      const scale = longSide > 720 ? 720 / longSide : 1;\n      const w = Math.round(video.videoWidth * scale), h = Math.round(video.videoHeight * scale);\n      if (!this._stashCanvas) this._stashCanvas = document.createElement('canvas');\n      const cv = this._stashCanvas;\n      if (cv.width !== w) cv.width = w;\n      if (cv.height !== h) cv.height = h;\n      try { cv.getContext('2d').drawImage(video, 0, 0, w, h); this.lastGoodFrameAt = Date.now(); } catch (e) {}\n    }\n\n    // The freshest quality-passing frame for the cloud capture, or null if none stashed.\n    getLastGoodFrame() {\n      if (!this._stashCanvas || !this.lastGoodFrameAt) return null;\n      return { canvas: this._stashCanvas, at: this.lastGoodFrameAt };\n    }\n\n    unlockResult() {\n      this.resultLocked = false;\n      this.samples = [];\n      this.ageTrace = [];\n      this.analyzedCount = 0;\n      this.firstSampleAt = 0;\n      this.stableFrames = 0;\n      this.lastGoodFrameAt = 0;       // FIX A \u2014 the stashed frame belonged to the finished scan\n      this._lastSampleBox = null;     // FIX B \u2014 don't compare the new scan against the old face\n      this._lastSampleAt = 0;\n      this._framedAt = 0;             // Feature 1 \u2014 a rescan replays the framing beat\n      // A restarted scan must not inherit the previous face's result: without these, the\n      // funnel's 400ms readiness poll sees stale hasVisibleResult/age text and declares\n      // the NEW scan ready instantly with the OLD number.\n      this.hasVisibleResult = false;\n      if (this.output && this.output.age) this.output.age.textContent = '--';\n    }\n\n    assessQuality(frame) {\n      if (frame.faceCount > 1) {\n        return { good: false, message: this.label('multipleFaces', 'One face only') };\n      }\n      const box = frame.box;\n      if (!box) {\n        return { good: false, message: this.label('noFace', 'No face') };\n      }\n      const centerX = box.x + box.width / 2;\n      const centerY = box.y + box.height / 2;\n      const centered = Math.abs(centerX - 0.5) < 0.3 && Math.abs(centerY - 0.5) < 0.34;\n      const sizeOk = box.width > 0.16 && box.width < 0.82 && box.height > 0.2;\n      const motionOk = !Number.isFinite(frame.motion) || frame.motion < 0.036;\n      if (!centered || !sizeOk || !motionOk) {\n        return { good: false, message: this.label('qualityHold', 'Hold still') };\n      }\n      return { good: true, message: this.label('singleFace', 'Face detected') };\n    }\n\n    async skinMaskForFrame(frame) {\n      if (!this.skinParser || !this.skinParser.ready || !frame || !frame.box) return this.latestSkinMask;\n      const now = Date.now();\n      if (this.latestSkinMask && now - this.lastSkinParsedAt < SKIN_PARSE_INTERVAL) return this.latestSkinMask;\n      const result = await this.skinParser.parse(this.video, frame.box);\n      if (this.skinParser.failed) this.syncEngineStatus();\n      if (result) {\n        this.latestSkinMask = result;\n        this.lastSkinParsedAt = now;\n      }\n      return this.latestSkinMask;\n    }\n\n    sampleFace(box, skinMask) {\n      const sx = Math.max(0, Math.floor(box.x * this.video.videoWidth));\n      const sy = Math.max(0, Math.floor(box.y * this.video.videoHeight));\n      const sw = Math.min(this.video.videoWidth - sx, Math.floor(box.width * this.video.videoWidth));\n      const sh = Math.min(this.video.videoHeight - sy, Math.floor(box.height * this.video.videoHeight));\n      if (sw < 24 || sh < 24) return null;\n\n      this.sampleCtx.clearRect(0, 0, this.sampleCanvas.width, this.sampleCanvas.height);\n      this.sampleCtx.drawImage(this.video, sx, sy, sw, sh, 0, 0, this.sampleCanvas.width, this.sampleCanvas.height);\n      const image = this.sampleCtx.getImageData(0, 0, this.sampleCanvas.width, this.sampleCanvas.height);\n      const data = image.data;\n      const width = image.width;\n      const height = image.height;\n      let total = 0;\n      let totalSq = 0;\n      let colorTotal = 0;\n      let textureTotal = 0;\n      let count = 0;\n      let underEyeTotal = 0;\n      let underEyeCount = 0;\n      let cheekTotal = 0;\n      let cheekCount = 0;\n      let rTotal = 0;\n      let gTotal = 0;\n      let bTotal = 0;\n      const px = []; // compact r,g,b buffer for the gray-world second pass (~1-4k samples)\n\n      for (let y = 4; y < height - 4; y += 2) {\n        for (let x = 4; x < width - 4; x += 2) {\n          const i = (y * width + x) * 4;\n          const r = data[i];\n          const g = data[i + 1];\n          const b = data[i + 2];\n          const nx = x / width;\n          const ny = y / height;\n          if (skinMask) {\n            if (!this.isSkinSample(skinMask, nx, ny)) continue;\n          } else if (!this.isFallbackSkinRoi(nx, ny)) {\n            continue;\n          }\n          const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n          if (luma < 35 || luma > 245) continue;\n          /* neighbor LUMA (2px right / 2 rows down). The old code averaged the raw\n             R byte and G byte at those offsets against luma, so \"texture\" tracked\n             color balance (\u00B120pts with light temperature) instead of surface detail. */\n          const iR = i + 8;\n          const iD = i + width * 8;\n          const lumaRight = 0.2126 * (data[iR] || r) + 0.7152 * (data[iR + 1] || g) + 0.0722 * (data[iR + 2] || b);\n          const lumaDown = 0.2126 * (data[iD] || r) + 0.7152 * (data[iD + 1] || g) + 0.0722 * (data[iD + 2] || b);\n          const neighbor = (lumaRight + lumaDown) / 2;\n          const colorSpread = Math.max(r, g, b) - Math.min(r, g, b);\n          total += luma;\n          totalSq += luma * luma;\n          colorTotal += colorSpread;\n          textureTotal += Math.abs(luma - neighbor);\n          rTotal += r;\n          gTotal += g;\n          bTotal += b;\n          px.push(r, g, b);\n          count += 1;\n\n          const isUnderEye = ny > 0.34 && ny < 0.52 && ((nx > 0.18 && nx < 0.43) || (nx > 0.57 && nx < 0.82));\n          const isCheek = ny > 0.54 && ny < 0.75 && ((nx > 0.16 && nx < 0.4) || (nx > 0.6 && nx < 0.84));\n          if (isUnderEye) {\n            underEyeTotal += luma;\n            underEyeCount += 1;\n          }\n          if (isCheek) {\n            cheekTotal += luma;\n            cheekCount += 1;\n          }\n        }\n      }\n\n      if (!count || count < (skinMask ? 420 : 300)) return null;\n      const mean = total / count;\n      const variance = Math.max(0, totalSq / count - mean * mean);\n      const underEye = underEyeCount ? underEyeTotal / underEyeCount : mean;\n      const cheek = cheekCount ? cheekTotal / cheekCount : mean;\n      const lumaStd = Math.sqrt(variance);\n\n      /* Lighting normalization for the skin-ish metrics (tone/texture):\n         gray-world white balance removes color-temperature casts, exposure\n         rescale to the reference mean (150) removes brightness dependence.\n         Gains/scale are clamped so extreme scenes can't overcorrect.\n         Glow + brightness intentionally stay RAW \u2014 they ARE the light metrics. */\n      const scale = clamp(150 / Math.max(1, mean), 0.6, 1.8);\n      const grayMean = (rTotal + gTotal + bTotal) / (3 * count);\n      /* wide gain clamps: skin is inherently warm (blue gain regularly needs ~1.5),\n         tight clamps would saturate on every normal face and leave casts uncorrected */\n      const gainR = clamp(grayMean / Math.max(1, rTotal / count), 0.55, 1.9);\n      const gainG = clamp(grayMean / Math.max(1, gTotal / count), 0.55, 1.9);\n      const gainB = clamp(grayMean / Math.max(1, bTotal / count), 0.55, 1.9);\n      let colorNormTotal = 0;\n      for (let p = 0; p < px.length; p += 3) {\n        const nr = px[p] * gainR;\n        const ng = px[p + 1] * gainG;\n        const nb = px[p + 2] * gainB;\n        colorNormTotal += Math.max(nr, ng, nb) - Math.min(nr, ng, nb);\n      }\n\n      /* usable-conditions check: exposure near reference band, no harsh contrast */\n      const conditionsOk = mean >= 90 && mean <= 205 && lumaStd < 55;\n\n      return {\n        mean,\n        lumaStd,\n        colorStd: colorTotal / count,\n        texture: textureTotal / count,\n        shadow: clamp((cheek - underEye) / 52, 0, 1),\n        lumaStdNorm: lumaStd * scale,\n        colorStdNorm: (colorNormTotal / count) * scale,\n        textureNorm: (textureTotal / count) * scale,\n        shadowNorm: clamp(((cheek - underEye) * scale) / 52, 0, 1),\n        conditionsOk,\n        skinMaskRatio: skinMask ? skinMask.skinRatio : 0,\n        source: skinMask ? skinMask.source : 'landmark-skin-roi',\n      };\n    }\n\n    isFallbackSkinRoi(nx, ny) {\n      const forehead = nx > 0.36 && nx < 0.64 && ny > 0.2 && ny < 0.34;\n      const leftCheek = nx > 0.17 && nx < 0.4 && ny > 0.5 && ny < 0.72;\n      const rightCheek = nx > 0.6 && nx < 0.83 && ny > 0.5 && ny < 0.72;\n      const noseBridge = nx > 0.43 && nx < 0.57 && ny > 0.36 && ny < 0.57;\n      return forehead || leftCheek || rightCheek || noseBridge;\n    }\n\n    isSkinSample(skinMask, nx, ny) {\n      const x = clamp(Math.floor(nx * skinMask.width), 0, skinMask.width - 1);\n      const y = clamp(Math.floor(ny * skinMask.height), 0, skinMask.height - 1);\n      return skinMask.mask[y * skinMask.width + x] === 1;\n    }\n\n    updateOutputs() {\n      if (!this.samples.length) return;\n      /* median over the sample window \u2014 robust to flicker/outlier frames (mean was drift-prone) */\n      const median = (key) => {\n        const values = this.samples.map((item) => item[key]).sort((a, b) => a - b);\n        const mid = values.length >> 1;\n        return values.length % 2 ? values[mid] : (values[mid - 1] + values[mid]) / 2;\n      };\n      const ageSamples = this.samples.filter((item) => Number.isFinite(item.ageLow) && Number.isFinite(item.ageHigh));\n      const vitality = Math.round(median('vitality'));\n      const glowScore = median('glowScore');\n      const toneScore = median('toneScore');\n      const textureScore = median('textureScore');\n      const hasSkinResult = Number.isFinite(vitality) && Number.isFinite(glowScore) && Number.isFinite(toneScore) && Number.isFinite(textureScore);\n\n      if (this.output.age) {\n        const wa = this.windowAge();\n        if (ageSamples.length && wa != null) {\n          this.output.age.textContent = `${Math.round(wa)}`;\n        } else {\n          this.output.age.textContent = '--';\n        }\n      }\n      this.updateAgeNote();\n      if (this.output.vitality) this.output.vitality.textContent = `${vitality}/100`;\n      if (this.output.vitalityNote) this.output.vitalityNote.textContent = this.label('vitalityNote', 'Higher = more balanced');\n      if (this.output.glow) this.output.glow.textContent = `${Math.round(glowScore)}/100`;\n      if (this.output.glowNote) this.output.glowNote.textContent = this.label('glowNote', 'Higher = more balanced light');\n      if (this.output.tone) this.output.tone.textContent = `${Math.round(toneScore)}/100`;\n      if (this.output.toneNote) this.output.toneNote.textContent = this.label('toneNote', 'Higher = more even');\n      if (this.output.texture) this.output.texture.textContent = `${Math.round(textureScore)}/100`;\n      if (this.output.textureNote) this.output.textureNote.textContent = this.label('textureNote', 'Higher = smoother');\n      const ageUnavailable = this.ageEstimator && this.ageEstimator.failed;\n      this.hasVisibleResult = hasSkinResult;\n      this.updateResultVisibility();\n    }\n\n    scoreLabel(score, type) {\n      if (type === 'texture') {\n        if (score >= 78) return this.label('textureCalm', 'Calm');\n        if (score >= 58) return this.label('textureModerate', 'Moderate');\n        return this.label('textureVisible', 'Visible');\n      }\n      if (score >= 78) return this.label('toneHigh', 'High');\n      if (score >= 58) return this.label('toneMedium', 'Medium');\n      return this.label('toneLow', 'Low');\n    }\n\n    mapPoint(x, y) {\n      const width = this.canvas.width;\n      const height = this.canvas.height;\n      const videoAspect = this.video.videoWidth / this.video.videoHeight;\n      const canvasAspect = width / height;\n      let renderedWidth = width;\n      let renderedHeight = height;\n      let offsetX = 0;\n      let offsetY = 0;\n\n      if (canvasAspect > videoAspect) {\n        renderedHeight = width / videoAspect;\n        offsetY = (height - renderedHeight) / 2;\n      } else {\n        renderedWidth = height * videoAspect;\n        offsetX = (width - renderedWidth) / 2;\n      }\n\n      return {\n        x: offsetX + x * renderedWidth,\n        y: offsetY + y * renderedHeight,\n      };\n    }\n\n    resizeCanvas() {\n      if (!this.canvas) return;\n      const rect = this.canvas.getBoundingClientRect();\n      const ratio = this.dpr();\n      const width = Math.max(1, Math.round(rect.width * ratio));\n      const height = Math.max(1, Math.round(rect.height * ratio));\n      if (this.canvas.width !== width || this.canvas.height !== height) {\n        this.canvas.width = width;\n        this.canvas.height = height;\n      }\n    }\n\n    dpr() {\n      return Math.min(window.devicePixelRatio || 1, 2);\n    }\n\n    handleVisibilityChange() {\n      if (document.hidden) {\n        this.stopLoop();\n      } else if (this.stream) {\n        this.startLoop();\n      }\n    }\n\n    stopCamera() {\n      this.stopLoop();\n      if (!this.stream) return;\n      this.stream.getTracks().forEach((track) => track.stop());\n      this.stream = null;\n      // clear the live marker \u2014 startCamera() early-returns on it, so leaving it set\n      // makes any later restart a silent no-op (dead scan with no recovery path)\n      if (this.root && this.root.dataset.camera === 'live') delete this.root.dataset.camera;\n    }\n  }\n\n  function clamp(value, min, max) {\n    return Math.min(max, Math.max(min, value));\n  }\n\n  function softmax(values) {\n    const max = values.reduce((largest, value) => Math.max(largest, value), -Infinity);\n    const exponents = values.map((value) => Math.exp(value - max));\n    const total = exponents.reduce((sum, value) => sum + value, 0) || 1;\n    return exponents.map((value) => value / total);\n  }\n\n  function normalizeSeries(values) {\n    const mean = values.reduce((sum, value) => sum + value, 0) / Math.max(values.length, 1);\n    const centered = values.map((value) => value - mean);\n    const variance = centered.reduce((sum, value) => sum + value * value, 0) / Math.max(centered.length, 1);\n    const std = Math.sqrt(Math.max(variance, 0.000001));\n    return centered.map((value) => value / std);\n  }\n\n  function combineProjection(xValues, yValues, sign) {\n    const x = normalizeSeries(xValues);\n    const y = normalizeSeries(yValues);\n    const xStd = standardDeviation(x);\n    const yStd = standardDeviation(y);\n    const alpha = xStd / Math.max(yStd, 0.000001);\n    return normalizeSeries(x.map((value, index) => value + sign * alpha * y[index]));\n  }\n\n  function standardDeviation(values) {\n    const mean = values.reduce((sum, value) => sum + value, 0) / Math.max(values.length, 1);\n    const variance = values.reduce((sum, value) => sum + Math.pow(value - mean, 2), 0) / Math.max(values.length, 1);\n    return Math.sqrt(Math.max(variance, 0));\n  }\n\n  function estimateBpmFromSignal(signal, fps) {\n    const clean = normalizeSeries(signal);\n    const n = clean.length;\n    if (n < 45 || !Number.isFinite(fps) || fps <= 0) throw new Error('Invalid rPPG signal');\n    const minHz = PULSE_MIN_BPM / 60;\n    const maxHz = PULSE_MAX_BPM / 60;\n    const stepHz = 0.02;\n    const spectrum = [];\n\n    for (let freq = minHz; freq <= maxHz; freq += stepHz) {\n      let real = 0;\n      let imag = 0;\n      for (let index = 0; index < n; index += 1) {\n        const windowValue = 0.54 - 0.46 * Math.cos((2 * Math.PI * index) / Math.max(n - 1, 1));\n        const angle = (2 * Math.PI * freq * index) / fps;\n        const value = clean[index] * windowValue;\n        real += value * Math.cos(angle);\n        imag -= value * Math.sin(angle);\n      }\n      spectrum.push({ freq, magnitude: Math.hypot(real, imag) });\n    }\n\n    let peak = spectrum[0];\n    spectrum.forEach((item) => {\n      if (item.magnitude > peak.magnitude) peak = item;\n    });\n    const noiseItems = spectrum.filter((item) => Math.abs(item.freq - peak.freq) > 0.18);\n    const noise = noiseItems.reduce((sum, item) => sum + item.magnitude, 0) / Math.max(noiseItems.length, 1);\n    return {\n      bpm: peak.freq * 60,\n      snr: peak.magnitude / Math.max(noise, 0.000001),\n    };\n  }\n\n  function roundedRect(ctx, x, y, width, height, radius) {\n    const size = Math.min(radius, width / 2, height / 2);\n    ctx.beginPath();\n    ctx.moveTo(x + size, y);\n    ctx.lineTo(x + width - size, y);\n    ctx.quadraticCurveTo(x + width, y, x + width, y + size);\n    ctx.lineTo(x + width, y + height - size);\n    ctx.quadraticCurveTo(x + width, y + height, x + width - size, y + height);\n    ctx.lineTo(x + size, y + height);\n    ctx.quadraticCurveTo(x, y + height, x, y + height - size);\n    ctx.lineTo(x, y + size);\n    ctx.quadraticCurveTo(x, y, x + size, y);\n    ctx.closePath();\n  }\n\n  // Lazy/manual instantiation hook (used by the Face Scan Funnel so the camera\n  // only starts when the user reaches the live step). Auto-init is unchanged for\n  // any root WITHOUT [data-face-scan-manual], so the standard landing page behaves\n  // exactly as before.\n  function initFaceScan(root) {\n    if (root && !root.__faceScanLanding) {\n      root.__faceScanLanding = new FaceScanLanding(root);\n    }\n    return root ? root.__faceScanLanding : null;\n  }\n  window.FaceScanLanding = FaceScanLanding;\n  window.initFaceScan = initFaceScan;\n\n  document.addEventListener('DOMContentLoaded', () => {\n    document.querySelectorAll(ROOT_SELECTOR).forEach((root) => {\n      if (root.hasAttribute('data-face-scan-manual')) return;\n      initFaceScan(root);\n    });\n  });\n})();\n"],
  "mappings": "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",
  "names": ["box", "points"]
}
