{"version":3,"sources":["../src/persistence.js","../src/detector.js","../src/modal.js","../src/presets.js","../src/index.js"],"names":["memoryStore","resolveStorage","kind","store","probe","k","v","parseDuration","value","match","amount","unit","ExitStore","key","scope","raw","parsed","now","next","maxDisplays","cooldownMs","count","last","Emitter","type","handler","detail","fn","err","isBrowser","ExitIntentDetector","options","m","base","b","e","touch","mobile","reason","y","delta","FOCUSABLE","TW","THEMES","escapeHtml","str","c","uid","ExitModal","o","doc","overlay","dialog","titleEl","bodyEl","closeEl","actionsEl","cta","dismiss","el","container","reveal","remove","style","els","first","active","P","pid","discount","opts","code","title","text","ctaLabel","href","onClaim","theme","rest","id","chip","hint","newsletter","onSubmit","placeholder","submit","input","error","email","cartSaver","onResume","eyebrow","presets","exitIntent","config","modalConfig","onExit","detectorOptions","modal","detector","handle","src_default"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @file Lightweight persistence + frequency-capping for exit-intent triggers.\n * Backed by localStorage (persistent) or sessionStorage (per-session), with a\n * safe in-memory fallback when Web Storage is unavailable (SSR, privacy mode).\n */\n\n/** In-memory fallback so the library never throws when storage is blocked. */\nconst memoryStore = new Map()\n\n/**\n * Resolve the requested Storage, degrading to an in-memory shim on failure.\n * @param {'local'|'session'} kind\n * @returns {Storage}\n */\nfunction resolveStorage(kind) {\n  try {\n    const store = kind === 'session' ? window.sessionStorage : window.localStorage\n    const probe = '__oei_probe__'\n    store.setItem(probe, '1')\n    store.removeItem(probe)\n    return store\n  } catch {\n    return /** @type {Storage} */ ({\n      getItem: (k) => (memoryStore.has(k) ? memoryStore.get(k) : null),\n      setItem: (k, v) => void memoryStore.set(k, String(v)),\n      removeItem: (k) => void memoryStore.delete(k),\n    })\n  }\n}\n\n/**\n * Parse a cooldown value into milliseconds.\n * Accepts a number (ms) or a shorthand string: `500ms`, `30s`, `15m`, `24h`, `7d`, `2w`.\n * @param {number|string|undefined|null} value\n * @returns {number} Milliseconds (0 when unset/invalid).\n */\nexport function parseDuration(value) {\n  if (value == null) return 0\n  if (typeof value === 'number') return value > 0 ? value : 0\n  const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)?\\s*$/i.exec(value)\n  if (!match) return 0\n  const amount = parseFloat(match[1])\n  const unit = (match[2] || 'ms').toLowerCase()\n  const factors = { ms: 1, s: 1e3, m: 6e4, h: 36e5, d: 864e5, w: 6048e5 }\n  return Math.round(amount * factors[unit])\n}\n\n/**\n * @typedef {Object} ExitState\n * @property {number} count   Number of times the trigger has fired for this key.\n * @property {number} last    Epoch ms of the most recent fire (0 if never).\n */\n\n/**\n * Frequency-cap store for a single exit-intent instance.\n */\nexport class ExitStore {\n  /**\n   * @param {Object} [options]\n   * @param {string} [options.key='oei:default'] Storage key namespace.\n   * @param {'local'|'session'} [options.scope='local'] Persistence scope.\n   */\n  constructor({ key = 'oei:default', scope = 'local' } = {}) {\n    this.key = key\n    this.storage = resolveStorage(scope)\n  }\n\n  /** @returns {ExitState} */\n  read() {\n    try {\n      const raw = this.storage.getItem(this.key)\n      if (!raw) return { count: 0, last: 0 }\n      const parsed = JSON.parse(raw)\n      return {\n        count: Number(parsed.count) || 0,\n        last: Number(parsed.last) || 0,\n      }\n    } catch {\n      return { count: 0, last: 0 }\n    }\n  }\n\n  /**\n   * Record a fire: increments count and stamps `last`.\n   * @param {number} now Epoch ms (injectable for testing).\n   * @returns {ExitState}\n   */\n  record(now) {\n    const state = this.read()\n    const next = { count: state.count + 1, last: now }\n    try {\n      this.storage.setItem(this.key, JSON.stringify(next))\n    } catch {\n      /* storage full / blocked — best effort only */\n    }\n    return next\n  }\n\n  /** Clear all stored state for this key. */\n  reset() {\n    try {\n      this.storage.removeItem(this.key)\n    } catch {\n      /* no-op */\n    }\n  }\n\n  /**\n   * Whether the trigger is allowed to fire right now.\n   * @param {Object} rules\n   * @param {number} [rules.maxDisplays=Infinity] Cap on total displays.\n   * @param {number} [rules.cooldownMs=0] Minimum ms between displays.\n   * @param {number} rules.now Epoch ms (injectable for testing).\n   * @returns {boolean}\n   */\n  canFire({ maxDisplays = Infinity, cooldownMs = 0, now }) {\n    const { count, last } = this.read()\n    if (count >= maxDisplays) return false\n    if (cooldownMs > 0 && count > 0 && now - last < cooldownMs) return false\n    return true\n  }\n}\n","/**\n * @file Framework-agnostic exit-intent detector. Emits `exit` when a visitor\n * signals they are about to leave, with no UI of its own. Pair it with\n * {@link ExitModal} or any custom handler.\n */\n\nimport { ExitStore, parseDuration } from './persistence.js'\n\n/**\n * @typedef {Object} MobileOptions\n * @property {boolean} [inactivity=true]  Fire after a period of no interaction.\n * @property {number}  [inactivityMs=15000] Idle time before firing (ms).\n * @property {boolean} [scrollUp=true]    Fire on a fast upward scroll near the top.\n * @property {boolean} [backButton=false] Trap the first Back press via history state.\n */\n\n/**\n * @typedef {Object} ExitIntentOptions\n * @property {number}  [threshold=20] Distance (px) from the top edge that counts as leaving.\n * @property {number}  [delay=0] Grace period (ms) before firing; cancelled if the pointer returns.\n * @property {boolean|MobileOptions} [mobile=false] Enable touch-device heuristics.\n * @property {number}  [maxDisplays=Infinity] Max times to fire per persisted scope.\n * @property {number|string} [cooldown=0] Minimum time between fires (`'7d'`, `'24h'`, ms).\n * @property {'local'|'session'} [scope='local'] Persistence scope for frequency caps.\n * @property {string}  [storageKey='oei:default'] Storage namespace.\n * @property {boolean} [autoStart=true] Attach listeners immediately on construction.\n * @property {() => number} [now] Clock provider (injectable for testing).\n * @property {(detail: ExitDetail) => void} [onExit] Convenience callback for the `exit` event.\n */\n\n/**\n * @typedef {Object} ExitDetail\n * @property {'desktop-top'|'mobile-inactivity'|'mobile-scroll'|'mobile-back'|'manual'} reason\n * @property {number} count Total fires including this one.\n */\n\n/** Minimal typed event emitter. */\nclass Emitter {\n  constructor() {\n    /** @type {Map<string, Set<Function>>} */\n    this._listeners = new Map()\n  }\n\n  /**\n   * @param {string} type\n   * @param {Function} handler\n   * @returns {() => void} Unsubscribe function.\n   */\n  on(type, handler) {\n    if (!this._listeners.has(type)) this._listeners.set(type, new Set())\n    this._listeners.get(type).add(handler)\n    return () => this.off(type, handler)\n  }\n\n  /**\n   * @param {string} type\n   * @param {Function} handler\n   */\n  off(type, handler) {\n    this._listeners.get(type)?.delete(handler)\n  }\n\n  /**\n   * @param {string} type\n   * @param {*} [detail]\n   */\n  emit(type, detail) {\n    this._listeners.get(type)?.forEach((fn) => {\n      try {\n        fn(detail)\n      } catch (err) {\n        // eslint-disable-next-line no-console\n        console.error(`[exit-intent] listener for \"${type}\" threw`, err)\n      }\n    })\n  }\n}\n\nconst isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'\n\n/**\n * Detects exit intent across desktop and (optionally) touch devices.\n *\n * @example\n * const detector = new ExitIntentDetector({ cooldown: '1d', maxDisplays: 3 })\n * const off = detector.on('exit', ({ reason }) => console.log('leaving:', reason))\n */\nexport class ExitIntentDetector extends Emitter {\n  /** @param {ExitIntentOptions} [options] */\n  constructor(options = {}) {\n    super()\n    /** @type {Required<Omit<ExitIntentOptions, 'mobile'|'onExit'|'now'>> & ExitIntentOptions} */\n    this.options = {\n      threshold: 20,\n      delay: 0,\n      mobile: false,\n      maxDisplays: Infinity,\n      cooldown: 0,\n      scope: 'local',\n      storageKey: 'oei:default',\n      autoStart: true,\n      ...options,\n    }\n    this._now = options.now || (() => Date.now())\n    this._cooldownMs = parseDuration(this.options.cooldown)\n    this._store = new ExitStore({ key: this.options.storageKey, scope: this.options.scope })\n    this._active = false\n    this._pending = null\n    this._idleTimer = null\n    this._lastScrollY = 0\n    this._bound = null\n\n    if (options.onExit) this.on('exit', options.onExit)\n    if (this.options.autoStart) this.start()\n  }\n\n  /** @returns {MobileOptions|null} Normalized mobile config, or null if disabled. */\n  get _mobileConfig() {\n    const m = this.options.mobile\n    if (!m) return null\n    const base = { inactivity: true, inactivityMs: 15000, scrollUp: true, backButton: false }\n    return m === true ? base : { ...base, ...m }\n  }\n\n  /** @returns {boolean} True on touch/no-hover devices. */\n  get _isTouch() {\n    if (!isBrowser) return false\n    return (\n      ('ontouchstart' in window || navigator.maxTouchPoints > 0) &&\n      window.matchMedia?.('(hover: none)').matches !== false\n    )\n  }\n\n  /** Attach listeners. Idempotent. @returns {this} */\n  start() {\n    if (!isBrowser || this._active) return this\n    this._active = true\n    const b = {\n      mouseout: (e) => this._onMouseOut(e),\n      mousemove: () => this._cancelPending(),\n      scroll: () => this._onScroll(),\n      touch: () => this._resetIdle(),\n      popstate: () => this._onPopState(),\n    }\n    this._bound = b\n\n    const touch = this._isTouch\n    const mobile = this._mobileConfig\n\n    if (!touch) {\n      document.addEventListener('mouseout', b.mouseout, { passive: true })\n      document.addEventListener('mousemove', b.mousemove, { passive: true })\n    }\n\n    if (touch && mobile) {\n      if (mobile.scrollUp) {\n        this._lastScrollY = window.scrollY\n        window.addEventListener('scroll', b.scroll, { passive: true })\n      }\n      if (mobile.inactivity) {\n        window.addEventListener('touchstart', b.touch, { passive: true })\n        window.addEventListener('scroll', b.touch, { passive: true })\n        this._resetIdle()\n      }\n      if (mobile.backButton) {\n        try {\n          history.pushState({ oei: true }, '')\n          window.addEventListener('popstate', b.popstate)\n        } catch {\n          /* history unavailable */\n        }\n      }\n    }\n    return this\n  }\n\n  /** Detach all listeners and clear timers. Idempotent. @returns {this} */\n  stop() {\n    if (!isBrowser || !this._active) return this\n    this._active = false\n    const b = this._bound\n    if (b) {\n      document.removeEventListener('mouseout', b.mouseout)\n      document.removeEventListener('mousemove', b.mousemove)\n      window.removeEventListener('scroll', b.scroll)\n      window.removeEventListener('touchstart', b.touch)\n      window.removeEventListener('scroll', b.touch)\n      window.removeEventListener('popstate', b.popstate)\n    }\n    this._cancelPending()\n    if (this._idleTimer) clearTimeout(this._idleTimer)\n    this._idleTimer = null\n    return this\n  }\n\n  /** Stop and remove all event subscribers. */\n  destroy() {\n    this.stop()\n    this._listeners.clear()\n  }\n\n  /** Clear persisted frequency-cap state (counts + cooldown). */\n  reset() {\n    this._store.reset()\n    return this\n  }\n\n  /**\n   * Force the exit flow, honoring frequency caps.\n   * @param {ExitDetail['reason']} [reason='manual']\n   * @returns {boolean} Whether it actually fired.\n   */\n  trigger(reason = 'manual') {\n    return this._fire(reason)\n  }\n\n  /* ---- internal handlers ---- */\n\n  _onMouseOut(e) {\n    // Only when leaving the window at the top, not moving into another element.\n    if (e.clientY > this.options.threshold) return\n    const to = e.relatedTarget || e.toElement\n    if (to) return\n    if (this.options.delay > 0) {\n      if (this._pending) return\n      this._pending = setTimeout(() => {\n        this._pending = null\n        this._fire('desktop-top')\n      }, this.options.delay)\n    } else {\n      this._fire('desktop-top')\n    }\n  }\n\n  _cancelPending() {\n    if (this._pending) {\n      clearTimeout(this._pending)\n      this._pending = null\n    }\n  }\n\n  _onScroll() {\n    const y = window.scrollY\n    const delta = y - this._lastScrollY\n    this._lastScrollY = y\n    // Fast upward fling while near the top of the page.\n    if (delta < -40 && y < 200) this._fire('mobile-scroll')\n  }\n\n  _resetIdle() {\n    const mobile = this._mobileConfig\n    if (!mobile?.inactivity) return\n    if (this._idleTimer) clearTimeout(this._idleTimer)\n    this._idleTimer = setTimeout(() => this._fire('mobile-inactivity'), mobile.inactivityMs)\n  }\n\n  _onPopState() {\n    this._fire('mobile-back')\n  }\n\n  /**\n   * @param {ExitDetail['reason']} reason\n   * @returns {boolean}\n   */\n  _fire(reason) {\n    const now = this._now()\n    if (\n      !this._store.canFire({\n        maxDisplays: this.options.maxDisplays,\n        cooldownMs: this._cooldownMs,\n        now,\n      })\n    ) {\n      return false\n    }\n    const { count } = this._store.record(now)\n    if (count >= this.options.maxDisplays) this.stop()\n    this.emit('exit', { reason, count })\n    return true\n  }\n}\n","/**\n * @file Accessible, Tailwind-styled modal used as the default exit-intent UI.\n *\n * Every element carries BOTH Tailwind utility classes (so apps with Tailwind get\n * styling for free) and a semantic `oei-*` hook class (targeted by the shipped\n * fallback CSS, so it also looks right without Tailwind). Import the fallback via\n * `@oblique-code/exit-intent/styles.css` when your app has no Tailwind build.\n */\n\nconst FOCUSABLE =\n  'a[href],button:not([disabled]),textarea,input,select,[tabindex]:not([tabindex=\"-1\"])'\n\n/**\n * Tailwind utility strings per element. Mirror these in `src/styles.css` so the\n * two rendering paths stay visually identical.\n */\nconst TW = {\n  overlay:\n    'oei-overlay fixed inset-0 z-[2147483000] flex items-center justify-center bg-slate-950/70 p-4 opacity-0 backdrop-blur-sm transition-opacity duration-200 ease-out',\n  overlayOpen: 'oei-overlay--open opacity-100',\n  dialog:\n    'oei-dialog relative w-full max-w-md scale-95 rounded-2xl bg-white p-6 text-slate-900 shadow-2xl transition-transform duration-200 ease-out dark:bg-slate-900 dark:text-slate-100 sm:p-8',\n  dialogOpen: 'oei-dialog--open scale-100',\n  // p-0/border-0/bg-transparent are explicit resets: host pages commonly define\n  // a generic `button { padding/border/background }` rule, and since a plain\n  // type selector still wins any property this class doesn't itself declare,\n  // omitting these let a 36px icon button collapse to 0 width under such a rule.\n  close:\n    'oei-close absolute right-3 top-3 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full border-0 bg-transparent p-0 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-500 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-white',\n  title: 'oei-title pr-8 text-xl font-semibold tracking-tight text-slate-900 dark:text-white sm:text-2xl',\n  body: 'oei-body mt-2 text-sm leading-relaxed text-slate-600 dark:text-slate-300',\n  actions: 'oei-actions mt-6 flex flex-col gap-2 sm:flex-row-reverse',\n  // Color-free CTA base; the accent colors come from THEMES below.\n  cta: 'oei-cta inline-flex cursor-pointer items-center justify-center rounded-xl border-0 px-5 py-2.5 text-sm font-medium no-underline transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900',\n  dismiss:\n    'oei-dismiss inline-flex cursor-pointer items-center justify-center rounded-xl border-0 bg-transparent px-5 py-2.5 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-500 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white',\n}\n\n/**\n * Accent themes for the primary CTA. Static strings so Tailwind's scanner can\n * pick them up; each also carries an `oei-cta--*` hook for the fallback CSS.\n * All pairs meet WCAG AA contrast (≥ 4.5:1) in default and hover states.\n * @type {Record<'slate'|'indigo'|'emerald'|'rose'|'amber', string>}\n */\nconst THEMES = {\n  slate:\n    'oei-cta--slate bg-slate-900 text-white hover:bg-slate-700 focus-visible:ring-slate-500 dark:bg-white dark:text-slate-900 dark:hover:bg-slate-200',\n  indigo:\n    'oei-cta--indigo bg-indigo-600 text-white hover:bg-indigo-700 focus-visible:ring-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-400 dark:hover:text-indigo-950',\n  emerald:\n    'oei-cta--emerald bg-emerald-700 text-white hover:bg-emerald-800 focus-visible:ring-emerald-500 dark:bg-emerald-500 dark:text-emerald-950 dark:hover:bg-emerald-400',\n  rose:\n    'oei-cta--rose bg-rose-600 text-white hover:bg-rose-700 focus-visible:ring-rose-500 dark:bg-rose-500 dark:hover:bg-rose-400 dark:hover:text-rose-950',\n  amber:\n    'oei-cta--amber bg-amber-400 text-amber-950 hover:bg-amber-300 focus-visible:ring-amber-500',\n}\n\n/**\n * @typedef {Object} CtaConfig\n * @property {string} label Button text.\n * @property {string} [href] If set, renders an anchor; otherwise a button.\n * @property {string} [target] Anchor target (e.g. `_blank`).\n * @property {() => (void|boolean)} [onClick] Click handler (also runs for anchors).\n *   Return `false` to keep the modal open (e.g. failed validation).\n */\n\n/**\n * @typedef {Object} ExitModalOptions\n * @property {string} [title='Wait — before you go'] Heading text.\n * @property {string} [html] Trusted HTML for the body. Takes precedence over `text`.\n * @property {string} [text] Plain-text body (safely escaped).\n * @property {'slate'|'indigo'|'emerald'|'rose'|'amber'} [theme='slate'] CTA accent theme.\n * @property {CtaConfig|null} [cta] Primary call-to-action.\n * @property {string|null} [dismissLabel='No thanks'] Secondary dismiss button; null to hide.\n * @property {boolean} [closeButton=true] Show the top-right ✕ button.\n * @property {boolean} [closeOnBackdrop=true] Close when the backdrop is clicked.\n * @property {boolean} [closeOnEsc=true] Close on the Escape key.\n * @property {boolean} [lockScroll=true] Prevent background scroll while open.\n * @property {HTMLElement} [container=document.body] Where the modal is mounted.\n * @property {(dialog: HTMLElement) => void} [onRender] Called once after the DOM is\n *   built — bind events to custom `html` content here.\n * @property {(reason: 'cta'|'dismiss'|'backdrop'|'esc'|'close'|'programmatic') => void} [onClose]\n * @property {() => void} [onOpen]\n */\n\n/** Escape a string for safe insertion as text content. */\nfunction escapeHtml(str) {\n  return String(str).replace(\n    /[&<>\"']/g,\n    (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c],\n  )\n}\n\nlet uid = 0\n\n/**\n * A self-contained, accessible modal dialog.\n *\n * @example\n * const modal = new ExitModal({ title: 'Leaving already?', text: 'Take 10% off.' })\n * modal.open()\n */\nexport class ExitModal {\n  /** @param {ExitModalOptions} [options] */\n  constructor(options = {}) {\n    /** @type {ExitModalOptions} */\n    this.options = {\n      title: 'Wait — before you go',\n      theme: 'slate',\n      dismissLabel: 'No thanks',\n      closeButton: true,\n      closeOnBackdrop: true,\n      closeOnEsc: true,\n      lockScroll: true,\n      ...options,\n    }\n    this.isOpen = false\n    this._id = `oei-modal-${++uid}`\n    this._overlay = null\n    this._dialog = null\n    this._lastActive = null\n    this._prevOverflow = ''\n    this._onKeydown = (e) => this._handleKeydown(e)\n  }\n\n  /** Build the DOM (once) and return the overlay element. @returns {HTMLElement} */\n  render() {\n    if (this._overlay) return this._overlay\n    const o = this.options\n    const doc = document\n\n    const overlay = doc.createElement('div')\n    overlay.className = TW.overlay\n    overlay.setAttribute('role', 'presentation')\n\n    const dialog = doc.createElement('div')\n    dialog.className = TW.dialog\n    dialog.setAttribute('role', 'dialog')\n    dialog.setAttribute('aria-modal', 'true')\n    dialog.id = this._id\n\n    // Title\n    let titleEl = null\n    if (o.title) {\n      titleEl = doc.createElement('h2')\n      titleEl.className = TW.title\n      titleEl.id = `${this._id}-title`\n      titleEl.textContent = o.title\n      dialog.setAttribute('aria-labelledby', titleEl.id)\n    }\n\n    // Body\n    let bodyEl = null\n    if (o.html || o.text) {\n      bodyEl = doc.createElement('div')\n      bodyEl.className = TW.body\n      bodyEl.id = `${this._id}-body`\n      if (o.html) bodyEl.innerHTML = o.html\n      else bodyEl.textContent = o.text\n      dialog.setAttribute('aria-describedby', bodyEl.id)\n    }\n\n    // Close (✕) button\n    let closeEl = null\n    if (o.closeButton) {\n      closeEl = doc.createElement('button')\n      closeEl.type = 'button'\n      closeEl.className = TW.close\n      closeEl.setAttribute('aria-label', 'Close dialog')\n      closeEl.innerHTML =\n        '<svg class=\"shrink-0\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>'\n      closeEl.addEventListener('click', () => this.close('close'))\n    }\n\n    // Actions\n    let actionsEl = null\n    if (o.cta || o.dismissLabel) {\n      actionsEl = doc.createElement('div')\n      actionsEl.className = TW.actions\n      if (o.cta) {\n        const cta = o.cta.href ? doc.createElement('a') : doc.createElement('button')\n        cta.className = `${TW.cta} ${THEMES[o.theme] || THEMES.slate}`\n        cta.textContent = o.cta.label\n        if (o.cta.href) {\n          cta.href = o.cta.href\n          if (o.cta.target) cta.target = o.cta.target\n        } else {\n          cta.type = 'button'\n        }\n        cta.addEventListener('click', (e) => {\n          if (o.cta.onClick?.() === false) {\n            // Handler vetoed (e.g. validation failed) — keep the modal open.\n            if (o.cta.href) e.preventDefault()\n            return\n          }\n          this.close('cta')\n        })\n        actionsEl.appendChild(cta)\n      }\n      if (o.dismissLabel) {\n        const dismiss = doc.createElement('button')\n        dismiss.type = 'button'\n        dismiss.className = TW.dismiss\n        dismiss.textContent = o.dismissLabel\n        dismiss.addEventListener('click', () => this.close('dismiss'))\n        actionsEl.appendChild(dismiss)\n      }\n    }\n\n    ;[closeEl, titleEl, bodyEl, actionsEl].forEach((el) => el && dialog.appendChild(el))\n    overlay.appendChild(dialog)\n\n    if (o.closeOnBackdrop) {\n      overlay.addEventListener('click', (e) => {\n        if (e.target === overlay) this.close('backdrop')\n      })\n    }\n\n    this._overlay = overlay\n    this._dialog = dialog\n    o.onRender?.(dialog)\n    return overlay\n  }\n\n  /** Mount + reveal the modal. @returns {this} */\n  open() {\n    if (this.isOpen) return this\n    const overlay = this.render()\n    const container = this.options.container || document.body\n    this._lastActive = document.activeElement\n    container.appendChild(overlay)\n\n    if (this.options.lockScroll) {\n      this._prevOverflow = document.body.style.overflow\n      document.body.style.overflow = 'hidden'\n    }\n\n    document.addEventListener('keydown', this._onKeydown, true)\n\n    // Next frame → trigger the transition, then move focus in.\n    const reveal = () => {\n      overlay.classList.add(...TW.overlayOpen.split(' '))\n      this._dialog.classList.add(...TW.dialogOpen.split(' '))\n      this._focusFirst()\n    }\n    if (typeof requestAnimationFrame === 'function') requestAnimationFrame(reveal)\n    else reveal()\n\n    this.isOpen = true\n    this.options.onOpen?.()\n    return this\n  }\n\n  /**\n   * Hide + unmount the modal, restoring focus and scroll.\n   * @param {'cta'|'dismiss'|'backdrop'|'esc'|'close'|'programmatic'} [reason='programmatic']\n   * @returns {this}\n   */\n  close(reason = 'programmatic') {\n    if (!this.isOpen) return this\n    this.isOpen = false\n    document.removeEventListener('keydown', this._onKeydown, true)\n\n    const overlay = this._overlay\n    overlay.classList.remove(...TW.overlayOpen.split(' '))\n    this._dialog.classList.remove(...TW.dialogOpen.split(' '))\n\n    if (this.options.lockScroll) document.body.style.overflow = this._prevOverflow\n\n    const remove = () => overlay.parentNode && overlay.parentNode.removeChild(overlay)\n    const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n    if (reduced) remove()\n    else setTimeout(remove, 220)\n\n    // Restore focus to the element that was focused before opening.\n    if (this._lastActive && typeof this._lastActive.focus === 'function') {\n      this._lastActive.focus()\n    }\n    this.options.onClose?.(reason)\n    return this\n  }\n\n  /** Remove the modal from the DOM immediately and drop references. */\n  destroy() {\n    this.close('programmatic')\n    this._overlay = null\n    this._dialog = null\n  }\n\n  /* ---- internal ---- */\n\n  _focusableEls() {\n    return Array.from(this._dialog.querySelectorAll(FOCUSABLE)).filter((el) => {\n      if (el.hasAttribute('disabled') || el.getAttribute('aria-hidden') === 'true') return false\n      if (el.hidden) return false\n      const style = typeof getComputedStyle === 'function' ? getComputedStyle(el) : null\n      return !style || (style.display !== 'none' && style.visibility !== 'hidden')\n    })\n  }\n\n  _focusFirst() {\n    const els = this._focusableEls()\n    ;(els[0] || this._dialog).focus?.()\n    if (!els.length) this._dialog.setAttribute('tabindex', '-1')\n  }\n\n  _handleKeydown(e) {\n    if (e.key === 'Escape' && this.options.closeOnEsc) {\n      e.preventDefault()\n      this.close('esc')\n      return\n    }\n    if (e.key !== 'Tab') return\n    // Focus trap.\n    const els = this._focusableEls()\n    if (!els.length) {\n      e.preventDefault()\n      return\n    }\n    const first = els[0]\n    const last = els[els.length - 1]\n    const active = document.activeElement\n    if (e.shiftKey && active === first) {\n      e.preventDefault()\n      last.focus()\n    } else if (!e.shiftKey && active === last) {\n      e.preventDefault()\n      first.focus()\n    }\n  }\n}\n\nexport { escapeHtml }\n","/**\n * @file Ready-made, production-styled modal configurations.\n *\n * Each preset returns a plain {@link ExitModalOptions} object, so you can use it\n * directly or spread-and-override any field:\n *\n * ```js\n * exitIntent({ modal: presets.discount({ code: 'SAVE15', discount: '15%' }) })\n * new ExitModal({ ...presets.newsletter({ onSubmit }), title: 'Custom title' })\n * ```\n *\n * Like the modal itself, every element carries Tailwind utilities plus an\n * `oei-*` hook class covered by the fallback stylesheet.\n */\n\nimport { escapeHtml } from './modal.js'\n\n/** @typedef {import('./modal.js').ExitModalOptions} ExitModalOptions */\n\n/** Static Tailwind strings for preset-only elements (mirrored in styles.css). */\nconst P = {\n  eyebrow:\n    'oei-eyebrow mb-2 inline-flex items-center gap-1.5 rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-slate-700 dark:bg-slate-800 dark:text-slate-300',\n  coupon:\n    'oei-coupon mt-4 flex select-all items-center justify-center rounded-xl border-2 border-dashed border-slate-300 bg-slate-50 px-4 py-3 font-mono text-lg font-semibold tracking-[0.2em] text-slate-900 dark:border-slate-600 dark:bg-slate-800 dark:text-white',\n  couponHint: 'oei-coupon-hint mt-2 text-center text-xs text-slate-500 dark:text-slate-400',\n  field: 'oei-field mt-4',\n  input:\n    'oei-input w-full rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm text-slate-900 placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:border-slate-600 dark:bg-slate-800 dark:text-white dark:placeholder:text-slate-400',\n  inputError: 'oei-input--error border-rose-500 focus:ring-rose-500 focus:border-rose-500',\n  errorMsg: 'oei-error mt-1.5 text-xs font-medium text-rose-600 dark:text-rose-400',\n}\n\nlet pid = 0\n\n/**\n * @typedef {Object} DiscountPresetOptions\n * @property {string} [discount='10%'] Discount amount shown in the copy.\n * @property {string} [code='SAVE10'] Coupon code displayed in the dashed chip.\n * @property {string} [title] Heading override.\n * @property {string} [text] Lead sentence override.\n * @property {string} [ctaLabel] CTA label override.\n * @property {string} [href] Where the CTA navigates (optional).\n * @property {() => (void|boolean)} [onClaim] CTA click handler.\n * @property {ExitModalOptions['theme']} [theme='indigo']\n */\n\n/**\n * Discount / coupon-code modal with a click-to-copy code chip.\n * @param {DiscountPresetOptions} [opts]\n * @returns {ExitModalOptions}\n */\nexport function discount(opts = {}) {\n  const {\n    discount: amount = '10%',\n    code = 'SAVE10',\n    title = `Wait — take ${amount} off`,\n    text = 'Use this code at checkout before you go. It only takes a second.',\n    ctaLabel = `Claim ${amount} off`,\n    href,\n    onClaim,\n    theme = 'indigo',\n    ...rest\n  } = opts\n  const id = `oei-coupon-${++pid}`\n\n  return {\n    title,\n    theme,\n    html: [\n      `<p>${escapeHtml(text)}</p>`,\n      `<button type=\"button\" id=\"${id}\" class=\"${P.coupon} w-full cursor-pointer\" aria-label=\"Copy code ${escapeHtml(code)}\">${escapeHtml(code)}</button>`,\n      `<p class=\"${P.couponHint}\" id=\"${id}-hint\" aria-live=\"polite\">Click the code to copy it</p>`,\n    ].join(''),\n    cta: { label: ctaLabel, href, onClick: onClaim },\n    onRender(dialog) {\n      const chip = dialog.querySelector(`#${id}`)\n      const hint = dialog.querySelector(`#${id}-hint`)\n      chip?.addEventListener('click', async () => {\n        try {\n          await navigator.clipboard.writeText(code)\n          if (hint) hint.textContent = 'Copied to clipboard ✓'\n        } catch {\n          if (hint) hint.textContent = 'Select and copy the code above'\n        }\n      })\n    },\n    ...rest,\n  }\n}\n\n/**\n * @typedef {Object} NewsletterPresetOptions\n * @property {(email: string) => void} onSubmit Called with a valid email.\n * @property {string} [title] Heading override.\n * @property {string} [text] Lead sentence override.\n * @property {string} [placeholder='you@example.com']\n * @property {string} [ctaLabel='Subscribe']\n * @property {ExitModalOptions['theme']} [theme='emerald']\n */\n\n/**\n * Email-capture modal with inline validation (invalid email keeps it open).\n * @param {NewsletterPresetOptions} opts\n * @returns {ExitModalOptions}\n */\nexport function newsletter(opts = {}) {\n  const {\n    onSubmit,\n    title = 'Before you go…',\n    text = 'Get our best tips in your inbox. No spam, unsubscribe anytime.',\n    placeholder = 'you@example.com',\n    ctaLabel = 'Subscribe',\n    theme = 'emerald',\n    ...rest\n  } = opts\n  const id = `oei-email-${++pid}`\n\n  const submit = () => {\n    const input = /** @type {HTMLInputElement|null} */ (document.getElementById(id))\n    const error = document.getElementById(`${id}-error`)\n    const email = input?.value.trim() ?? ''\n    const valid = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)\n    if (!valid) {\n      input?.classList.add(...P.inputError.split(' '))\n      input?.setAttribute('aria-invalid', 'true')\n      if (error) error.hidden = false\n      input?.focus()\n      return false // veto close\n    }\n    onSubmit?.(email)\n    return true\n  }\n\n  return {\n    title,\n    theme,\n    html: [\n      `<p>${escapeHtml(text)}</p>`,\n      `<div class=\"${P.field}\">`,\n      `<label for=\"${id}\" class=\"sr-only\">Email address</label>`,\n      `<input id=\"${id}\" class=\"${P.input}\" type=\"email\" name=\"email\" autocomplete=\"email\" inputmode=\"email\" placeholder=\"${escapeHtml(placeholder)}\" aria-describedby=\"${id}-error\" />`,\n      `<p id=\"${id}-error\" class=\"${P.errorMsg}\" role=\"alert\" hidden>Please enter a valid email address.</p>`,\n      `</div>`,\n    ].join(''),\n    cta: { label: ctaLabel, onClick: submit },\n    onRender(dialog) {\n      const input = dialog.querySelector(`#${id}`)\n      // Enter submits; typing clears the error state.\n      input?.addEventListener('keydown', (e) => {\n        if (e.key === 'Enter') {\n          e.preventDefault()\n          dialog.querySelector('.oei-cta')?.click()\n        }\n      })\n      input?.addEventListener('input', () => {\n        input.classList.remove(...P.inputError.split(' '))\n        input.removeAttribute('aria-invalid')\n        const error = dialog.querySelector(`#${id}-error`)\n        if (error) error.hidden = true\n      })\n    },\n    ...rest,\n  }\n}\n\n/**\n * @typedef {Object} CartSaverPresetOptions\n * @property {string} [title] Heading override.\n * @property {string} [text] Lead sentence override.\n * @property {string} [ctaLabel='Return to cart']\n * @property {string} [href] Cart URL for the CTA.\n * @property {() => (void|boolean)} [onResume] CTA click handler.\n * @property {string} [eyebrow='Your cart is waiting'] Small badge above the title; '' hides it.\n * @property {ExitModalOptions['theme']} [theme='rose']\n */\n\n/**\n * Cart-abandonment modal nudging the visitor back to checkout.\n * @param {CartSaverPresetOptions} [opts]\n * @returns {ExitModalOptions}\n */\nexport function cartSaver(opts = {}) {\n  const {\n    title = 'Leaving your cart behind?',\n    text = 'Your items are saved for now, but they’re not reserved — popular items sell out fast.',\n    ctaLabel = 'Return to cart',\n    href,\n    onResume,\n    eyebrow = 'Your cart is waiting',\n    theme = 'rose',\n    ...rest\n  } = opts\n\n  return {\n    title,\n    theme,\n    html: [\n      eyebrow ? `<span class=\"${P.eyebrow}\">🛒 ${escapeHtml(eyebrow)}</span>` : '',\n      `<p>${escapeHtml(text)}</p>`,\n    ].join(''),\n    cta: { label: ctaLabel, href, onClick: onResume },\n    dismissLabel: 'Continue browsing',\n    ...rest,\n  }\n}\n\n/** All presets, keyed by name. */\nexport const presets = { discount, newsletter, cartSaver }\n","/**\n * @file Public entry point for `@oblique-code/exit-intent`.\n */\n\nimport { ExitIntentDetector } from './detector.js'\nimport { ExitModal } from './modal.js'\n\nexport { ExitIntentDetector } from './detector.js'\nexport { ExitModal } from './modal.js'\nexport { ExitStore, parseDuration } from './persistence.js'\nexport { presets, discount, newsletter, cartSaver } from './presets.js'\n\n/**\n * @typedef {import('./detector.js').ExitIntentOptions} ExitIntentOptions\n * @typedef {import('./modal.js').ExitModalOptions} ExitModalOptions\n */\n\n/**\n * @typedef {ExitIntentOptions & {\n *   modal?: ExitModalOptions | false,\n *   onExit?: (detail: import('./detector.js').ExitDetail) => void\n * }} ExitIntentConfig\n */\n\n/**\n * @typedef {Object} ExitIntentHandle\n * @property {ExitIntentDetector} detector The underlying detector.\n * @property {ExitModal|null} modal The modal instance (null when `modal: false`).\n * @property {() => ExitIntentHandle} start Re-arm the detector.\n * @property {() => ExitIntentHandle} stop Detach listeners.\n * @property {() => void} open Show the modal now (bypasses detection).\n * @property {() => void} close Hide the modal.\n * @property {() => void} reset Clear persisted frequency-cap state.\n * @property {() => void} destroy Tear everything down.\n */\n\n/**\n * One-call setup: wires a detector to a Tailwind modal with sensible defaults.\n *\n * @param {ExitIntentConfig} [config]\n * @returns {ExitIntentHandle}\n *\n * @example\n * import { exitIntent } from '@oblique-code/exit-intent'\n * import '@oblique-code/exit-intent/styles.css'\n *\n * exitIntent({\n *   cooldown: '7d',\n *   maxDisplays: 1,\n *   modal: {\n *     title: 'Wait — before you go',\n *     text: 'Here is 10% off your first order.',\n *     cta: { label: 'Claim 10% off', href: '/offer' },\n *   },\n * })\n */\nexport function exitIntent(config = {}) {\n  const { modal: modalConfig, onExit, ...detectorOptions } = config\n\n  const modal = modalConfig === false ? null : new ExitModal(modalConfig || {})\n\n  const detector = new ExitIntentDetector({\n    ...detectorOptions,\n    onExit: (detail) => {\n      if (modal) modal.open()\n      onExit?.(detail)\n    },\n  })\n\n  /** @type {ExitIntentHandle} */\n  const handle = {\n    detector,\n    modal,\n    start() {\n      detector.start()\n      return handle\n    },\n    stop() {\n      detector.stop()\n      return handle\n    },\n    open() {\n      modal?.open()\n    },\n    close() {\n      modal?.close()\n    },\n    reset() {\n      detector.reset()\n    },\n    destroy() {\n      detector.destroy()\n      modal?.destroy()\n    },\n  }\n  return handle\n}\n\nexport default exitIntent\n"]}