{
  "version": 3,
  "sources": ["/cdn/shop/t/16/assets/lp-book-scene.js?x=1793824132886"],
  "sourcesContent": ["// Landing hero's 3D books (sections/lp-hero-scroll.liquid): a thin adapter\n// around the book3d scene module (assets/book-scene.js, synced by\n// .github/scripts/sync_book_scene.py). The page owns the copy, layout and\n// scrolling; the module owns the models, lighting, horizontal drag /\n// arrow-key orbit and the Proverbs opening.\n//\n// The scene mounts when the element connects and is disposed when it\n// disconnects, which covers theme-editor section load/unload and reorders.\n// Each mount gets a fresh canvas, since disposal releases its WebGL context.\n// The spines poster stays until the scene is drawn. If the scene fails to\n// load, or isn't drawn within REST_AFTER_MS of a mount (each remount gets its\n// own deadline), it is disposed and the resting poster (the covers, as the\n// reveal leaves them) replaces the spines for good (rest()). Without a\n// hardware GPU it never mounts: the resting poster comes in at once (see\n// hasFastWebGL). A rendering\n// failure after the scene was drawn (lost WebGL context, shader error) brings\n// the poster back and remounts; a failed Proverbs opening, which the module\n// reports with error.code 'opening', is retried on the next gesture. The\n// Proverbs opening downloads only when the visitor first turns right.\n//\n// Reveal: the scene opens on the spines, and its \"scroll progress\" (0..1)\n// turns them to the covers. The adapter plays that once, over REVEAL_MS, as\n// soon as the scene is drawn (progress fed linearly; the module eases it);\n// page scroll moves neither it nor the orbit. A remount mid-reveal continues\n// it. A swipe or arrow key takes over from the reveal (the module's manual\n// control).\nconst REVEAL_MS = 2500;\nconst REST_AFTER_MS = 10000;\n// The page's wording for the module's status codes ('ready' clears it).\nconst STATUS = {\n  loading: 'Loading the journals.',\n  opening: 'Opening Proverbs.',\n  ready: '',\n  'load-error': 'The journals couldn\u2019t load.',\n  'opening-error': 'Proverbs couldn\u2019t open. Turn it again to try once more.',\n};\n// The bundled arrangement with each layout's camera field of view narrowed by\n// that layout's zoom, which enlarges every view of the books (covers, spines\n// and the open Proverbs) without moving the camera.\nconst zoomed = (arrangement, zoom) => {\n  const copy = structuredClone(arrangement);\n  for (const [layout, factor] of Object.entries(zoom)) {\n    const camera = copy.layouts?.[layout]?.camera;\n    if (!camera || factor === 1) continue;\n    const half = (camera.fov * Math.PI) / 360;\n    camera.fov = (Math.atan(Math.tan(half) / factor) * 360) / Math.PI;\n  }\n  return copy;\n};\n// Whether WebGL draws on a GPU. With failIfMajorPerformanceCaveat the\n// browser refuses a context it would render in software (a blocklisted GPU,\n// hardware acceleration turned off, a virtual machine, headless test\n// browsers), where each of the scene's frames ties up the main thread for\n// seconds, and once it has drawn the 10 s deadline no longer applies:\n// measured 2026-11-04 in the\n// dev-checks Lighthouse runs, 4 of 6 landing loads hung and the rest spent\n// 175-232 s in book-scene.js. That flag only covers Chrome's fallback to\n// software, though: headless Chrome (Lighthouse, PageSpeed Insights, CI)\n// asks for SwiftShader outright (--use-angle=swiftshader-webgl) and gets a\n// context with no caveat, and each frame's commit then held the main thread\n// for ~3.6 s (dev-checks run 41866408032), so every landing load hung. So\n// the renderer is also named, and a software rasterizer counts as no GPU.\n// Asked once per page; the probe's context is released at once.\nconst SOFTWARE_RENDERER = /swiftshader|llvmpipe|lavapipe|softpipe|software|basic render driver/i;\nlet fastWebGL;\nconst hasFastWebGL = () => {\n  if (fastWebGL === undefined) {\n    let gl = null;\n    try {\n      gl = document.createElement('canvas').getContext('webgl2', { failIfMajorPerformanceCaveat: true });\n    } catch {\n      gl = null;\n    }\n    let renderer = '';\n    try {\n      const info = gl?.getExtension('WEBGL_debug_renderer_info');\n      renderer = String(gl?.getParameter(info ? info.UNMASKED_RENDERER_WEBGL : gl.RENDERER) || '');\n    } catch {\n      renderer = '';\n    }\n    fastWebGL = Boolean(gl) && !SOFTWARE_RENDERER.test(renderer);\n    gl?.getExtension('WEBGL_lose_context')?.loseContext();\n  }\n  return fastWebGL;\n};\nconst VIEWS = {\n  covers: 'Showing the covers.',\n  spines: 'Showing the spines.',\n  inside: 'Proverbs is open.',\n};\n\nif (!customElements.get('lp-book-scene')) {\n  customElements.define(\n    'lp-book-scene',\n    class LpBookScene extends HTMLElement {\n      connectedCallback() {\n        this.track = this.closest('.lp-hero');\n        this.stage = this.closest('.lp-hero__stage');\n        this.status = this.querySelector('[data-scene-status]');\n        if (!this.track || !this.stage || !this.dataset.module) return;\n\n        this.onPageHide = (event) => event.persisted && this.unmount();\n        this.onPageShow = (event) => event.persisted && this.mount();\n        window.addEventListener('pagehide', this.onPageHide);\n        window.addEventListener('pageshow', this.onPageShow);\n        this.mount();\n      }\n\n      // No scene from here on: dispose it, stop its downloads and cross-fade\n      // the spines to the resting poster once that has decoded (lp-hero-scroll.css,\n      // data-state=\"still\"); the spines stay if it can't load.\n      rest() {\n        if (this.resting) return;\n        this.resting = true;\n        this.unmount();\n        const picture = this.querySelector('template[data-scene-rest]')?.content.firstElementChild?.cloneNode(true);\n        if (!picture) return;\n        const image = picture.querySelector('img');\n        this.querySelector('.lp-scene__poster').after(picture);\n        image\n          .decode()\n          .then(() => {\n            if (this.resting) this.dataset.state = 'still';\n          })\n          .catch(() => picture.remove());\n      }\n\n      disconnectedCallback() {\n        this.unmount();\n        window.removeEventListener('pagehide', this.onPageHide);\n        window.removeEventListener('pageshow', this.onPageShow);\n      }\n\n      async mount() {\n        if (this.lifetime || this.resting) return;\n        if (!hasFastWebGL()) {\n          this.rest();\n          return;\n        }\n        const lifetime = (this.lifetime = new AbortController());\n        const { signal } = lifetime;\n        this.deadline = setTimeout(() => this.rest(), REST_AFTER_MS);\n\n        let mountBookScene;\n        let defaultArrangement;\n        try {\n          ({ mountBookScene, defaultArrangement } = await import(this.dataset.module));\n        } catch (error) {\n          if (!signal.aborted) this.fail(error);\n          return;\n        }\n        if (signal.aborted) return;\n\n        const canvas = document.createElement('canvas');\n        canvas.className = 'lp-scene__canvas';\n        canvas.tabIndex = 0;\n        canvas.setAttribute('aria-label', this.dataset.label || '');\n        this.querySelector('.lp-scene__canvas')?.remove();\n        this.prepend(canvas);\n        this.dataset.state = 'loading';\n\n        // The framing padding and zoom are set in lp-hero-scroll.css, which\n        // also sizes the poster by them.\n        const style = getComputedStyle(this);\n        const number = (name, fallback) => {\n          const value = parseFloat(style.getPropertyValue(name));\n          return Number.isFinite(value) ? value : fallback;\n        };\n        const framingPadding = { mobile: number('--lp-pad-mobile', 0.08), desktop: number('--lp-pad-desktop', 0.14) };\n        const zoom = { mobile: number('--lp-zoom-mobile', 1), desktop: number('--lp-zoom-desktop', 1) };\n\n        let openingFailed = false;\n        try {\n          this.scene = mountBookScene({\n            canvas,\n            manifestURL: this.dataset.manifest,\n            framingPadding,\n            arrangement: zoomed(defaultArrangement, zoom),\n            onReady: () => {\n              clearTimeout(this.deadline);\n              this.dataset.state = 'ready';\n              this.reveal();\n            },\n            onStatus: (text, code) => this.announce(STATUS[code] ?? text),\n            onView: (view) => this.announce(VIEWS[view] || ''),\n            onError: (error) => {\n              if (this.dataset.state !== 'ready') return this.fail(error);\n              // Proverbs' opening failed to prepare, but the closed scene\n              // still renders: retry it on the next gesture. Otherwise\n              // rendering itself failed ('context-lost', or 'shader', which\n              // stops the module drawing for good): back to the poster, then\n              // a fresh canvas.\n              if (error?.code === 'opening') {\n                openingFailed = true;\n                return;\n              }\n              this.restart(error);\n            },\n          });\n        } catch (error) {\n          this.fail(error);\n          return;\n        }\n        this.scene.ready.catch(() => {});\n\n        const retry = () => {\n          if (!openingFailed) return;\n          openingFailed = false;\n          this.scene.retryOpening().catch(() => {});\n        };\n        canvas.addEventListener('pointerdown', retry, { signal });\n        canvas.addEventListener(\n          'keydown',\n          (event) => ['ArrowLeft', 'ArrowRight', 'Home'].includes(event.key) && retry(),\n          { signal }\n        );\n\n        this.frame = 0;\n        this.reveal();\n      }\n\n\n      unmount() {\n        if (!this.lifetime) return;\n        this.lifetime.abort();\n        this.lifetime = null;\n        clearTimeout(this.deadline);\n        cancelAnimationFrame(this.frame);\n        this.frame = 0;\n        this.scene?.dispose();\n        this.scene = null;\n        this.querySelector('.lp-scene__canvas')?.remove();\n        delete this.dataset.state;\n      }\n\n      fail(error) {\n        console.error('Book scene:', error);\n        this.rest();\n      }\n\n      // Remount after a rendering failure, outside the module's callback.\n      // After two restarts the resting poster stays for good.\n      restart(error) {\n        console.error('Book scene:', error);\n        if (this.restarting) return;\n        this.restarting = true;\n        this.restarts = (this.restarts || 0) + 1;\n        delete this.dataset.state;\n        setTimeout(() => {\n          this.restarting = false;\n          this.unmount();\n          if (this.restarts > 2) this.rest();\n          else if (this.isConnected) this.mount();\n        });\n      }\n\n      announce(text) {\n        if (this.status) this.status.textContent = text;\n      }\n\n      // Feeds the reveal's progress each frame until it reaches 1, from the\n      // first time the scene is drawn (the clock survives a remount).\n      reveal() {\n        cancelAnimationFrame(this.frame);\n        this.frame = 0;\n        if (!this.scene || this.dataset.state !== 'ready') return;\n        this.revealClock ||= performance.now();\n        const tick = () => {\n          this.frame = 0;\n          const progress = Math.min(1, (performance.now() - this.revealClock) / REVEAL_MS);\n          this.scene?.setScrollProgress(progress);\n          if (progress < 1) this.frame = requestAnimationFrame(tick);\n        };\n        tick();\n      }\n    }\n  );\n}\n"],
  "mappings": "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",
  "names": []
}
