{
  "version": 3,
  "sources": ["/cdn/shop/t/4/assets/branding-logo-placer.js?blp=45"],
  "sourcesContent": ["import { StandardEvents } from '@shopify/events';\nimport { knockoutBackground } from '@theme/fp-logo-knockout';\n\n/**\n * Branded seat-cover popup: place a logo on a high/low-back photo.\n * Crop + background removal edit the file; drag on the seat is a preview only.\n * Logo files, placement text, count, an optional embroidery note, and a\n * composite screenshot go on the line item.\n *\n * @typedef {Object} LogoEntry\n * @property {number} id\n * @property {number} index\n * @property {File} file\n * @property {File} originalFile\n * @property {string} objectUrl\n * @property {string} workingSrc\n * @property {string | null} warpedSrc\n * @property {boolean} hasBackground\n * @property {number} x\n * @property {number} y\n * @property {number} scale\n * @property {number} rotation\n * @property {HTMLElement} layer\n * @property {HTMLImageElement} image\n * @property {HTMLElement | null} twinLayer\n * @property {HTMLImageElement | null} twinImage\n * @property {HTMLInputElement} fileInput\n * @property {HTMLInputElement} placementInput\n */\n\n/**\n * Placement previews go with the order as a reference for the embroiderer.\n * A 960px JPEG is about a tenth of the size of the 1200px PNG it replaces,\n * which is most of what made Add to cart slow on personalised covers.\n */\nconst PREVIEW_WIDTH = 960;\nconst PREVIEW_TYPE = 'image/jpeg';\nconst PREVIEW_QUALITY = 0.85;\n\n/**\n * Real-world scale of each seat photo, so the logo is drawn at its true\n * embroidered size. A customer expected a bigger logo because the preview\n * drew 145 mm at roughly 250 mm. `back` is the widest part of the seat back as\n * a share of the photo width, measured off the photo; `mm` is that width on\n * the real seat; `aspect` is the photo's height / width. Keyed by the photo's\n * file name. A new photo uses SEAT_PHOTO_DEFAULT until it is measured and added.\n */\nconst SEAT_PHOTOS = {\n  'fc3d60dd-97d5-4353-9340-03c727248278': { back: 0.56, mm: 480, aspect: 1.25 }, // tractor / telehandler / forklift, high back\n  'ChatGPT_Image_Aug_15_2026_at_10_46_36_PM': { back: 0.6, mm: 480, aspect: 1.25 }, // tractor / telehandler / forklift, low back\n  'excavator-high-back-branding': { back: 0.48, mm: 480, aspect: 1.23 },\n  'excavator-low-back-branding': { back: 0.47, mm: 480, aspect: 1.25 },\n  'loader-high-back-branding': { back: 0.52, mm: 480, aspect: 1.23 },\n  'loader-low-back-branding': { back: 0.5, mm: 480, aspect: 1.131 },\n  'van-driver-branding': { back: 0.54, mm: 480, aspect: 1.25 }, // van, lorry, pickup\n  'van-double-branding': { back: 0.82, mm: 900, aspect: 1.25 }, // both passenger seats across\n};\nconst SEAT_PHOTO_DEFAULT = { back: 0.52, mm: 480, aspect: 1.25 };\n\n/**\n * A logo processed for another seat's cover (see prepareLogo).\n * @typedef {Object} PreparedLogo\n * @property {File} file\n * @property {string} workingSrc\n * @property {string | null} warpedSrc\n * @property {boolean} hasBackground\n */\n\nclass BrandingLogoPlacer extends HTMLElement {\n  /** @type {Record<string, HTMLElement>} */\n  refs = {};\n\n  /** @type {Element | null} */\n  #section = null;\n\n  /** @type {HTMLFormElement | null} */\n  #form = null;\n\n  /** @type {HTMLElement | null} */\n  #target = null;\n\n  /** @type {LogoEntry[]} */\n  #logos = [];\n\n  /** @type {number} */\n  #nextId = 1;\n\n  /** @type {number | null} */\n  #activeId = null;\n\n  /** Which seat's slide is on screen (1-based). Only used on two-seat bundles. */\n  #seatStep = 1;\n\n  /** A reopened editor belongs to a newer interaction than a pending refresh. */\n  #editorSession = 0;\n  #productSelectVersion = 0;\n\n  /** Latest in-progress request for each seat (or upload slot). */\n  #uploadSequence = 0;\n  /** @type {Map<string, number>} */\n  #pendingUploads = new Map();\n\n  #pointerId = -1;\n\n  /** Bumped whenever something that shows in the placement preview changes. */\n  #previewVersion = 0;\n  /** The version the preview files on the form were drawn for. */\n  #attachedVersion = -1;\n  /** @type {Promise<void> | null} */\n  #attachPromise = null;\n  #attachPromiseVersion = -1;\n  /** @type {ReturnType<typeof setTimeout> | undefined} */\n  #prerenderTimer;\n  /** @type {'move' | 'crop-move' | 'crop-resize' | null} */\n  #dragMode = null;\n  /** @type {string | null} */\n  #cropHandle = null;\n  #cropStart = { x: 0, y: 0, left: 8, top: 8, width: 84, height: 84 };\n  #addInFlight = false;\n  #allowNextClick = false;\n  #wasHidden = true;\n  /** @type {'headrest' | 'backrest'} */\n  #position = 'backrest';\n\n  connectedCallback() {\n    this.#collectRefs();\n    this.#wasHidden = this.hasAttribute('hidden');\n\n    this.#section = this.closest('.shopify-section, dialog');\n    this.#section?.addEventListener(StandardEvents.productSelect, this.#handleProductSelect);\n\n    this.#form = /** @type {HTMLFormElement | null} */ (document.getElementById(this.dataset.formId || ''));\n    document.addEventListener('click', this.#onAddButtonClick, true);\n\n    this.refs.fileInput?.addEventListener('change', this.#onFileChange);\n    this.refs.openButton?.addEventListener('click', () => this.#openModal());\n    this.refs.clearButton?.addEventListener('click', (event) => {\n      event.preventDefault();\n      event.stopPropagation();\n      this.#askRemovePersonalisation();\n    });\n    this.refs.removeCancel?.addEventListener('click', () => this.#closeRemoveDialog());\n    this.refs.removeConfirm?.addEventListener('click', () => this.#removePersonalisation());\n    this.refs.removeDialog?.addEventListener('click', (event) => {\n      if (event.target === this.refs.removeDialog) this.#closeRemoveDialog();\n    });\n    this.refs.closeButton?.addEventListener('click', () => this.#closeModal({ discard: !this.#applied }));\n    this.refs.dialog?.addEventListener('cancel', (event) => {\n      event.preventDefault();\n      this.#closeModal({ discard: !this.#applied });\n    });\n    this.refs.finishButton?.addEventListener('click', () => {\n      // On a two-seat bundle this button walks forward through the slides\n      // before it becomes confirm on the last one.\n      if (this.#isPerSeat && this.#seatStep < this.#brandedSeats) {\n        this.#goToSeat(this.#seatStep + 1);\n        return;\n      }\n      this.#confirmPersonalisation();\n    });\n    this.refs.backButton?.addEventListener('click', () => this.#goToSeat(this.#seatStep - 1));\n    this.refs.seatSteps?.addEventListener('click', (event) => {\n      const tab = event.target instanceof Element ? event.target.closest('[data-seat]') : null;\n      if (tab instanceof HTMLElement) this.#goToSeat(Number(tab.dataset.seat));\n    });\n    this.refs.deleteButton?.addEventListener('click', () => {\n      if (this.#activeId != null) this.#removeLogo(this.#activeId);\n    });\n    this.refs.cropButton?.addEventListener('click', () => this.#applyCrop());\n    this.refs.resetButton?.addEventListener('click', () => this.#resetLogo());\n    this.refs.bgButton?.addEventListener('click', () => this.#removeBackground());\n    this.refs.posHead?.addEventListener('change', () => this.#setPosition('headrest'));\n    this.refs.posBack?.addEventListener('change', () => this.#setPosition('backrest'));\n    this.refs.noteField?.addEventListener('input', () => this.#syncEmbroideryNote());\n\n    this.refs.dialog?.addEventListener('click', (event) => {\n      if (event.target === this.refs.dialog) this.#closeModal({ discard: !this.#applied });\n    });\n\n    this.addEventListener('dragenter', this.#onDragEnter);\n    this.addEventListener('dragover', this.#onDragOver);\n    this.addEventListener('dragleave', this.#onDragLeave);\n    this.addEventListener('drop', this.#onDrop);\n\n    this.refs.cropBox?.addEventListener('pointerdown', this.#onCropPointerDown);\n    window.addEventListener('resize', this.#onResize);\n\n    this.#target = this.refs.previewHost || null;\n    this.#syncSeatPreview();\n    this.#syncPositionOptions();\n    this.#syncFormState();\n    this.#syncPersonalisedUi();\n  }\n\n  disconnectedCallback() {\n    this.#section?.removeEventListener(StandardEvents.productSelect, this.#handleProductSelect);\n    document.removeEventListener('click', this.#onAddButtonClick, true);\n    window.removeEventListener('resize', this.#onResize);\n    // Keep uploaded File objects. Horizon morph can disconnect this element\n    // while the customer is placing a logo; wiping files here is how branded\n    // orders land with Logo count 0.\n    this.#section = null;\n    this.#form = null;\n  }\n\n  #onResize = () => {\n    this.#applySlotToLogos();\n  };\n\n  #collectRefs() {\n    /** @type {Record<string, HTMLElement>} */\n    const refs = {};\n    for (const element of this.querySelectorAll('[ref]')) {\n      if (!(element instanceof HTMLElement)) continue;\n      const key = element.getAttribute('ref');\n      if (key) refs[key] = element;\n    }\n    this.refs = refs;\n  }\n\n  get #requireUpload() {\n    return this.dataset.requireUpload === 'true';\n  }\n\n  get #maxBytes() {\n    return (Number(this.dataset.maxFileMb) || 5) * 1024 * 1024;\n  }\n\n  get #isVanDouble() {\n    return this.dataset.previewGroup === 'van-double';\n  }\n\n  /** Headrest spacing: how far each logo sits from the middle of the photo, in % of its width. */\n  get #vanDoubleSpread() {\n    return Number(this.dataset.vanDoubleSpread || 19.5);\n  }\n\n  /** The backrest is wider than the headrest, so its two logos sit further apart. */\n  get #vanDoubleBackrestSpread() {\n    return Number(this.dataset.vanDoubleBackrestSpread || 20);\n  }\n\n  get #vanDoubleHeadrestY() {\n    return Number(this.dataset.vanDoubleHeadrestY || 16);\n  }\n\n  get #vanDoubleHeadrestScale() {\n    return Number(this.dataset.vanDoubleHeadrestScale || 14);\n  }\n\n  get #maxLogos() {\n    if (this.#isVanDouble) return 1;\n    return Math.max(1, Math.min(4, Number(this.dataset.maxLogos) || 1));\n  }\n\n  /**\n   * Bundles sell two covers under one variant, so \"Both seats branded\" needs a\n   * logo per seat. A van double is still one logo stitched on both seats.\n   */\n  get #brandedSeats() {\n    if (this.#isVanDouble) return 1;\n    return Math.max(1, Math.min(this.#maxLogos, Number(this.dataset.brandedSeats) || 1));\n  }\n\n  get #isPerSeat() {\n    return this.#brandedSeats > 1;\n  }\n\n  /** The lowest seat still without a logo, or the last seat when all are done. */\n  #firstEmptySeat() {\n    for (let n = 1; n <= this.#brandedSeats; n += 1) {\n      if (!this.#logoForSeat(n)) return n;\n    }\n    return this.#brandedSeats;\n  }\n\n  /** @param {number} seat @returns {any} */\n  #logoForSeat(seat) {\n    return this.#logos.find((logo) => logo.seat === seat) ?? null;\n  }\n\n  /**\n   * One slide per seat: only the seat on screen shows its logo, so the\n   * customer reviews each cover on its own rather than both stacked on one.\n   */\n  #goToSeat(seat) {\n    const target = Math.max(1, Math.min(this.#brandedSeats, seat));\n    this.#seatStep = target;\n    const logo = this.#logoForSeat(target);\n    if (logo) {\n      this.#setActive(logo.id);\n      this.#position = this.#positionFor(logo);\n      this.#syncPositionOptions();\n    } else {\n      this.#activeId = null;\n    }\n    this.#clearError();\n    this.#syncSeatSlides();\n    this.#syncFormState();\n    this.#resetCropBox();\n    this.#drawEditor();\n  }\n\n  /** Show only the active seat's layer; non-per-seat products show them all. */\n  #syncSeatSlides() {\n    for (const logo of this.#logos) {\n      const mine = !this.#isPerSeat || logo.seat === this.#seatStep;\n      logo.layer.toggleAttribute('hidden', !mine);\n      logo.twinLayer?.toggleAttribute('hidden', !mine);\n    }\n  }\n\n  get #formId() {\n    return this.dataset.formId || '';\n  }\n\n  get #active() {\n    return this.#logos.find((logo) => logo.id === this.#activeId) ?? null;\n  }\n\n  get #dialog() {\n    return /** @type {HTMLDialogElement | undefined} */ (this.refs.dialog);\n  }\n\n  #openModal() {\n    const opening = !this.#dialog?.open;\n    if (opening) this.#editorSession += 1;\n    // Only choose a slide when the modal is actually opening. Re-entering it\n    // while open (an upload calls this) must leave the customer where they are.\n    if (this.#isPerSeat && !this.#dialog?.open) {\n      const want = this.#firstEmptySeat();\n      if (want !== this.#seatStep) {\n        this.#seatStep = want;\n        const logo = this.#logoForSeat(want);\n        this.#activeId = logo ? logo.id : null;\n      }\n      this.#syncSeatSlides();\n    }\n    this.#syncSeatPreview();\n    this.#syncPositionOptions();\n    this.#ensureTwinLayers();\n    if (!this.#dialog?.open) this.#dialog?.showModal();\n    if (opening && this.#dialog?.open) this.#trackPersonalisation('personalisation_start');\n    this.#drawEditor();\n  }\n\n  #trackPersonalisation(name) {\n    window.fpAnalytics?.track(name, {\n      product_id: this.dataset.productId || '',\n      product_handle: this.dataset.productHandle || '',\n      branded_seats: this.#applied ? this.#requiredLogoCount() : 0,\n    });\n  }\n\n  /**\n   * @param {{ discard?: boolean }} [options]\n   */\n  #closeModal(options = {}) {\n    this.#cancelPendingUploads();\n    this.#syncEmbroideryNote();\n    if (this.#dialog?.open) this.#dialog.close();\n    // Leaving without personalising: the editor's own messages (\"Please upload\n    // a logo\u2026\") no longer apply to the unbranded cover left on the page.\n    if (!this.#applied) this.#clearError();\n    if (options.discard && !this.#applied) this.#clearAllLogos();\n    this.#syncFormState();\n    this.#syncPersonalisedUi();\n  }\n\n  get #applied() {\n    return this.dataset.applied === 'true';\n  }\n\n  /** @param {boolean} value */\n  #setApplied(value) {\n    this.dataset.applied = value ? 'true' : 'false';\n    this.#syncPersonalisedUi();\n    this.#syncFormState();\n  }\n\n  #syncPersonalisedUi() {\n    const applied = this.#applied;\n    this.refs.openButton?.classList.toggle('is-applied', applied);\n    this.refs.openButton?.setAttribute('aria-pressed', String(applied));\n    this.refs.clearButton?.toggleAttribute('hidden', !applied);\n  }\n\n  #askRemovePersonalisation() {\n    const dialog = /** @type {HTMLDialogElement | undefined} */ (this.refs.removeDialog);\n    if (!dialog) {\n      this.#removePersonalisation();\n      return;\n    }\n    if (!dialog.open) dialog.showModal();\n  }\n\n  #closeRemoveDialog() {\n    const dialog = /** @type {HTMLDialogElement | undefined} */ (this.refs.removeDialog);\n    if (dialog?.open) dialog.close();\n  }\n\n  #removePersonalisation() {\n    const wasApplied = this.#applied;\n    this.#closeRemoveDialog();\n    this.#clearAllLogos();\n    this.#selectBrandingValue('Unbranded');\n    this.#setApplied(false);\n    if (wasApplied) this.#trackPersonalisation('personalisation_remove');\n  }\n\n  #targetBrandingValue() {\n    if (this.#isPerSeat) {\n      const filled = this.#logos.filter((logo) => logo.seat).length;\n      return filled >= 2 ? 'Both seats branded' : 'One seat branded';\n    }\n    return this.dataset.brandedValue || 'Branded';\n  }\n\n  /** @param {string} value */\n  #selectBrandingValue(value) {\n    const picker = this.#section?.querySelector('variant-picker') || document.querySelector('variant-picker');\n    if (!(picker instanceof HTMLElement)) return;\n    const radios = [...picker.querySelectorAll('input[data-option-name=\"Branding\"]')].filter(\n      (radio) => radio instanceof HTMLInputElement\n    );\n    if (!radios.length) return;\n\n    const wanted = (value || '').toLowerCase();\n    const match = radios.find((radio) => radio.value.toLowerCase() === wanted)\n      || radios.find((radio) => {\n        const down = radio.value.toLowerCase();\n        if (down.includes('unbranded')) return wanted.includes('unbranded');\n        if (wanted.includes('both')) return down.includes('both');\n        if (wanted.includes('one')) return down.includes('one');\n        return down.includes('branded') && !down.includes('unbranded');\n      });\n    if (!(match instanceof HTMLInputElement)) return;\n    if (!match.checked) {\n      match.checked = true;\n      match.dispatchEvent(new Event('change', { bubbles: true }));\n    }\n  }\n\n  #confirmPersonalisation() {\n    if (this.#pendingUploads.size) {\n      this.#showError('Your logo is still loading. Please wait before confirming personalisation.');\n      return;\n    }\n    const ready = this.#logos.filter((logo) => logo.file instanceof File && logo.file.size > 0).length;\n    if (this.#requireUpload && ready < 1) {\n      this.#showError('Please upload a logo before confirming personalisation.');\n      return;\n    }\n    this.#clearError();\n    this.#setApplied(true);\n    this.#selectBrandingValue(this.#targetBrandingValue());\n    this.#trackPersonalisation('personalisation_confirm');\n    this.#closeModal();\n  }\n\n  #detectSeatKind() {\n    const forced = this.dataset.seatStyle;\n    if (forced === 'high' || forced === 'low') return forced;\n    // One product per machine with a \"Seat back\" option: follow the choice.\n    const picker = this.#section?.querySelector('variant-picker') || document.querySelector('variant-picker');\n    const seatBack = picker?.querySelector('input[data-option-name=\"Seat back\"]:checked');\n    if (seatBack instanceof HTMLInputElement) return /low/i.test(seatBack.value) ? 'low' : 'high';\n    const haystack = `${this.dataset.productHandle || ''} ${this.dataset.productTitle || ''}`.toLowerCase();\n    if (/\\blow[\\s-]?back\\b|\\blowback\\b|\\blow\\b/.test(haystack) && !/\\bhigh[\\s-]?back\\b/.test(haystack)) {\n      return 'low';\n    }\n    return 'high';\n  }\n\n  #syncSeatPreview() {\n    const kind = this.#detectSeatKind();\n    this.dataset.seatKind = kind;\n    const high = this.dataset.highImage || '';\n    const low = this.dataset.lowImage || '';\n    const src = kind === 'low' ? low || high : high || low;\n    const seat = /** @type {HTMLImageElement | undefined} */ (this.refs.seatImage);\n    const placeholder = this.refs.seatPlaceholder;\n    const label = this.refs.seatLabel;\n\n    if (label) {\n      label.textContent = this.#seatKindLabel();\n    }\n\n    if (seat && src) {\n      seat.hidden = false;\n      if (seat.src !== src) {\n        seat.src = src;\n        seat.alt = `${kind}-back seat cover preview`;\n      }\n      if (placeholder) placeholder.hidden = true;\n    } else if (placeholder) {\n      placeholder.hidden = false;\n    }\n\n    if (this.refs.modalTitle && this.dataset.productTitle) {\n      this.refs.modalTitle.textContent = this.dataset.productTitle;\n    }\n    const brandedPrice = this.#brandedDisplayPrice();\n    if (brandedPrice) {\n      if (this.refs.modalPrice) this.refs.modalPrice.textContent = brandedPrice;\n      if (this.refs.footerPrice) this.refs.footerPrice.textContent = brandedPrice;\n    }\n  }\n\n  #requiredLogoCount() {\n    if (!this.#isPerSeat) return 1;\n    return this.#targetBrandingValue().toLowerCase().includes('both') ? 2 : 1;\n  }\n\n  /**\n   * The Branded variant price, which already includes embroidery.\n   * @returns {string}\n   */\n  #brandedDisplayPrice() {\n    if (this.#isPerSeat && this.#requiredLogoCount() >= 2 && this.dataset.bothSeatsPrice) {\n      return this.dataset.bothSeatsPrice;\n    }\n    return this.dataset.personalisedPrice || this.dataset.productPrice || this.dataset.productPriceBase || '';\n  }\n\n  #syncPositionOptions() {\n    const lowBack = this.#detectSeatKind() === 'low';\n    this.refs.headrestOption?.toggleAttribute('hidden', lowBack);\n    if (lowBack && this.#position === 'headrest') {\n      this.#setPosition('backrest');\n      return;\n    }\n    // Keep the radios showing the placement of the seat on screen.\n    const head = /** @type {HTMLInputElement | undefined} */ (this.refs.posHead);\n    const back = /** @type {HTMLInputElement | undefined} */ (this.refs.posBack);\n    if (head) head.checked = this.#position === 'headrest';\n    if (back) back.checked = this.#position === 'backrest';\n  }\n\n  get #fixedScale() {\n    return this.#scaleFor(null);\n  }\n\n  /** Set while a bundle preview is drawn on another group's photo. */\n  #sizingPhotoSrc = '';\n\n  get #seatPhoto() {\n    let src = this.#sizingPhotoSrc;\n    if (!src) {\n      const high = this.dataset.highImage || '';\n      const low = this.dataset.lowImage || '';\n      src = this.#detectSeatKind() === 'low' ? low || high : high || low;\n    }\n    const match = Object.keys(SEAT_PHOTOS).find((name) => src.includes(name));\n    return match ? SEAT_PHOTOS[match] : SEAT_PHOTO_DEFAULT;\n  }\n\n  /** Logo width as a % of the photo width when stitched at logoMm. */\n  get #trueScale() {\n    const photo = this.#seatPhoto;\n    const mm = Number(this.dataset.logoMm) || 145;\n    return (mm / photo.mm) * photo.back * 100;\n  }\n\n  /** @param {any} logo */\n  #scaleFor(logo) {\n    const headrest = this.#positionFor(logo) === 'headrest' && this.#detectSeatKind() !== 'low';\n    const scale = Math.min(this.#trueScale, Number(this.dataset.fixedScale) || 36);\n    if (this.#isVanDouble && headrest) return Math.min(scale, this.#vanDoubleHeadrestScale);\n    return scale;\n  }\n\n  /** @param {LogoEntry} logo */\n  #logoMaxHeight(logo) {\n    const headrest = this.#positionFor(logo) === 'headrest' && this.#detectSeatKind() !== 'low';\n    // The same mm on the same photo, so a tall logo is capped at logoMm too.\n    let height = this.#trueScale / this.#seatPhoto.aspect;\n    if (this.#isVanDouble && headrest) height = Math.min(height, 7);\n    if (!logo.hasBackground) return height;\n    return height * (headrest ? 0.78 : 0.86);\n  }\n\n  /** @returns {{ x: number, y: number }} */\n  /** @param {'headrest'|'backrest'} [position] */\n  #slotCoords(position) {\n    const lowBack = this.#detectSeatKind() === 'low';\n    const useHead = (position || this.#position) === 'headrest' && !lowBack;\n    if (useHead) {\n      return {\n        x: Number(this.dataset.headrestX || 50),\n        y: this.#isVanDouble ? this.#vanDoubleHeadrestY : Number(this.dataset.headrestY || 11),\n      };\n    }\n    return {\n      x: Number(this.dataset.backrestX || 50),\n      y: Number(this.dataset.backrestY || 31),\n    };\n  }\n\n  /**\n   * @param {'headrest' | 'backrest'} position\n   */\n  #setPosition(position) {\n    const lowBack = this.#detectSeatKind() === 'low';\n    const next = lowBack ? 'backrest' : position;\n    this.#position = next;\n    const head = /** @type {HTMLInputElement | undefined} */ (this.refs.posHead);\n    const back = /** @type {HTMLInputElement | undefined} */ (this.refs.posBack);\n    if (head) head.checked = next === 'headrest';\n    if (back) back.checked = next === 'backrest';\n    // Each seat carries its own placement, so Headrest/Backrest applies to the\n    // cover on screen and leaves the other one alone.\n    if (this.#isPerSeat) {\n      const mine = this.#logoForSeat(this.#seatStep);\n      if (mine) {\n        mine.position = next;\n        this.#applyLogoTransform(mine);\n      }\n    } else {\n      for (const logo of this.#logos) logo.position = next;\n      this.#applySlotToLogos();\n    }\n    this.#writePlacements();\n  }\n\n  /** The placement this logo sits at, falling back to the shared one. */\n  #positionFor(logo) {\n    const want = (logo && logo.position) || this.#position;\n    return this.#detectSeatKind() === 'low' ? 'backrest' : want;\n  }\n\n  #applySlotToLogos() {\n    this.#ensureTwinLayers();\n    for (const logo of this.#logos) {\n      const slot = this.#slotCoords(this.#positionFor(logo));\n      logo.x = slot.x;\n      logo.y = slot.y;\n      logo.scale = this.#scaleFor(logo);\n      this.#applyLogoTransform(logo);\n    }\n  }\n\n  /** @param {any} event */\n  #handleProductSelect = (event) => {\n    if (!(event.target instanceof Element) || event.target.closest('product-card')) return;\n    const version = ++this.#productSelectVersion;\n    const editorSession = this.#editorSession;\n\n    event.promise\n      ?.then((/** @type {any} */ { detail }) => {\n        if (version !== this.#productSelectVersion || !detail?.html) return;\n        const { html, newProduct } = detail;\n        const changedProduct = Boolean(newProduct && String(newProduct.id) !== String(this.dataset.productId));\n        const reopenedEditor = !changedProduct && this.#dialog?.open && this.#editorSession !== editorSession;\n\n        if (newProduct) this.dataset.productId = newProduct.id;\n        else if (detail.productId && detail.productId !== this.dataset.productId) return;\n\n        const updated = html.querySelector(`branding-logo-placer[data-block-id=\"${this.dataset.blockId}\"]`);\n        if (!updated) return;\n\n        const nowApplied = updated.getAttribute('data-applied') === 'true';\n        this.dataset.applied = nowApplied ? 'true' : 'false';\n\n        if (updated instanceof HTMLElement) {\n          for (const key of [\n            'productTitle',\n            'productHandle',\n            'productPrice',\n            'productPriceBase',\n            'personalisedPrice',\n            'bothSeatsPrice',\n            'personaliseUplift',\n            'brandedValue',\n            'highImage',\n            'lowImage',\n            'seatKind',\n            'previewGroup',\n            'maxLogos',\n            'brandedSeats',\n            'vanDoubleSpread',\n            'vanDoubleBackrestSpread',\n            'vanDoubleHeadrestY',\n            'vanDoubleHeadrestScale',\n          ]) {\n            const attr = key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);\n            const value = updated.getAttribute(`data-${attr}`);\n            if (value != null) this.dataset[key] = value;\n          }\n        }\n\n        this.#syncSeatPreview();\n        this.#syncPositionOptions();\n        this.#ensureTwinLayers();\n        this.#syncFormState();\n        this.#syncPersonalisedUi();\n\n        // A previous Remove click must not dismiss an editor the shopper has\n        // since reopened, or discard the new artwork they are preparing.\n        if (!this.#applied && !reopenedEditor) {\n          this.#closeModal({ discard: true });\n          this.#clearError();\n        }\n      })\n      .catch((/** @type {any} */ error) => {\n        if (error?.name !== 'AbortError') console.warn('[branding-logo-placer] Event promise rejected:', error);\n      });\n  };\n\n  #syncFormState() {\n    const active = this.#applied;\n    const count = this.#logos.length;\n    const { fileInput, countInput, previewInput, dropzoneTitle, editor, statusText, openButton, positionFieldset, positionInput, sizeInput } = this.refs;\n\n    if (fileInput instanceof HTMLInputElement) {\n      fileInput.disabled = !this.#isPerSeat && count >= this.#maxLogos && this.#maxLogos > 1;\n    }\n    if (countInput instanceof HTMLInputElement) {\n      countInput.disabled = !active;\n      countInput.value = active ? String(count) : '0';\n    }\n    if (previewInput instanceof HTMLInputElement) previewInput.disabled = !active;\n    if (this.refs.previewInput2 instanceof HTMLInputElement) {\n      this.refs.previewInput2.disabled = !active || !this.#isPerSeat;\n    }\n    if (positionInput instanceof HTMLInputElement) positionInput.disabled = !active;\n    if (sizeInput instanceof HTMLInputElement) sizeInput.disabled = !active;\n    // The \"Logo embroidery \u00A39.99 \u2026 included in this price\" line. Liquid only\n    // sets it from the variant the page loaded on, so a page opened on Branded\n    // (a Google Shopping link, or a customer who changed their mind) kept\n    // sending it after personalisation was removed, and Unbranded orders\n    // arrived saying the embroidery was included.\n    const { embroideryInput } = this.refs;\n    if (embroideryInput instanceof HTMLInputElement) embroideryInput.disabled = !active;\n    this.#syncEmbroideryNote();\n\n    for (const logo of this.#logos) {\n      logo.fileInput.disabled = !active;\n      logo.placementInput.disabled = !active;\n    }\n    this.#syncSeatSlides();\n\n    if (active) this.#mountFilesOnForm();\n    this.#syncAcceleratedCheckout(active);\n\n    editor?.toggleAttribute('hidden', count === 0);\n    positionFieldset?.toggleAttribute('hidden', count === 0);\n    this.refs.vanDoubleHint?.toggleAttribute('hidden', !this.#isVanDouble);\n    const seats = this.#brandedSeats;\n    const perSeat = this.#isPerSeat;\n    if (perSeat && this.#seatStep > seats) this.#seatStep = seats;\n    const step = perSeat ? this.#seatStep : 1;\n    const thisSeat = perSeat ? this.#logoForSeat(step) : null;\n    const filled = perSeat ? this.#logos.filter((logo) => logo.seat).length : count;\n    const seatsDone = perSeat && filled >= seats;\n    const nextSeat = perSeat ? Math.min(step + 1, seats) : Math.min(count + 1, seats);\n\n    this.refs.seatSteps?.toggleAttribute('hidden', !perSeat);\n    if (perSeat) {\n      for (let n = 1; n <= 2; n += 1) {\n        const tab = this.refs[`seatTab${n}`];\n        const state = this.refs[`seatState${n}`];\n        if (tab instanceof HTMLElement) {\n          const on = n === step;\n          tab.classList.toggle('is-active', on);\n          tab.setAttribute('aria-selected', String(on));\n          tab.toggleAttribute('hidden', n > seats);\n        }\n        if (state instanceof HTMLElement) state.textContent = this.#logoForSeat(n) ? 'Logo added' : 'Empty';\n      }\n    }\n    if (this.refs.backButton instanceof HTMLElement) {\n      const canGoBack = perSeat && step > 1;\n      this.refs.backButton.toggleAttribute('hidden', !canGoBack);\n      if (canGoBack) this.refs.backButton.textContent = `Back to seat ${step - 1}`;\n    }\n\n    if (dropzoneTitle) {\n      dropzoneTitle.textContent = perSeat\n        ? thisSeat\n          ? `Replace the logo for seat ${step}`\n          : `Add the logo for seat ${step}`\n        : count >= this.#maxLogos && this.#maxLogos > 1\n          ? `Maximum ${this.#maxLogos} logos`\n          : count > 0\n            ? 'Replace logo'\n            : this.#isVanDouble\n              ? 'Add a logo (both seats)'\n              : 'Add a logo file';\n    }\n    if (this.refs.dropzoneHint) {\n      this.refs.dropzoneHint.textContent = this.#isVanDouble\n        ? `One logo, embroidered on both seats \u00B7 PNG, JPG or WEBP \u00B7 max ${this.dataset.maxFileMb || 5}MB`\n        : perSeat\n          ? `Seat ${step} of ${seats} \u00B7 PNG, JPG or WEBP \u00B7 max ${this.dataset.maxFileMb || 5}MB`\n          : `PNG, JPG or WEBP \u00B7 max ${this.dataset.maxFileMb || 5}MB`;\n    }\n    if (statusText) {\n      statusText.textContent = this.#pendingUploads.size\n        ? 'Your logo is still loading. Please wait before confirming personalisation.'\n        : count\n        ? this.#isVanDouble\n          ? '1 logo ready on both seats. Saved with your order when you add to cart.'\n          : perSeat\n            ? seatsDone\n              ? 'Both seats ready. Saved with your order when you add to cart.'\n              : `${filled} of ${seats} seats done. Confirm for one seat, or add seat ${this.#firstEmptySeat()} for both.`\n            : `${count} logo${count === 1 ? '' : 's'} ready. Saved with your order when you add to cart.`\n        : perSeat\n          ? 'No logos yet. Add one for a single seat, or both seats.'\n          : 'No logo yet.';\n    }\n    if (this.refs.finishButton instanceof HTMLElement) {\n      this.refs.finishButton.textContent =\n        perSeat && step < seats ? `Continue to seat ${nextSeat}` : 'Confirm personalisation';\n    }\n\n    this.#writePlacements();\n  }\n\n  /** Shop Pay / Apple Pay / Google Pay cannot attach logo files. */\n  #syncAcceleratedCheckout(active) {\n    const form = this.#refreshForm();\n    const container = form\n      ?.closest('product-form-component')\n      ?.querySelector('[ref=\"acceleratedCheckoutButtonContainer\"]');\n    if (!(container instanceof HTMLElement)) return;\n    if (active) {\n      container.setAttribute('hidden', '');\n      container.dataset.fpPlacerHidden = '';\n      return;\n    }\n    // Removing a personalisation brings the wallets back, unless the variant\n    // itself cannot be bought (the add button is disabled).\n    const addButton = form?.querySelector('button[name=\"add\"]');\n    const unbuyable = addButton instanceof HTMLButtonElement && addButton.disabled;\n    if (!unbuyable && ('fpPlacerHidden' in container.dataset || container.hasAttribute('hidden'))) {\n      delete container.dataset.fpPlacerHidden;\n      container.removeAttribute('hidden');\n    }\n  }\n\n  hasLogoFile() {\n    return this.#logos.some((logo) => logo.file instanceof File && logo.file.size > 0);\n  }\n\n  /** @returns {boolean} */\n  blockIfMissingLogo() {\n    return this.#blockIfMissingLogo();\n  }\n\n  /** @returns {boolean} */\n  #blockIfMissingLogo() {\n    if (this.#applied === false) return false;\n    if (this.#pendingUploads.size) {\n      this.#showError('Your logo is still loading. Please wait before adding to cart.');\n      this.#openModal();\n      return true;\n    }\n    if (!this.#requireUpload) return false;\n    const ready = this.#logos.filter((logo) => logo.file instanceof File && logo.file.size > 0).length;\n    const needed = this.#requiredLogoCount();\n    if (ready >= needed) return false;\n    // Land on the seat that is actually missing, which need not be the next one\n    // in order if the customer filled seat 2 first.\n    const empty = this.#firstEmptySeat();\n    if (this.#isPerSeat) this.#goToSeat(empty);\n    this.#showError(\n      needed > 1\n        ? ready === 0\n          ? `Please add a logo for each seat (${needed} needed) before adding to cart.`\n          : `Add the logo for seat ${empty} before adding to cart.`\n        : 'Please upload a logo before adding to cart.'\n    );\n    this.#openModal();\n    return true;\n  }\n\n  #refreshForm() {\n    this.#form = /** @type {HTMLFormElement | null} */ (document.getElementById(this.#formId));\n    return this.#form;\n  }\n\n  /** Copies the modal note onto the cart line so it shows on the order. */\n  #syncEmbroideryNote() {\n    const field = this.refs.noteField;\n    const input = this.refs.noteInput;\n    if (!(input instanceof HTMLInputElement)) return;\n    const active = this.#applied;\n    const text = field instanceof HTMLTextAreaElement ? field.value.trim().slice(0, 255) : '';\n    if (field instanceof HTMLTextAreaElement && field.value.length > 255) {\n      field.value = field.value.slice(0, 255);\n    }\n    if (!active || !text) {\n      input.value = '';\n      input.disabled = true;\n      return;\n    }\n    input.disabled = false;\n    input.value = text;\n  }\n\n  /**\n   * File inputs linked with the form=\"\" attribute often serialize as\n   * C:\\fakepath\\filename on iOS/Safari instead of uploading the bytes.\n   * Keep the real File objects on the form and in Ajax FormData.\n   */\n  #mountFilesOnForm() {\n    const form = this.#refreshForm();\n    const host = this.refs.inputsHost;\n    if (!form || !this.#applied) return;\n    if (host instanceof HTMLElement && host.parentElement !== form) form.append(host);\n    for (const ref of ['previewInput', 'previewInput2', 'noteInput']) {\n      const field = this.refs[ref];\n      if (field instanceof HTMLInputElement && field.parentElement !== form) form.append(field);\n    }\n    for (const logo of this.#logos) {\n      if (logo.file instanceof File) this.#assignFile(logo.fileInput, logo.file);\n    }\n  }\n\n  /**\n   * @param {FormData} formData\n   */\n  appendFilesToFormData(formData) {\n    if (!(formData instanceof FormData)) return;\n\n    for (const [key, value] of [...formData.entries()]) {\n      const isFileSlot =\n        /^properties\\[Logo \\d+]$/.test(key) ||\n        key === 'properties[Placement preview]' ||\n        key === 'properties[Seat 2 preview]';\n      if (!isFileSlot) continue;\n      if (typeof value === 'string' || (value instanceof File && value.size === 0)) {\n        formData.delete(key);\n      }\n    }\n\n    if (!this.#applied) return;\n\n    this.#syncEmbroideryNote();\n    this.#mountFilesOnForm();\n    const noteInput = /** @type {HTMLInputElement | undefined} */ (this.refs.noteInput);\n    if (noteInput?.name) {\n      formData.delete(noteInput.name);\n      if (!noteInput.disabled && noteInput.value.trim()) {\n        formData.set(noteInput.name, noteInput.value.trim());\n      }\n    }\n    for (const logo of this.#logos) {\n      if (!(logo.file instanceof File) || logo.file.size === 0) continue;\n      const name = logo.fileInput?.name || `properties[Logo ${logo.index}]`;\n      formData.delete(name);\n      formData.set(name, logo.file, logo.file.name || 'logo.png');\n    }\n    for (const ref of ['previewInput', 'previewInput2']) {\n      const preview = /** @type {HTMLInputElement | undefined} */ (this.refs[ref]);\n      const previewFile = preview?.files?.[0];\n      if (preview?.name && !preview.disabled && previewFile instanceof File && previewFile.size > 0) {\n        formData.delete(preview.name);\n        formData.set(preview.name, previewFile, previewFile.name);\n      }\n    }\n  }\n\n  /**\n   * @param {Event} event\n   * @returns {HTMLButtonElement | null}\n   */\n  #findOurAddButton(event) {\n    if (!(event.target instanceof Element)) return null;\n    const form = this.#refreshForm();\n    if (!form) return null;\n    const button = event.target.closest('button[type=\"submit\"], button[name=\"add\"]');\n    if (!(button instanceof HTMLButtonElement)) return null;\n    if (button.form === form || form.contains(button)) return button;\n    return null;\n  }\n\n  /** @param {Event} event */\n  #findWalletButton(event) {\n    if (!(event.target instanceof Element)) return null;\n    const form = this.#refreshForm();\n    const host = form?.closest('product-form-component');\n    if (!host) return null;\n    const wallet = event.target.closest(\n      'shopify-accelerated-checkout, .shopify-payment-button, shopify-accelerated-checkout-cart, .accelerated-checkout-block'\n    );\n    if (!wallet || !host.contains(wallet)) return null;\n    return wallet;\n  }\n\n  /** @param {MouseEvent} event */\n  #onAddButtonClick = (event) => {\n    if (!this.#applied) return;\n\n    if (this.#findWalletButton(event)) {\n      event.preventDefault();\n      event.stopPropagation();\n      event.stopImmediatePropagation();\n      if (this.#blockIfMissingLogo()) return;\n      const addButton = this.#refreshForm()\n        ?.closest('product-form-component')\n        ?.querySelector('[ref=\"addToCartButton\"]');\n      if (addButton instanceof HTMLButtonElement) addButton.click();\n      else this.#showError('Please use Add to cart so your logo is saved with the order.');\n      return;\n    }\n\n    const button = this.#findOurAddButton(event);\n    if (!button) return;\n\n    if (this.#blockIfMissingLogo()) {\n      event.preventDefault();\n      event.stopPropagation();\n      event.stopImmediatePropagation();\n      return;\n    }\n\n    if (this.#allowNextClick) {\n      this.#allowNextClick = false;\n      return;\n    }\n\n    // Drawn in the background since the last change: let the add go straight on.\n    if (this.#attachedVersion === this.#previewVersion) {\n      this.#mountFilesOnForm();\n      return;\n    }\n\n    if (this.#addInFlight) {\n      event.preventDefault();\n      event.stopPropagation();\n      event.stopImmediatePropagation();\n      return;\n    }\n\n    event.preventDefault();\n    event.stopPropagation();\n    event.stopImmediatePropagation();\n    this.#addInFlight = true;\n\n    this.#ensurePreview()\n      .catch((error) => console.warn('[branding-logo-placer] Screenshot skipped:', error))\n      .then(() => {\n        this.#mountFilesOnForm();\n        this.#allowNextClick = true;\n        button.click();\n      })\n      .finally(() => {\n        setTimeout(() => {\n          this.#addInFlight = false;\n          this.#allowNextClick = false;\n        }, 1500);\n      });\n  };\n\n  /** @param {Event} event */\n  #onFileChange = (event) => {\n    const file = /** @type {HTMLInputElement} */ (event.target).files?.[0];\n    if (this.refs.fileInput instanceof HTMLInputElement) this.refs.fileInput.value = '';\n    if (file) this.#loadFile(file);\n  };\n\n  /** @param {DragEvent} event */\n  #onDragEnter = (event) => {\n    event.preventDefault();\n    this.classList.add('is-dragging-file');\n  };\n\n  /** @param {DragEvent} event */\n  #onDragOver = (event) => {\n    event.preventDefault();\n  };\n\n  /** @param {DragEvent} event */\n  #onDragLeave = (event) => {\n    if (event.target === this || !this.contains(/** @type {Node} */ (event.relatedTarget))) {\n      this.classList.remove('is-dragging-file');\n    }\n  };\n\n  /** @param {DragEvent} event */\n  #onDrop = (event) => {\n    event.preventDefault();\n    this.classList.remove('is-dragging-file');\n    if (!this.#applied && !this.#dialog?.open) return;\n    const file = event.dataTransfer?.files?.[0];\n    if (file) this.#loadFile(file);\n  };\n\n  /** @param {File} file */\n  async #loadFile(file) {\n    // The request belongs to the seat shown when the file was chosen, even\n    // if the customer visits another seat while image processing is running.\n    const perSeat = this.#isPerSeat;\n    const seat = this.#seatStep;\n    const position = this.#position;\n    if (perSeat) {\n      const existing = this.#logoForSeat(seat);\n      if (existing) this.#removeLogo(existing.id);\n    } else if (this.#maxLogos === 1) {\n      if (this.#logos[0]) this.#removeLogo(this.#logos[0].id);\n    } else if (this.#logos.length + this.#pendingUploads.size >= this.#maxLogos) {\n      this.#showError(`You can add up to ${this.#maxLogos} logos.`);\n      return;\n    }\n\n    const version = ++this.#uploadSequence;\n    const key = perSeat ? `seat:${seat}` : this.#maxLogos === 1 ? 'single' : `upload:${version}`;\n    this.#pendingUploads.set(key, version);\n    const isCurrent = () =>\n      this.#pendingUploads.get(key) === version &&\n      perSeat === this.#isPerSeat &&\n      (!perSeat || seat <= this.#brandedSeats);\n    this.#clearError();\n    this.#syncFormState();\n    let objectUrl = '';\n    let mounted = false;\n    try {\n      const logoFile = await this.#normalizeLogoFile(file);\n      if (!isCurrent()) return;\n      objectUrl = URL.createObjectURL(logoFile);\n      const workingSrc = await this.#fileToPngDataUrl(logoFile).catch(() => objectUrl);\n      const hasBackground = await this.#detectBackgroundLayer(workingSrc).catch(() => false);\n      const warpedSrc = await this.#warpPreview(workingSrc).catch(() => null);\n      if (!isCurrent()) return;\n      this.#clearError();\n\n      const slot = this.#slotCoords(position);\n      const entry = this.#createLogoEntry({\n        file: logoFile,\n        objectUrl,\n        workingSrc,\n        warpedSrc,\n        hasBackground,\n        x: slot.x,\n        y: slot.y,\n        scale: this.#fixedScale,\n        rotation: 0,\n      });\n\n      entry.position = position;\n      this.#constrainLogo(entry);\n      if (perSeat) entry.seat = seat;\n      this.#logos.push(entry);\n      mounted = true;\n      this.#pendingUploads.delete(key);\n      this.#reindexLogos();\n      if (!perSeat || this.#seatStep === seat) this.#setActive(entry.id);\n      this.#mountLogo(entry);\n      this.#syncSeatSlides();\n      this.#syncFormState();\n      this.#resetCropBox();\n      this.#drawEditor();\n      this.#openModal();\n    } catch {\n      if (isCurrent()) this.#showError('Please use a PNG, JPG, WEBP or HEIC file.');\n    } finally {\n      if (objectUrl && !mounted) URL.revokeObjectURL(objectUrl);\n      // An obsolete completion must not clear a newer request for this seat.\n      if (this.#pendingUploads.get(key) === version) {\n        this.#pendingUploads.delete(key);\n        this.#syncFormState();\n      }\n    }\n  }\n\n  /**\n   * @param {object} data\n   * @param {File} data.file\n   * @param {string} data.objectUrl\n   * @param {string} data.workingSrc\n   * @param {string | null} data.warpedSrc\n   * @param {boolean} data.hasBackground\n   * @param {number} data.x\n   * @param {number} data.y\n   * @param {number} data.scale\n   * @param {number} data.rotation\n   * @returns {LogoEntry}\n   */\n  #createLogoEntry(data) {\n    const id = this.#nextId++;\n    const template = /** @type {HTMLTemplateElement | undefined} */ (this.refs.layerTemplate);\n    const fragment = template?.content?.cloneNode(true);\n    const layer = /** @type {HTMLElement | null} */ (\n      fragment instanceof DocumentFragment ? fragment.querySelector('.branding-preview__logo') : null\n    );\n    if (!layer) throw new Error('Logo layer template missing');\n\n    const image = /** @type {HTMLImageElement} */ (layer.querySelector('img'));\n    image.alt = `Logo preview ${id}`;\n    image.src = data.warpedSrc || data.workingSrc;\n\n    /** @type {HTMLElement | null} */\n    let twinLayer = null;\n    /** @type {HTMLImageElement | null} */\n    let twinImage = null;\n    if (this.#isVanDouble) {\n      twinLayer = this.#cloneTwinLayer(layer);\n      twinImage = /** @type {HTMLImageElement | null} */ (twinLayer.querySelector('img'));\n    }\n\n    const fileInput = document.createElement('input');\n    fileInput.type = 'file';\n    fileInput.accept = 'image/png,image/jpeg,image/webp,image/heic,image/heif,.png,.jpg,.jpeg,.webp,.heic,.heif';\n    fileInput.name = 'properties[Logo 1]';\n    fileInput.setAttribute('form', this.#formId);\n    fileInput.className = 'visually-hidden';\n    this.#assignFile(fileInput, data.file);\n\n    const placementInput = document.createElement('input');\n    placementInput.type = 'hidden';\n    placementInput.name = 'properties[Logo 1 placement]';\n    placementInput.setAttribute('form', this.#formId);\n    this.refs.inputsHost?.append(fileInput, placementInput);\n    this.#mountFilesOnForm();\n\n    /** @type {LogoEntry} */\n    const entry = {\n      id,\n      index: 1,\n      file: data.file,\n      originalFile: data.file,\n      objectUrl: data.objectUrl,\n      workingSrc: data.workingSrc,\n      warpedSrc: data.warpedSrc,\n      hasBackground: data.hasBackground,\n      x: data.x,\n      y: data.y,\n      scale: data.scale,\n      rotation: data.rotation,\n      layer,\n      image,\n      twinLayer,\n      twinImage,\n      fileInput,\n      placementInput,\n    };\n\n    this.#applyLogoTransform(entry);\n    return entry;\n  }\n\n  /** @param {LogoEntry} logo */\n  #mountLogo(logo) {\n    this.#target = this.refs.previewHost || null;\n    if (!this.#target) return;\n    if (logo.layer.parentElement !== this.#target) this.#target.appendChild(logo.layer);\n    if (logo.twinLayer && logo.twinLayer.parentElement !== this.#target) this.#target.appendChild(logo.twinLayer);\n    this.#applyLogoTransform(logo);\n  }\n\n  #reindexLogos() {\n    // On a two-seat bundle the property number IS the seat number, so seat 2\n    // stays \"Logo 2\" even when seat 1 is still empty.\n    if (this.#isPerSeat) {\n      this.#logos.sort((a, b) => (a.seat || 0) - (b.seat || 0));\n      for (const logo of this.#logos) {\n        logo.index = logo.seat || 1;\n        logo.fileInput.name = `properties[Logo ${logo.index}]`;\n        logo.placementInput.name = `properties[Logo ${logo.index} placement]`;\n      }\n      return;\n    }\n    this.#logos.forEach((logo, i) => {\n      logo.index = i + 1;\n      logo.fileInput.name = `properties[Logo ${logo.index}]`;\n      logo.placementInput.name = `properties[Logo ${logo.index} placement]`;\n    });\n  }\n\n  /** @param {number} id */\n  #setActive(id) {\n    this.#activeId = id;\n    for (const logo of this.#logos) {\n      logo.layer.classList.toggle('is-selected', logo.id === id);\n    }\n    this.#drawEditor();\n  }\n\n  /** @param {number} id */\n  #removeLogo(id) {\n    const index = this.#logos.findIndex((logo) => logo.id === id);\n    if (index < 0) return;\n    const [logo] = this.#logos.splice(index, 1);\n    logo.layer.remove();\n    logo.twinLayer?.remove();\n    logo.fileInput.remove();\n    logo.placementInput.remove();\n    URL.revokeObjectURL(logo.objectUrl);\n    const stay = this.#isPerSeat ? this.#logoForSeat(this.#seatStep) : null;\n    this.#activeId = (stay ?? this.#logos[this.#logos.length - 1])?.id ?? null;\n    if (this.#activeId != null) this.#setActive(this.#activeId);\n    this.#reindexLogos();\n    this.#syncSeatSlides();\n    this.#syncFormState();\n    this.#drawEditor();\n  }\n\n  #cancelPendingUploads() {\n    this.#pendingUploads.clear();\n  }\n\n  #clearAllLogos() {\n    this.#cancelPendingUploads();\n    for (const logo of [...this.#logos]) this.#removeLogo(logo.id);\n  }\n\n  /** @param {HTMLElement} layer */\n  #cloneTwinLayer(layer) {\n    const twinLayer = /** @type {HTMLElement} */ (layer.cloneNode(true));\n    twinLayer.removeAttribute('hidden');\n    twinLayer.classList.remove('is-selected');\n    twinLayer.classList.add('branding-preview__logo--twin');\n    twinLayer.setAttribute('aria-hidden', 'true');\n    const twinImage = twinLayer.querySelector('img');\n    if (twinImage instanceof HTMLImageElement) twinImage.alt = '';\n    return twinLayer;\n  }\n\n  #ensureTwinLayers() {\n    // Dropping from \"Both seats branded\" to one seat (or to a van double) has\n    // to shed the logos the new variant no longer carries.\n    while (this.#logos.length > this.#maxLogos) {\n      this.#removeLogo(this.#logos[this.#logos.length - 1].id);\n    }\n\n    for (const logo of this.#logos) {\n      if (this.#isVanDouble) {\n        if (!logo.twinLayer) {\n          logo.twinLayer = this.#cloneTwinLayer(logo.layer);\n          logo.twinImage = /** @type {HTMLImageElement | null} */ (logo.twinLayer.querySelector('img'));\n        }\n        this.#mountLogo(logo);\n      } else if (logo.twinLayer) {\n        logo.twinLayer.remove();\n        logo.twinLayer = null;\n        logo.twinImage = null;\n        this.#applyLogoTransform(logo);\n      }\n    }\n  }\n\n  /** @param {LogoEntry} logo */\n  #previewPoints(logo) {\n    if (!this.#isVanDouble) return [{ x: logo.x, y: logo.y }];\n    const headrest = this.#positionFor(logo) === 'headrest' && this.#detectSeatKind() !== 'low';\n    const spread = headrest ? this.#vanDoubleSpread : this.#vanDoubleBackrestSpread;\n    const clamp = (value) => Math.max(8, Math.min(92, value));\n    return [\n      { x: clamp(logo.x - spread), y: logo.y },\n      { x: clamp(logo.x + spread), y: logo.y },\n    ];\n  }\n\n  /** @param {LogoEntry} logo */\n  #constrainLogo(logo) {\n    const place = this.#positionFor(logo);\n    const slot = this.#slotCoords(place);\n    const headrest = place === 'headrest' && this.#detectSeatKind() !== 'low';\n    const backgroundScale = headrest ? 0.76 : 0.85;\n    logo.scale = this.#scaleFor(logo) * (logo.hasBackground ? backgroundScale : 1);\n    logo.x = slot.x;\n    logo.y = logo.hasBackground && headrest ? slot.y - 0.5 : slot.y;\n  }\n\n  /** @param {LogoEntry} logo */\n  #applyLogoTransform(logo) {\n    this.#constrainLogo(logo);\n    const points = this.#previewPoints(logo);\n    const src = logo.warpedSrc || logo.workingSrc;\n    const applyLayer = (layer, image, point) => {\n      if (!(layer instanceof HTMLElement) || !point) return;\n      layer.style.setProperty('--logo-x', `${point.x}%`);\n      layer.style.setProperty('--logo-y', `${point.y}%`);\n      layer.style.setProperty('--logo-scale', String(logo.scale));\n      layer.style.setProperty('--logo-max-height', String(this.#logoMaxHeight(logo)));\n      layer.toggleAttribute('data-background-layer', logo.hasBackground);\n      layer.style.setProperty('--logo-rotation', '0deg');\n      if (image && image.src !== src) image.src = src;\n    };\n    applyLayer(logo.layer, logo.image, points[0]);\n    applyLayer(logo.twinLayer, logo.twinImage, points[1] || points[0]);\n    logo.rotation = 0;\n    this.#previewChanged();\n  }\n\n  /**\n   * Changes on every edit that shows in the placement preview, so other code\n   * (the second-seat card) can tell whether a preview it drew is still current.\n   */\n  get previewVersion() {\n    return this.#previewVersion;\n  }\n\n  /**\n   * Something in the preview changed. Once personalisation is confirmed, redraw\n   * the preview in the background after edits settle, so Add to cart doesn't\n   * have to stop and draw it.\n   */\n  #previewChanged() {\n    this.#previewVersion += 1;\n    clearTimeout(this.#prerenderTimer);\n    if (!this.#applied || this.#logos.length === 0) return;\n    this.#prerenderTimer = setTimeout(() => {\n      this.#ensurePreview().catch((error) => console.warn('[branding-logo-placer] Preview skipped:', error));\n    }, 500);\n  }\n\n  /** Draws the preview files for the current state, once per change. */\n  #ensurePreview() {\n    const version = this.#previewVersion;\n    if (this.#attachedVersion === version) return Promise.resolve();\n    if (this.#attachPromise && this.#attachPromiseVersion === version) return this.#attachPromise;\n    this.#attachPromiseVersion = version;\n    const pending = this.#attachScreenshot()\n      .then(() => {\n        // Only current if nothing changed while it was drawing.\n        if (this.#previewVersion === version) this.#attachedVersion = version;\n      })\n      .finally(() => {\n        if (this.#attachPromise === pending) this.#attachPromise = null;\n      });\n    this.#attachPromise = pending;\n    return pending;\n  }\n\n  #seatKindLabel() {\n    const group = this.dataset.previewGroup;\n    if (group === 'van-double') return 'Double passenger';\n    if (group === 'van-driver' || group === 'pickup-driver') return 'Driver';\n    if (group === 'pickup-passenger') return 'Passenger';\n    return this.#detectSeatKind() === 'low' ? 'Low back' : 'High back';\n  }\n\n  #writePlacements() {\n    this.#previewChanged();\n    const kind = this.#seatKindLabel();\n    const mm = this.dataset.logoMm || '145';\n    const label = (place) => {\n      const slot = place === 'headrest' && this.#detectSeatKind() !== 'low' ? 'Headrest' : 'Backrest';\n      return this.#isVanDouble ? `Both seats \u00B7 ${slot}` : slot;\n    };\n    if (this.refs.positionInput instanceof HTMLInputElement) {\n      // The summary line lists every seat's placement when they differ.\n      const each = this.#isPerSeat\n        ? this.#logos.map((logo) => `Seat ${logo.index}: ${label(this.#positionFor(logo))}`).join(' \u00B7 ')\n        : '';\n      this.refs.positionInput.value = each || label(this.#position);\n    }\n    if (this.refs.sizeInput instanceof HTMLInputElement) this.refs.sizeInput.value = `${mm} mm`;\n    for (const logo of this.#logos) {\n      // On a two-seat bundle each logo names its seat, so the order shows the\n      // packer which cover carries which file.\n      const seat = this.#isPerSeat ? `Seat ${logo.index} \u00B7 ` : '';\n      logo.placementInput.value = `${seat}${kind} \u00B7 ${label(this.#positionFor(logo))} \u00B7 ${mm} mm`;\n    }\n    if (this.refs.countInput instanceof HTMLInputElement && this.#applied) {\n      this.refs.countInput.value = String(this.#logos.length);\n    }\n  }\n\n  /** @param {PointerEvent} event */\n  #onCropPointerDown = (event) => {\n    if (!(event.target instanceof Element)) return;\n    event.preventDefault();\n    event.stopPropagation();\n    const handle = event.target.closest('[data-crop]');\n    this.#pointerId = event.pointerId;\n    this.#dragMode = handle ? 'crop-resize' : 'crop-move';\n    this.#cropHandle = handle?.getAttribute('data-crop') || null;\n    const box = this.refs.cropBox;\n    this.#cropStart = {\n      x: event.clientX,\n      y: event.clientY,\n      left: parseFloat(box?.style.left || '8'),\n      top: parseFloat(box?.style.top || '8'),\n      width: parseFloat(box?.style.width || '84'),\n      height: parseFloat(box?.style.height || '84'),\n    };\n    window.addEventListener('pointermove', this.#onPointerMove);\n    window.addEventListener('pointerup', this.#onPointerUp);\n    window.addEventListener('pointercancel', this.#onPointerUp);\n  };\n\n  /** @param {PointerEvent} event */\n  #onPointerMove = (event) => {\n    if (event.pointerId !== this.#pointerId || !this.#dragMode) return;\n    event.preventDefault();\n\n    const stage = this.refs.editorStage?.getBoundingClientRect();\n    const box = this.refs.cropBox;\n    if (!stage || !box || !stage.width) return;\n    const dx = ((event.clientX - this.#cropStart.x) / stage.width) * 100;\n    const dy = ((event.clientY - this.#cropStart.y) / stage.height) * 100;\n    let { left, top, width, height } = this.#cropStart;\n\n    if (this.#dragMode === 'crop-move') {\n      left += dx;\n      top += dy;\n    } else if (this.#cropHandle === 'se') {\n      width += dx;\n      height += dy;\n    } else if (this.#cropHandle === 'ne') {\n      width += dx;\n      top += dy;\n      height -= dy;\n    } else if (this.#cropHandle === 'sw') {\n      left += dx;\n      width -= dx;\n      height += dy;\n    } else if (this.#cropHandle === 'nw') {\n      left += dx;\n      top += dy;\n      width -= dx;\n      height -= dy;\n    }\n\n    width = this.#clamp(width, 12, 100);\n    height = this.#clamp(height, 12, 100);\n    left = this.#clamp(left, 0, 100 - width);\n    top = this.#clamp(top, 0, 100 - height);\n    this.#setCropBox(left, top, width, height);\n  };\n\n  /** @param {PointerEvent} event */\n  #onPointerUp = (event) => {\n    if (event.pointerId !== this.#pointerId) return;\n    this.#dragMode = null;\n    this.#cropHandle = null;\n    this.#pointerId = -1;\n    window.removeEventListener('pointermove', this.#onPointerMove);\n    window.removeEventListener('pointerup', this.#onPointerUp);\n    window.removeEventListener('pointercancel', this.#onPointerUp);\n    this.#writePlacements();\n  };\n\n  /**\n   * Fit the crop box to the artwork rather than to the stage.\n   *\n   * It used to sit at a fixed 8/8/84/84 of the square stage. A wide logo is\n   * letterboxed inside that square, so the dashed box enclosed the artwork\n   * *and* a band of empty space above and below \u2014 it looked like it selected\n   * everything, while the sides quietly cut 8% off each edge. Pressing the\n   * tick to confirm therefore shrank the logo a little every time, with no\n   * way back, which reads as \"zoom in works and zoom out doesn't\".\n   */\n  async #resetCropBox() {\n    const logo = this.#active;\n    if (!logo) {\n      this.#setCropBox(8, 8, 84, 84);\n      return;\n    }\n    try {\n      const img = await this.#loadImageElement(logo.workingSrc);\n      const fit = Math.min(1 / img.naturalWidth, 1 / img.naturalHeight);\n      const width = img.naturalWidth * fit * 100;\n      const height = img.naturalHeight * fit * 100;\n      this.#setCropBox((100 - width) / 2, (100 - height) / 2, width, height);\n    } catch {\n      this.#setCropBox(8, 8, 84, 84);\n    }\n  }\n\n  /** Restore the picture as it was uploaded, undoing crops and background removal. */\n  async #resetLogo() {\n    const logo = this.#active;\n    if (!logo) return;\n    logo.file = logo.originalFile;\n    this.#assignFile(logo.fileInput, logo.originalFile);\n    logo.workingSrc = logo.objectUrl;\n    logo.hasBackground = await this.#detectBackgroundLayer(logo.workingSrc).catch(() => false);\n    logo.warpedSrc = await this.#warpPreview(logo.workingSrc).catch(() => null);\n    this.#applyLogoTransform(logo);\n    await this.#resetCropBox();\n    this.#drawEditor();\n    this.#writePlacements();\n  }\n\n  /**\n   * @param {number} left\n   * @param {number} top\n   * @param {number} width\n   * @param {number} height\n   */\n  #setCropBox(left, top, width, height) {\n    const box = this.refs.cropBox;\n    if (!box) return;\n    box.style.left = `${left}%`;\n    box.style.top = `${top}%`;\n    box.style.width = `${width}%`;\n    box.style.height = `${height}%`;\n  }\n\n  async #drawEditor() {\n    const canvas = /** @type {HTMLCanvasElement | undefined} */ (this.refs.editorCanvas);\n    const logo = this.#active;\n    if (!canvas) return;\n    const ctx = canvas.getContext('2d');\n    if (!ctx) return;\n\n    const size = 520;\n    canvas.width = size;\n    canvas.height = size;\n    ctx.clearRect(0, 0, size, size);\n    if (!logo) return;\n\n    try {\n      const img = await this.#loadImageElement(logo.workingSrc);\n      const scale = Math.min(size / img.naturalWidth, size / img.naturalHeight);\n      const w = img.naturalWidth * scale;\n      const h = img.naturalHeight * scale;\n      ctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h);\n    } catch {\n      // ignore\n    }\n  }\n\n  async #applyCrop() {\n    const logo = this.#active;\n    const box = this.refs.cropBox;\n    if (!logo || !box) return;\n\n    const left = parseFloat(box.style.left || '8') / 100;\n    const top = parseFloat(box.style.top || '8') / 100;\n    const width = parseFloat(box.style.width || '84') / 100;\n    const height = parseFloat(box.style.height || '84') / 100;\n\n    const img = await this.#loadImageElement(logo.workingSrc);\n    const size = 520;\n\n    // A box still hugging the whole picture means \"keep it as it is\". Without\n    // this the tick re-encodes and trims a pixel or two on every press.\n    const fit = Math.min(1 / img.naturalWidth, 1 / img.naturalHeight);\n    const full = { w: img.naturalWidth * fit, h: img.naturalHeight * fit };\n    const untouched =\n      Math.abs(width - full.w) < 0.01 &&\n      Math.abs(height - full.h) < 0.01 &&\n      Math.abs(left - (1 - full.w) / 2) < 0.01 &&\n      Math.abs(top - (1 - full.h) / 2) < 0.01;\n    if (untouched) return;\n\n    const scale = Math.min(size / img.naturalWidth, size / img.naturalHeight);\n    const drawW = img.naturalWidth * scale;\n    const drawH = img.naturalHeight * scale;\n    const offsetX = (size - drawW) / 2;\n    const offsetY = (size - drawH) / 2;\n\n    const sx = this.#clamp(((left * size - offsetX) / drawW) * img.naturalWidth, 0, img.naturalWidth - 1);\n    const sy = this.#clamp(((top * size - offsetY) / drawH) * img.naturalHeight, 0, img.naturalHeight - 1);\n    const sw = this.#clamp((width * size) / drawW * img.naturalWidth, 1, img.naturalWidth - sx);\n    const sh = this.#clamp((height * size) / drawH * img.naturalHeight, 1, img.naturalHeight - sy);\n\n    const out = document.createElement('canvas');\n    out.width = Math.max(1, Math.round(sw));\n    out.height = Math.max(1, Math.round(sh));\n    const ctx = out.getContext('2d');\n    if (!ctx) return;\n    ctx.drawImage(img, sx, sy, sw, sh, 0, 0, out.width, out.height);\n    await this.#commitCanvas(logo, out, `${this.#baseName(logo.file.name)}-cropped.png`);\n    await this.#resetCropBox();\n  }\n\n  async #removeBackground() {\n    const logo = this.#active;\n    if (!logo) return;\n    const img = await this.#loadImageElement(logo.workingSrc);\n    const canvas = document.createElement('canvas');\n    canvas.width = img.naturalWidth;\n    canvas.height = img.naturalHeight;\n    const ctx = canvas.getContext('2d', { willReadFrequently: true });\n    if (!ctx) return;\n    ctx.drawImage(img, 0, 0);\n    const image = ctx.getImageData(0, 0, canvas.width, canvas.height);\n    // The keying itself lives in assets/fp-logo-knockout.js, which explains\n    // each step; it leaves a logo that is already cut out untouched.\n    knockoutBackground(image);\n    ctx.putImageData(image, 0, 0);\n    await this.#commitCanvas(logo, canvas, `${this.#baseName(logo.file.name)}-nobg.png`);\n  }\n\n  /**\n   * @param {LogoEntry} logo\n   * @param {HTMLCanvasElement} canvas\n   * @param {string} filename\n   */\n  async #commitCanvas(logo, canvas, filename) {\n    const blob = await new Promise((resolve) => canvas.toBlob((b) => resolve(b), 'image/png'));\n    if (!blob) return;\n    const file = new File([blob], filename, { type: 'image/png' });\n    logo.file = file;\n    this.#assignFile(logo.fileInput, file);\n    logo.workingSrc = canvas.toDataURL('image/png');\n    logo.hasBackground = await this.#detectBackgroundLayer(logo.workingSrc).catch(() => false);\n    logo.warpedSrc = await this.#warpPreview(logo.workingSrc).catch(() => null);\n    this.#applyLogoTransform(logo);\n    this.#drawEditor();\n    this.#writePlacements();\n  }\n\n  /**\n   * @param {HTMLInputElement} input\n   * @param {File} file\n   */\n  #assignFile(input, file) {\n    const transfer = new DataTransfer();\n    // A null file clears the slot, which is how an emptied seat drops its\n    // stale preview instead of shipping last time's screenshot.\n    if (file instanceof File) transfer.items.add(file);\n    input.files = transfer.files;\n  }\n\n  /** @param {string} name */\n  #baseName(name) {\n    return name.replace(/\\.[^.]+$/, '') || 'logo';\n  }\n\n  async #attachScreenshot() {\n    if (this.#logos.length === 0) return;\n\n    if (this.#isPerSeat) {\n      // One composite per cover, so the order shows each seat as the customer\n      // arranged it rather than both logos stacked on a single seat.\n      const slots = [\n        [1, this.refs.previewInput],\n        [2, this.refs.previewInput2],\n      ];\n      for (const [seat, input] of slots) {\n        if (!(input instanceof HTMLInputElement)) continue;\n        const logo = this.#logoForSeat(seat);\n        if (!logo) {\n          this.#assignFile(input, null);\n          continue;\n        }\n        const blob = await this.#capturePreviewBlob([logo]);\n        if (blob) this.#assignFile(input, new File([blob], `seat-${seat}-preview.jpg`, { type: PREVIEW_TYPE }));\n      }\n      return;\n    }\n\n    const input = /** @type {HTMLInputElement | undefined} */ (this.refs.previewInput);\n    if (!input) return;\n    const blob = await this.#capturePreviewBlob();\n    if (!blob) return;\n    this.#assignFile(input, new File([blob], 'placement-preview.jpg', { type: PREVIEW_TYPE }));\n  }\n\n  /**\n   * @param {any[]} [only] Logos to composite; defaults to all of them.\n   * @returns {Promise<Blob | null>}\n   */\n  async #capturePreviewBlob(only) {\n    const host = this.#target || this.refs.previewHost;\n    const seat = /** @type {HTMLImageElement | undefined} */ (this.refs.seatImage);\n    if (!host) return null;\n\n    const width = PREVIEW_WIDTH;\n    const height =\n      seat && seat.naturalWidth\n        ? Math.round(width * (seat.naturalHeight / seat.naturalWidth))\n        : Math.round(width * ((host.clientHeight || 900) / (host.clientWidth || 900)));\n    const canvas = document.createElement('canvas');\n    canvas.width = width;\n    canvas.height = height;\n    const ctx = canvas.getContext('2d');\n    if (!ctx) return null;\n    ctx.fillStyle = '#ececec';\n    ctx.fillRect(0, 0, width, height);\n\n    if (seat && !seat.hidden && seat.naturalWidth) {\n      this.#drawContain(ctx, seat, width, height);\n    }\n\n    for (const logo of only || this.#logos) {\n      try {\n        const img = await this.#loadImageElement(logo.warpedSrc || logo.workingSrc);\n        const maxLogoW = (logo.scale / 100) * width;\n        const maxLogoH = (this.#logoMaxHeight(logo) / 100) * height;\n        const logoScale = Math.min(\n          maxLogoW / (img.naturalWidth || 1),\n          maxLogoH / (img.naturalHeight || 1)\n        );\n        const logoW = img.naturalWidth * logoScale;\n        const logoH = img.naturalHeight * logoScale;\n        for (const point of this.#previewPoints(logo)) {\n          ctx.save();\n          ctx.translate((point.x / 100) * width, (point.y / 100) * height);\n          ctx.drawImage(img, -logoW / 2, -logoH / 2, logoW, logoH);\n          ctx.restore();\n        }\n      } catch (error) {\n        console.warn('[branding-logo-placer] Logo composite skipped:', error);\n      }\n    }\n\n    return new Promise((resolve) => canvas.toBlob((blob) => resolve(blob), PREVIEW_TYPE, PREVIEW_QUALITY));\n  }\n\n  /**\n   * Runs a logo file through the same steps as an upload in the modal (format\n   * and size normalising, background detection, the slight seat curve), for a\n   * logo that goes on another seat's cover. Throws on a file it can't read.\n   *\n   * @param {File} file\n   * @returns {Promise<PreparedLogo>}\n   */\n  async prepareLogo(file) {\n    const logoFile = await this.#normalizeLogoFile(file);\n    const workingSrc = await this.#fileToPngDataUrl(logoFile);\n    const hasBackground = await this.#detectBackgroundLayer(workingSrc).catch(() => false);\n    const warpedSrc = await this.#warpPreview(workingSrc).catch(() => null);\n    return { file: logoFile, workingSrc, warpedSrc, hasBackground };\n  }\n\n  /**\n   * The placement preview for another seat of the same vehicle, drawn on that\n   * seat's photo with this seat's logo, position and size. Used by the \"add the\n   * other seat too\" toggle, so each cover in the order shows its own seat.\n   *\n   * @param {string} group A preview group: van-double, van-driver, pickup-driver, pickup-passenger or lorry.\n   * @param {{ logo: PreparedLogo, position: 'headrest' | 'backrest' }} [instead] Draw this logo at this\n   *   position rather than the customer's logos on this cover (a different logo for the other seat).\n   * @returns {Promise<File | null>}\n   */\n  async capturePreviewFor(group, instead) {\n    if (!instead && (!this.#applied || this.#logos.length === 0)) return null;\n    const sources = instead\n      ? [/** @type {any} */ ({ ...instead.logo, position: instead.position, scale: this.#fixedScale, x: 50, y: 50 })]\n      : this.#logos;\n\n    let seatSrc = '';\n    try {\n      seatSrc = JSON.parse(this.dataset.seatImages || '{}')[group] || '';\n    } catch {\n      seatSrc = '';\n    }\n    const seat = seatSrc ? await this.#loadImageElement(seatSrc).catch(() => null) : null;\n\n    // Position and size depend on the preview group (a van double seat puts one\n    // logo on each headrest), so work them out as that group, then switch back\n    // before anything awaits.\n    const saved = this.dataset.previewGroup;\n    /** @type {{ logo: LogoEntry, points: { x: number, y: number }[], scale: number, maxHeight: number }[]} */\n    const placed = [];\n    try {\n      this.dataset.previewGroup = group;\n      this.#sizingPhotoSrc = seatSrc;\n      for (const logo of sources) {\n        const copy = { ...logo };\n        this.#constrainLogo(copy);\n        placed.push({\n          logo,\n          points: this.#previewPoints(copy),\n          scale: copy.scale,\n          maxHeight: this.#logoMaxHeight(copy),\n        });\n      }\n    } finally {\n      this.#sizingPhotoSrc = '';\n      if (saved == null) delete this.dataset.previewGroup;\n      else this.dataset.previewGroup = saved;\n    }\n\n    const width = PREVIEW_WIDTH;\n    const height = seat?.naturalWidth ? Math.round(width * (seat.naturalHeight / seat.naturalWidth)) : width;\n    const canvas = document.createElement('canvas');\n    canvas.width = width;\n    canvas.height = height;\n    const ctx = canvas.getContext('2d');\n    if (!ctx) return null;\n    ctx.fillStyle = '#ececec';\n    ctx.fillRect(0, 0, width, height);\n    if (seat) this.#drawContain(ctx, seat, width, height);\n\n    for (const { logo, points, scale, maxHeight } of placed) {\n      try {\n        const img = await this.#loadImageElement(logo.warpedSrc || logo.workingSrc);\n        const logoScale = Math.min(\n          ((scale / 100) * width) / (img.naturalWidth || 1),\n          ((maxHeight / 100) * height) / (img.naturalHeight || 1)\n        );\n        const logoW = img.naturalWidth * logoScale;\n        const logoH = img.naturalHeight * logoScale;\n        for (const point of points) {\n          ctx.drawImage(img, (point.x / 100) * width - logoW / 2, (point.y / 100) * height - logoH / 2, logoW, logoH);\n        }\n      } catch (error) {\n        console.warn('[branding-logo-placer] Logo composite skipped:', error);\n      }\n    }\n\n    const blob = await new Promise((resolve) => canvas.toBlob((b) => resolve(b), PREVIEW_TYPE, PREVIEW_QUALITY));\n    return blob ? new File([blob], `${group}-placement-preview.jpg`, { type: PREVIEW_TYPE }) : null;\n  }\n\n  /**\n   * @param {CanvasRenderingContext2D} ctx\n   * @param {CanvasImageSource & { naturalWidth?: number, naturalHeight?: number, width?: number, height?: number }} img\n   * @param {number} width\n   * @param {number} height\n   */\n  #drawContain(ctx, img, width, height) {\n    const iw = Number(img.naturalWidth || img.width) || width;\n    const ih = Number(img.naturalHeight || img.height) || height;\n    const scale = Math.min(width / iw, height / ih);\n    const dw = iw * scale;\n    const dh = ih * scale;\n    ctx.drawImage(img, (width - dw) / 2, (height - dh) / 2, dw, dh);\n  }\n\n  /**\n   * @param {string} src\n   * @returns {Promise<HTMLImageElement>}\n   */\n  #loadImageElement(src) {\n    return new Promise((resolve, reject) => {\n      const img = new Image();\n      img.onload = () => resolve(img);\n      img.onerror = () => reject(new Error('Image failed to load'));\n      img.src = src;\n    });\n  }\n\n  /**\n   * Detect a rectangular background by checking whether the corners and image\n   * border are opaque. Transparent logo artwork keeps the larger normal area.\n   * @param {string} src\n   * @returns {Promise<boolean>}\n   */\n  async #detectBackgroundLayer(src) {\n    const image = await this.#loadImageElement(src);\n    const size = 48;\n    const canvas = document.createElement('canvas');\n    canvas.width = size;\n    canvas.height = size;\n    const ctx = canvas.getContext('2d', { willReadFrequently: true });\n    if (!ctx) return false;\n    ctx.clearRect(0, 0, size, size);\n    ctx.drawImage(image, 0, 0, size, size);\n    const { data } = ctx.getImageData(0, 0, size, size);\n    const alphaAt = (x, y) => data[(y * size + x) * 4 + 3];\n\n    let borderPixels = 0;\n    let opaqueBorderPixels = 0;\n    for (let y = 0; y < size; y++) {\n      for (let x = 0; x < size; x++) {\n        if (x > 2 && x < size - 3 && y > 2 && y < size - 3) continue;\n        borderPixels++;\n        if (alphaAt(x, y) > 220) opaqueBorderPixels++;\n      }\n    }\n\n    const cornerAlpha = (startX, startY) => {\n      let total = 0;\n      for (let y = startY; y < startY + 4; y++) {\n        for (let x = startX; x < startX + 4; x++) total += alphaAt(x, y);\n      }\n      return total / 16;\n    };\n    const cornersOpaque = [\n      cornerAlpha(0, 0),\n      cornerAlpha(size - 4, 0),\n      cornerAlpha(0, size - 4),\n      cornerAlpha(size - 4, size - 4),\n    ].every((alpha) => alpha > 220);\n\n    return cornersOpaque && opaqueBorderPixels / borderPixels > 0.82;\n  }\n\n  /**\n   * Decode any phone photo (including HEIC / empty MIME) into a PNG Shopify will keep.\n   * @param {File} file\n   * @returns {Promise<File>}\n   */\n  async #normalizeLogoFile(file) {\n    const decoded = await this.#decodeImage(file);\n    // Browser-ready formats within the limits go up as the customer's own\n    // file: re-encoding a JPEG as PNG only made it bigger and slower to upload.\n    const readyType = ['image/png', 'image/jpeg', 'image/webp'].includes(file.type);\n    if (readyType && file.size <= this.#maxBytes && Math.max(decoded.width, decoded.height) <= 2000) {\n      decoded.close?.();\n      return file;\n    }\n    const canvas = document.createElement('canvas');\n    let width = decoded.width;\n    let height = decoded.height;\n    const maxEdge = 2000;\n    if (Math.max(width, height) > maxEdge) {\n      const scale = maxEdge / Math.max(width, height);\n      width = Math.round(width * scale);\n      height = Math.round(height * scale);\n    }\n    canvas.width = Math.max(1, width);\n    canvas.height = Math.max(1, height);\n    const ctx = canvas.getContext('2d');\n    if (!ctx) {\n      decoded.close?.();\n      throw new Error('Canvas unavailable');\n    }\n    ctx.drawImage(decoded.image, 0, 0, canvas.width, canvas.height);\n    decoded.close?.();\n\n    const toBlob = (type, quality) =>\n      new Promise((resolve) => canvas.toBlob((blob) => resolve(blob), type, quality));\n\n    let blob = await toBlob('image/png');\n    if (blob && blob.size > this.#maxBytes) blob = await toBlob('image/jpeg', 0.88);\n    if (blob && blob.size > this.#maxBytes) blob = await toBlob('image/jpeg', 0.72);\n    if (!blob || blob.size === 0) throw new Error('Could not encode logo');\n\n    const ext = blob.type === 'image/jpeg' ? 'jpg' : 'png';\n    return new File([blob], `${this.#baseName(file.name)}.${ext}`, { type: blob.type });\n  }\n\n  /**\n   * @param {File} file\n   * @returns {Promise<string>}\n   */\n  async #fileToPngDataUrl(file) {\n    const decoded = await this.#decodeImage(file);\n    const canvas = document.createElement('canvas');\n    canvas.width = decoded.width;\n    canvas.height = decoded.height;\n    const ctx = canvas.getContext('2d');\n    if (!ctx) {\n      decoded.close?.();\n      return URL.createObjectURL(file);\n    }\n    ctx.drawImage(decoded.image, 0, 0);\n    decoded.close?.();\n    return canvas.toDataURL('image/png');\n  }\n\n  /**\n   * @param {File} file\n   * @returns {Promise<{ image: CanvasImageSource, width: number, height: number, close?: () => void }>}\n   */\n  async #decodeImage(file) {\n    if (typeof createImageBitmap === 'function') {\n      try {\n        const bitmap = await createImageBitmap(file);\n        return { image: bitmap, width: bitmap.width, height: bitmap.height, close: () => bitmap.close?.() };\n      } catch {\n        // fall through\n      }\n    }\n    const url = URL.createObjectURL(file);\n    const image = await this.#loadImageElement(url);\n    return { image, width: image.naturalWidth, height: image.naturalHeight };\n  }\n\n  /**\n   * Curve + shade so the overlay reads more like stitches on a seat back.\n   * @param {string} src\n   * @returns {Promise<string | null>}\n   */\n  async #warpPreview(src) {\n    const image = await this.#loadImageElement(src);\n    const width = Math.max(1, Math.min(900, image.naturalWidth || image.width));\n    const height = Math.max(1, Math.round((width / image.naturalWidth) * image.naturalHeight));\n    const source = document.createElement('canvas');\n    source.width = width;\n    source.height = height;\n    const sourceCtx = source.getContext('2d', { willReadFrequently: true });\n    if (!sourceCtx) return null;\n    sourceCtx.drawImage(image, 0, 0, width, height);\n    const sourceData = sourceCtx.getImageData(0, 0, width, height);\n    const out = document.createElement('canvas');\n    out.width = width;\n    out.height = height;\n    const outCtx = out.getContext('2d');\n    if (!outCtx) return null;\n    const outImage = outCtx.createImageData(width, height);\n    const strength = 0.12;\n    const vBulge = 0.03;\n\n    for (let y = 0; y < height; y++) {\n      const ny = y / (height - 1 || 1);\n      const vCurve = 1 + vBulge * Math.sin(ny * Math.PI);\n      for (let x = 0; x < width; x++) {\n        const nx = (x / (width - 1 || 1)) * 2 - 1;\n\n        // A hint of curvature, not a fisheye. The previous mapping ran the\n        // whole width through sin(), whose slope at the centre is 1.65 \u2014 so\n        // the middle of every logo was squeezed by about two thirds while the\n        // edges stretched, and a 280px circle came out 219px wide. Leaning a\n        // little way from a straight mapping toward the sine keeps the\n        // curvature but holds the edges at \u00B11, so nothing is lost or crushed.\n        const srcNx = nx + strength * (Math.sin(nx * (Math.PI / 2)) - nx);\n        const srcX = this.#clamp(((srcNx + 1) / 2) * (width - 1), 0, width - 1);\n        const srcY = this.#clamp(((ny - 0.5) / vCurve + 0.5) * (height - 1), 0, height - 1);\n        const x0 = Math.floor(srcX);\n        const y0 = Math.floor(srcY);\n        const x1 = Math.min(width - 1, x0 + 1);\n        const y1 = Math.min(height - 1, y0 + 1);\n        const fx = srcX - x0;\n        const fy = srcY - y0;\n        const i = (y * width + x) * 4;\n        const s = sourceData.data;\n        const i00 = (y0 * width + x0) * 4;\n        const i10 = (y0 * width + x1) * 4;\n        const i01 = (y1 * width + x0) * 4;\n        const i11 = (y1 * width + x1) * 4;\n        // Blend with each sample weighted by its alpha. A transparent pixel\n        // still stores a colour \u2014 usually the white that was just keyed out\n        // \u2014 and mixing that in as if it were visible wrapped every cut edge\n        // in a pale halo on the seat.\n        const a00 = s[i00 + 3] * (1 - fx) * (1 - fy);\n        const a10 = s[i10 + 3] * fx * (1 - fy);\n        const a01 = s[i01 + 3] * (1 - fx) * fy;\n        const a11 = s[i11 + 3] * fx * fy;\n        const alpha = a00 + a10 + a01 + a11;\n        for (let c = 0; c < 3; c++) {\n          outImage.data[i + c] =\n            alpha > 0 ? Math.round((s[i00 + c] * a00 + s[i10 + c] * a10 + s[i01 + c] * a01 + s[i11 + c] * a11) / alpha) : 0;\n        }\n        outImage.data[i + 3] = Math.round(alpha);\n        const shade = 1 - Math.abs(nx) * 0.1;\n        outImage.data[i] = Math.round(outImage.data[i] * shade);\n        outImage.data[i + 1] = Math.round(outImage.data[i + 1] * shade);\n        outImage.data[i + 2] = Math.round(outImage.data[i + 2] * shade);\n      }\n    }\n\n    outCtx.putImageData(outImage, 0, 0);\n    return out.toDataURL('image/png');\n  }\n\n  /** @param {string} message */\n  #showError(message) {\n    for (const el of [this.refs.errorMessage, this.refs.modalError]) {\n      if (!el) continue;\n      el.textContent = message;\n      el.removeAttribute('hidden');\n    }\n  }\n\n  #clearError() {\n    for (const el of [this.refs.errorMessage, this.refs.modalError]) {\n      if (!el) continue;\n      el.textContent = '';\n      el.setAttribute('hidden', '');\n    }\n  }\n\n  /**\n   * @param {number} value\n   * @param {number} min\n   * @param {number} max\n   */\n  #clamp(value, min, max) {\n    return Math.min(max, Math.max(min, value));\n  }\n}\n\nif (!customElements.get('branding-logo-placer')) {\n  customElements.define('branding-logo-placer', BrandingLogoPlacer);\n}\n"],
  "mappings": "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",
  "names": ["logo", "input", "blob"]
}
