{"release_id":"01a1c502-4ad1-0000-ec62-adc54d3c69aa","chunk_id":"01a1c502-4c3e-77f1-97f5-430d92f8850d","file":"Dt1nNecJ.mjs","mappings":"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","names":["mode","displayName","rarity","foilUrl","$.derived","$$props","dotColor","revealed","$.state","imgLoaded","imgError","foilBlob","foilBlobUrl","filename","$.user_effect","seen","img","$.get","$.set","r","blob","isTouchDevice","downloadFoil","file","analytics.trackShare","err","a","div","root","div_1","div_2","div_3","div_4","span","span_1","span_2","span_3","div_5","div_6","root_1","p","p_1","t","img_1","root_2","$.set_attribute","$0","$$render","consequent","alternate","div_7","div_8","root_3","span_4","root_4","consequent_1","span_5","root_5","consequent_2","consequent_3","div_9","$.sibling","node_1","button","classes","$.set_style","classes_1","$.set_class","$.delegated"],"sources":["../../../../../../src/lib/components/FoilCardHero.svelte"],"sourcesContent":["<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\timport * as analytics from '$lib/analytics';\n\n\tinterface Props {\n\t\tgenerationId: string;\n\t\tmode?: 'roast' | 'hype';\n\t\tdisplayName?: string | null;\n\t\trarity?: string | null;\n\t}\n\tlet { generationId, mode = 'roast', displayName = null, rarity = null }: Props = $props();\n\n\tconst foilUrl = $derived(`/api/generation/${generationId}/foil.png`);\n\tconst dotColor = $derived(mode === 'hype' ? '#ffe500' : '#ff4d6d');\n\n\tlet revealed = $state(false);\n\tlet imgLoaded = $state(false);\n\tlet imgError = $state(false);\n\n\t// Pre-fetched blob + URL for the foil PNG. Storing both means we can\n\t// build a File for the Web Share API on mobile (Photos / gallery save)\n\t// AND fall back to <a download> on desktop without re-fetching.\n\tlet foilBlob = $state<Blob | null>(null);\n\tlet foilBlobUrl = $state<string | null>(null);\n\n\tconst filename = $derived(\n\t\tdisplayName ? `ratemyd-${displayName.replace(/[^a-z0-9]+/gi, '-')}.png` : 'ratemyd-foil-card.png'\n\t);\n\n\t// Preload + decode the foil PNG BEFORE the flip animation starts. Without\n\t// this, the front-face <img> can finish decoding mid-flip and the reveal\n\t// looks janky (image pops in halfway through the rotation).\n\t$effect(() => {\n\t\tif (typeof window === 'undefined') return;\n\t\tconst seen = localStorage.getItem(`foil-revealed-${generationId}`);\n\n\t\tconst img = new Image();\n\t\timg.src = foilUrl;\n\t\timg.decode()\n\t\t\t.then(() => {\n\t\t\t\timgLoaded = true;\n\t\t\t\tif (seen) {\n\t\t\t\t\trevealed = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// brief hold so the closed pack reads first, then flip\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\trevealed = true;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlocalStorage.setItem(`foil-revealed-${generationId}`, '1');\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/* localStorage blocked — flip still plays once per page load */\n\t\t\t\t\t}\n\t\t\t\t}, 500);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\timgError = true;\n\t\t\t});\n\n\t\t// Pre-fetch the PNG as a blob so the click handler is synchronous.\n\t\t// Critical for iOS Safari, where awaiting a fetch inside the click\n\t\t// handler drops user activation and the share / download fails.\n\t\tfetch(foilUrl)\n\t\t\t.then((r) => (r.ok ? r.blob() : null))\n\t\t\t.then((blob) => {\n\t\t\t\tif (blob) {\n\t\t\t\t\tfoilBlob = blob;\n\t\t\t\t\tfoilBlobUrl = URL.createObjectURL(blob);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t/* button stays disabled if blob isn't ready */\n\t\t\t});\n\t});\n\n\t// Touch devices: Web Share API → share sheet routes the file to\n\t// Photos / gallery (the only reliable path on iOS Safari, where <a\n\t// download> is ignored on cross-origin redirects, and on Android\n\t// Chrome where it dumps to Downloads instead of the gallery).\n\t// Desktops: <a download> direct save. canShare returns true on desktop\n\t// Chrome/Edge/Safari too (RAT-131), so we gate on `(pointer: coarse)`\n\t// to avoid showing a share sheet to mouse users.\n\tfunction isTouchDevice() {\n\t\treturn typeof window !== 'undefined' && window.matchMedia('(pointer: coarse)').matches;\n\t}\n\n\tasync function downloadFoil() {\n\t\tif (!foilBlob || !foilBlobUrl) return;\n\n\t\tif (isTouchDevice()) {\n\t\t\tconst file = new File([foilBlob], filename, { type: 'image/png' });\n\t\t\tif (navigator.canShare?.({ files: [file] })) {\n\t\t\t\ttry {\n\t\t\t\t\tawait navigator.share({ files: [file] });\n\t\t\t\t\tanalytics.trackShare('foil_download', 'result');\n\t\t\t\t\treturn;\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// AbortError = user dismissed the sheet. Anything else, fall\n\t\t\t\t\t// back to the download link below so they aren't left with\n\t\t\t\t\t// no way to grab the card.\n\t\t\t\t\tif ((err as Error)?.name === 'AbortError') return;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst a = document.createElement('a');\n\t\ta.href = foilBlobUrl;\n\t\ta.download = filename;\n\t\tdocument.body.appendChild(a);\n\t\ta.click();\n\t\ta.remove();\n\t\tanalytics.trackShare('foil_download', 'result');\n\t}\n</script>\n\n<div class=\"hero\">\n\t<div class=\"flip-stage\">\n\t\t<div class=\"flip-inner\" class:revealed>\n\t\t\t<!-- BACK · closed pack -->\n\t\t\t<div class=\"face back\">\n\t\t\t\t<div class=\"back-shine\"></div>\n\t\t\t\t<div class=\"back-logo\">\n\t\t\t\t\t<span class=\"back-mark\">ratemyd<span style=\"color: {dotColor}\">.</span></span>\n\t\t\t\t\t<span class=\"back-sub\">{t('foil collection')}</span>\n\t\t\t\t</div>\n\t\t\t\t<span class=\"back-tap\">{t('tap to reveal')}</span>\n\t\t\t</div>\n\n\t\t\t<!-- FRONT · server-rendered PNG -->\n\t\t\t<div class=\"face front\">\n\t\t\t\t{#if imgError}\n\t\t\t\t\t<div class=\"error\">\n\t\t\t\t\t\t<p>{t('the card render is still cooking.')}</p>\n\t\t\t\t\t\t<p class=\"error-sub\">{t('refresh the page in a moment.')}</p>\n\t\t\t\t\t</div>\n\t\t\t\t{:else}\n\t\t\t\t\t<img src={foilUrl} alt={t('your foil card')} decoding=\"async\" />\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"glow\" class:on={revealed}></div>\n\t</div>\n\n\t{#if displayName || rarity}\n\t\t<div class=\"meta\">\n\t\t\t{#if displayName}<span class=\"meta-name\">{displayName}</span>{/if}\n\t\t\t{#if rarity}<span class=\"meta-rarity\">{rarity}</span>{/if}\n\t\t</div>\n\t{/if}\n\n\t<div class=\"ctas\">\n\t\t<button class=\"cta primary\" onclick={downloadFoil} disabled={!foilBlobUrl || imgError}>\n\t\t\t<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n\t\t\t\t<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7,10 12,15 17,10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>\n\t\t\t</svg>\n\t\t\t{t('download foil card')}\n\t\t</button>\n\t</div>\n</div>\n\n<style>\n\t.hero {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 16px;\n\t}\n\t.flip-stage {\n\t\tposition: relative;\n\t\twidth: 320px;\n\t\tmax-width: 80vw;\n\t\taspect-ratio: 63/88;\n\t\tperspective: 1600px;\n\t}\n\t@media (min-width: 640px) {\n\t\t.flip-stage { width: 380px; }\n\t}\n\t.flip-inner {\n\t\tposition: relative;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\ttransform-style: preserve-3d;\n\t\ttransition: transform 1s cubic-bezier(0.22, 1.4, 0.3, 1);\n\t}\n\t.flip-inner.revealed { transform: rotateY(180deg); }\n\t.face {\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tbackface-visibility: hidden;\n\t\t-webkit-backface-visibility: hidden;\n\t\tborder-radius: 18px;\n\t\toverflow: hidden;\n\t}\n\t.front { transform: rotateY(180deg); }\n\t.front img {\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tdisplay: block;\n\t\tobject-fit: cover;\n\t\tborder-radius: 18px;\n\t}\n\t.error {\n\t\twidth: 100%; height: 100%;\n\t\tdisplay: flex; flex-direction: column;\n\t\talign-items: center; justify-content: center;\n\t\tbackground: var(--color-surface);\n\t\tcolor: var(--color-muted);\n\t\tfont-size: 12px;\n\t\ttext-align: center;\n\t\tpadding: 20px;\n\t}\n\t.error-sub { opacity: 0.6; margin-top: 4px; }\n\n\t/* card BACK · matches site design */\n\t.back {\n\t\tbackground: #0c0c0c;\n\t\tborder: 1px solid #262626;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tpadding: 22px;\n\t}\n\t.back::before {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tbackground: radial-gradient(ellipse at 50% 30%, #161616 0%, #0c0c0c 75%);\n\t}\n\t.back-shine {\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tbackground: linear-gradient(115deg, transparent 45%, rgba(255,255,255,.05) 50%, transparent 55%);\n\t\tmix-blend-mode: screen;\n\t\tanimation: shineFlow 5s linear infinite;\n\t\tbackground-size: 280% 100%;\n\t\tpointer-events: none;\n\t}\n\t@keyframes shineFlow {\n\t\t0% { background-position: 200% 0; }\n\t\t100% { background-position: -100% 0; }\n\t}\n\t.back-logo {\n\t\tposition: relative;\n\t\tz-index: 2;\n\t\ttext-align: center;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 12px;\n\t}\n\t.back-mark {\n\t\tfont-family: var(--font-display, 'Space Grotesk', system-ui, sans-serif);\n\t\tfont-size: 56px;\n\t\tline-height: 0.92;\n\t\tletter-spacing: -0.03em;\n\t\tfont-weight: 700;\n\t\tcolor: #f0f0f0;\n\t}\n\t@media (min-width: 640px) {\n\t\t.back-mark { font-size: 68px; }\n\t}\n\t.back-sub {\n\t\tfont-family: var(--font-display, 'Space Grotesk', system-ui, sans-serif);\n\t\tfont-size: 9px;\n\t\tfont-weight: 600;\n\t\tletter-spacing: 0.4em;\n\t\ttext-transform: uppercase;\n\t\tcolor: #737373;\n\t}\n\t.back-tap {\n\t\tposition: absolute;\n\t\tbottom: 28px;\n\t\tleft: 0; right: 0;\n\t\ttext-align: center;\n\t\tfont-family: var(--font-display, 'Space Grotesk', system-ui, sans-serif);\n\t\tfont-size: 10px;\n\t\tfont-weight: 600;\n\t\tletter-spacing: 0.32em;\n\t\ttext-transform: uppercase;\n\t\tcolor: rgba(255,255,255,0.42);\n\t\tz-index: 2;\n\t\tanimation: tapPulse 1.6s ease-in-out infinite;\n\t}\n\t@keyframes tapPulse {\n\t\t0%, 100% { opacity: 0.5; }\n\t\t50% { opacity: 0.95; }\n\t}\n\t.flip-inner.revealed .back-tap { opacity: 0; animation: none; }\n\n\t/* glow pulse around the card after the flip */\n\t.glow {\n\t\tposition: absolute;\n\t\tinset: -32px;\n\t\tborder-radius: 32px;\n\t\tpointer-events: none;\n\t\tz-index: -1;\n\t\topacity: 0;\n\t\tbackground: radial-gradient(ellipse at center,\n\t\t\trgba(255,221,51,0.35) 0%,\n\t\t\trgba(255,0,110,0.18) 40%,\n\t\t\ttransparent 70%);\n\t\tfilter: blur(20px);\n\t}\n\t.glow.on {\n\t\tanimation: glowPulse 1.4s ease-out 0.4s 1 forwards;\n\t}\n\t@keyframes glowPulse {\n\t\t0% { opacity: 0; transform: scale(0.85); }\n\t\t50% { opacity: 1; transform: scale(1.05); }\n\t\t100% { opacity: 0.4; transform: scale(1.0); }\n\t}\n\n\t/* meta info row under the card */\n\t.meta {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 10px;\n\t\tfont-family: var(--font-display, 'Space Grotesk', system-ui, sans-serif);\n\t\tfont-size: 11px;\n\t\tfont-weight: 700;\n\t\tletter-spacing: 0.18em;\n\t\ttext-transform: uppercase;\n\t\tcolor: var(--color-muted, #737373);\n\t\tmargin-top: 4px;\n\t\topacity: 0;\n\t\ttransform: translateY(6px);\n\t\ttransition: opacity 0.5s ease 1s, transform 0.5s ease 1s;\n\t}\n\t.hero:has(.flip-inner.revealed) .meta {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t}\n\t.meta-name { color: var(--color-text, #f0f0f0); }\n\t.meta-rarity::before {\n\t\tcontent: '·';\n\t\tmargin-right: 8px;\n\t\tcolor: var(--color-muted, #737373);\n\t}\n\n\t/* CTA buttons */\n\t.ctas {\n\t\tdisplay: flex;\n\t\tgap: 12px;\n\t\tmargin-top: 6px;\n\t\topacity: 0;\n\t\ttransform: translateY(8px);\n\t\ttransition: opacity 0.5s ease 1.1s, transform 0.5s ease 1.1s;\n\t\tpointer-events: none;\n\t}\n\t.hero:has(.flip-inner.revealed) .ctas {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t\tpointer-events: auto;\n\t}\n\t.cta {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 8px;\n\t\tpadding: 12px 22px;\n\t\tborder-radius: 12px;\n\t\tfont-family: var(--font-display, 'Space Grotesk', system-ui, sans-serif);\n\t\tfont-size: 13px;\n\t\tfont-weight: 700;\n\t\tcursor: pointer;\n\t\tborder: none;\n\t\ttransition: all 0.2s ease;\n\t}\n\t.cta:disabled { opacity: 0.5; cursor: not-allowed; }\n\t.cta.primary {\n\t\tbackground: var(--color-text, #f0f0f0);\n\t\tcolor: var(--color-bg, #0c0c0c);\n\t}\n\t.cta.primary:hover:not(:disabled) {\n\t\ttransform: translateY(-1px);\n\t}\n</style>\n"],"version":3}