{"id":2191,"date":"2026-04-21T19:44:29","date_gmt":"2026-04-21T17:44:29","guid":{"rendered":"https:\/\/zulumaps.com\/soundwave\/"},"modified":"2026-08-05T20:57:23","modified_gmt":"2026-08-05T18:57:23","slug":"soundwave","status":"publish","type":"page","link":"https:\/\/zulumaps.com\/de\/soundwave\/","title":{"rendered":"Soundwave"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"2191\" class=\"elementor elementor-2191 elementor-1200\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-85f9cac e-con-full e-flex e-con e-parent\" data-id=\"85f9cac\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-36d6b05 elementor-widget elementor-widget-shortcode\" data-id=\"36d6b05\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\"><script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/html2canvas\/1.4.1\/html2canvas.min.js\"><\/script>\n\n<div class=\"pme\" id=\"pme-root\">\n  <nav class=\"pme-nav\">\n    <button class=\"pme-nav-btn active\" data-panel=\"song\">\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M9 18V5l12-2v13\"\/><circle cx=\"6\" cy=\"18\" r=\"3\"\/><circle cx=\"18\" cy=\"16\" r=\"3\"\/><\/svg>Song\n    <\/button>\n    <button class=\"pme-nav-btn\" data-panel=\"labels\">\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7\"\/><path d=\"M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z\"\/><\/svg>Texte\n    <\/button>\n    <button class=\"pme-nav-btn\" data-panel=\"style\">\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"\/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"\/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"\/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"\/><\/svg>Stil\n    <\/button>\n    <button class=\"pme-nav-btn\" data-panel=\"size\">\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"\/><\/svg>Gr\u00f6\u00dfe\n    <\/button>\n  <\/nav>\n\n  <div class=\"pme-mobnav\">\n    <button class=\"pme-mobbtn active\" data-panel=\"song\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M9 18V5l12-2v13\"\/><circle cx=\"6\" cy=\"18\" r=\"3\"\/><circle cx=\"18\" cy=\"16\" r=\"3\"\/><\/svg>Song<\/button>\n    <button class=\"pme-mobbtn\" data-panel=\"labels\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7\"\/><path d=\"M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z\"\/><\/svg>Texte<\/button>\n    <button class=\"pme-mobbtn\" data-panel=\"style\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"\/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"\/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"\/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"\/><\/svg>Stil<\/button>\n    <button class=\"pme-mobbtn\" data-panel=\"size\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\"\/><\/svg>Gr\u00f6\u00dfe<\/button>\n  <\/div>\n\n  <div class=\"pme-panel\">\n    \n    <div class=\"pme-section active\" id=\"sec-song\">\n      <div class=\"pme-title\">Song<\/div>\n      <div class=\"pme-desc\">Suche nach deinem Lieblingssong, um seinen einzigartigen visuellen Fingerabdruck zu generieren.<\/div>\n      \n      <div style=\"position:relative; margin-bottom:14px;\">\n        <div class=\"pme-search\" style=\"margin-bottom:0;\">\n          <svg class=\"pme-sic\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"\/><path d=\"m21 21-4.35-4.35\"\/><\/svg>\n          <input type=\"text\" id=\"inp-search\" value=\"One Love - Bob Marley\" placeholder=\"Suche nach einem Song...\" autocomplete=\"off\"\/>\n        <\/div>\n        <div class=\"pme-results\" id=\"song-results\"><\/div>\n      <\/div>\n      \n      <div class=\"pme-pop-lbl\">Beliebte Songs<\/div>\n      <ul class=\"pme-pop\">\n        <li data-name=\"Blinding Lights\" data-artist=\"The Weeknd\" data-album=\"After Hours\" data-year=\"2019\">Blinding Lights - The Weeknd<\/li>\n        <li data-name=\"Shape of You\" data-artist=\"Ed Sheeran\" data-album=\"\u00f7 (Divide)\" data-year=\"2017\">Shape of You - Ed Sheeran<\/li>\n        <li data-name=\"Someone You Loved\" data-artist=\"Lewis Capaldi\" data-album=\"Divinely Uninspired to a Hellish Extent\" data-year=\"2018\">Someone You Loved - Lewis Capaldi<\/li>\n        <li data-name=\"Sunflower\" data-artist=\"Post Malone\" data-album=\"Hollywood's Bleeding\" data-year=\"2018\">Sunflower - Post Malone<\/li>\n        <li data-name=\"Starboy\" data-artist=\"The Weeknd\" data-album=\"Starboy\" data-year=\"2016\">Starboy - The Weeknd<\/li>\n        <li data-name=\"As It Was\" data-artist=\"Harry Styles\" data-album=\"Harry's House\" data-year=\"2022\">As It Was - Harry Styles<\/li>\n        <li data-name=\"Dance Monkey\" data-artist=\"Tones And I\" data-album=\"The Kids Are Coming\" data-year=\"2019\">Dance Monkey - Tones And I<\/li>\n      <\/ul>\n    <\/div>\n\n    <div class=\"pme-section\" id=\"sec-labels\">\n      <div class=\"pme-title\">Texte<\/div>\n      <div class=\"pme-desc\">Passe den Text auf deiner Music Map an.<\/div>\n      \n      <span class=\"pme-lbl\">Titel<\/span>\n      <div class=\"pme-field\"><input type=\"text\" id=\"inp-h1\" value=\"One Love\" \/><\/div>\n      \n      <span class=\"pme-lbl\">Interpret<\/span>\n      <div class=\"pme-field\"><input type=\"text\" id=\"inp-h2\" value=\"Bob Marley\" \/><\/div>\n\n      <span class=\"pme-lbl\">Album<\/span>\n      <div class=\"pme-field\"><input type=\"text\" id=\"inp-b1\" value=\"Exodus\" \/><\/div>\n      \n      <span class=\"pme-lbl\">Jahr<\/span>\n      <div class=\"pme-field\"><input type=\"text\" id=\"inp-b2\" value=\"1977\" \/><\/div>\n    <\/div>\n\n    <div class=\"pme-section\" id=\"sec-style\">\n      <div class=\"pme-title\">Stil & Form<\/div>\n      <div class=\"pme-desc\">W\u00e4hle, wie die Audiodaten deines Tracks visualisiert werden sollen.<\/div>\n      \n      <span class=\"pme-lbl\">Form<\/span>\n      <div class=\"shape-pins\" id=\"shape-grid\">\n        <button class=\"shape-btn\" data-shape=\"0\">\n          <div class=\"shape-icon\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#1a1a1a\" stroke-width=\"1.5\"><path d=\"M12 12m-8 0a8 8 0 1 0 16 0a8 8 0 1 0 -16 0 M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0\"\/><\/svg><\/div>\n          <span>Konzentrisch<\/span>\n        <\/button>\n        <button class=\"shape-btn active\" data-shape=\"1\">\n          <div class=\"shape-icon\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#1a1a1a\" stroke-width=\"1.5\"><path d=\"M12 21a9 9 0 0 0 9-9 9 9 0 0 0-9-9 9 9 0 0 0-9 9c0 4 2.5 7 6 8\"\/><\/svg><\/div>\n          <span>Spirale<\/span>\n        <\/button>\n        <button class=\"shape-btn\" data-shape=\"2\">\n          <div class=\"shape-icon\">\n            <svg viewBox=\"0 0 24 24\" stroke=\"#1a1a1a\" stroke-width=\"2\" stroke-linecap=\"round\">\n              <circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"none\"\/>\n              <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\"\/>\n            <\/svg>\n          <\/div>\n          <span>Radial<\/span>\n        <\/button>\n        <button class=\"shape-btn\" data-shape=\"3\">\n          <div class=\"shape-icon\"><svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M2 12 Q 6 2, 12 12 T 22 12 L 22 13 Q 18 23, 12 13 T 2 13 Z\"\/><\/svg><\/div>\n          <span>Schallwelle<\/span>\n        <\/button>\n        <button class=\"shape-btn\" data-shape=\"4\">\n          <div class=\"shape-icon\">\n            <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n              <rect x=\"3\" y=\"10\" width=\"3\" height=\"4\"\/><rect x=\"8\" y=\"6\" width=\"3\" height=\"12\"\/><rect x=\"13\" y=\"4\" width=\"3\" height=\"16\"\/><rect x=\"18\" y=\"8\" width=\"3\" height=\"8\"\/>\n            <\/svg>\n          <\/div>\n          <span>Bl\u00f6cke<\/span>\n        <\/button>\n        <button class=\"shape-btn\" data-shape=\"5\">\n          <div class=\"shape-icon\">\n            <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n              <path d=\"M12 6a6 6 0 1 0 0 12 6 6 0 0 0 0-12zm0 10.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9z\"\/>\n              <path d=\"M12 2v2M12 20v2M4 12H2M22 12h-2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\"\/>\n            <\/svg>\n          <\/div>\n          <span>Ring<\/span>\n        <\/button>\n        <button class=\"shape-btn\" data-shape=\"6\">\n          <div class=\"shape-icon\">\n            <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n              <path d=\"M12 2l2.4 5.6 6.6 1-4.8 4.8 1.1 6.6L12 17.6l-5.3 2.4 1.1-6.6L3 8.6l6.6-1z\" \/>\n              <circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"#fafafa\"\/>\n            <\/svg>\n          <\/div>\n          <span>Nova<\/span>\n        <\/button>\n        <button class=\"shape-btn\" data-shape=\"7\">\n          <div class=\"shape-icon\">\n            <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n              <circle cx=\"12\" cy=\"12\" r=\"10\" \/>\n              <circle cx=\"12\" cy=\"12\" r=\"6\" \/>\n              <circle cx=\"12\" cy=\"12\" r=\"2\" fill=\"currentColor\" \/>\n            <\/svg>\n          <\/div>\n          <span>Vinyl<\/span>\n        <\/button>\n      <\/div>\n\n      <span class=\"pme-lbl\">Farbdesign<\/span>\n      <div id=\"theme-pins\" class=\"theme-pins\"><\/div>\n    <\/div>\n\n    <div class=\"pme-section\" id=\"sec-size\">\n      <div class=\"pme-title\">Gr\u00f6\u00dfe<\/div>\n      <div class=\"pme-desc\">W\u00e4hle Material, Gr\u00f6\u00dfe und Ausrichtung.<\/div>\n      <span class=\"pme-lbl\">Material<\/span>\n      \n      <div id=\"mat-dropdown\">\n        <div class=\"dd-selected\" id=\"mat-selected\"><span id=\"mat-selected-text\">Poster<\/span><svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\"><path d=\"M2 4l4 4 4-4\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\"\/><\/svg><\/div>\n        <div class=\"dd-options\" id=\"mat-options\"><\/div>\n      <\/div>\n      \n      <div id=\"frame-opt-wrap\" style=\"display:none;margin-bottom:14px;\">\n        <span class=\"pme-lbl\">Rahmenfarbe<\/span>\n        <div style=\"display:grid;grid-template-columns:repeat(3,1fr);gap:6px;\">\n          <button class=\"pme-frame active\" data-frame=\"black\">Schwarz<\/button>\n          <button class=\"pme-frame\" data-frame=\"white\">Wei\u00df<\/button>\n          <button class=\"pme-frame\" data-frame=\"wood\">Holz<\/button>\n        <\/div>\n      <\/div>\n\n      <span class=\"pme-lbl\">Gr\u00f6\u00dfe<\/span>\n      <div id=\"size-dropdown\">\n        <div class=\"dd-selected\" id=\"size-selected\"><span id=\"size-selected-text\">30x40cm<\/span><svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\"><path d=\"M2 4l4 4 4-4\" stroke=\"#888\" stroke-width=\"1.5\" stroke-linecap=\"round\"\/><\/svg><\/div>\n        <div class=\"dd-options\" id=\"size-options\"><\/div>\n      <\/div>\n\n      <span class=\"pme-lbl\">Ausrichtung<\/span>\n      <div class=\"pme-orients\">\n        <button class=\"pme-or active\" data-orient=\"portrait\">Hochformat<\/button>\n        <button class=\"pme-or\" data-orient=\"landscape\">Querformat<\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"pme-bar\">\n      <div class=\"pme-prices\">\n        <span class=\"pme-old\" id=\"p-old\"><\/span>\n        <span class=\"pme-new\" id=\"p-new\"><\/span>\n      <\/div>\n      <button class=\"pme-cart\" id=\"btn-cart\">\n        <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"9\" cy=\"21\" r=\"1\"\/><circle cx=\"20\" cy=\"21\" r=\"1\"\/><path d=\"M1 1h4l2.68 13.39a2 2 0 001.99 1.61h9.72a2 2 0 001.99-1.61L23 6H6\"\/><\/svg>\n        In den Warenkorb\n      <\/button>\n    <\/div>\n  <\/div>\n\n  <div class=\"pme-canvas\" id=\"pme-canvas\">\n    <div class=\"pme-outer\" id=\"pme-outer\">\n      <div class=\"pme-poster soundwave-poster\" id=\"pme-poster\" style=\"width:360px;height:504px;\">\n        <div class=\"pme-poster-inner\" id=\"pme-poster-inner\">\n            \n          <div class=\"music-visual\">\n              <canvas id=\"music-layer\"><\/canvas>\n          <\/div>\n          \n          <div class=\"music-footer\" id=\"music-footer\">\n              <div class=\"mf-h1\" id=\"out-h1\">ONE LOVE<\/div>\n              <div class=\"mf-h2\" id=\"out-h2\">BOB MARLEY<\/div>\n              <div class=\"mf-b1\" id=\"out-b1\">EXODUS<\/div>\n              <div class=\"mf-b2\" id=\"out-b2\">1977<\/div>\n          <\/div>\n\n        <\/div>\n      <\/div>\n      <div class=\"pme-dimr\"><span id=\"dim-h\">40 cm<\/span><\/div>\n      <div class=\"pme-dimb\"><span id=\"dim-w\">30 cm<\/span><\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n\nvar RESOLUTION = 3; \n\n\/\/ --- THEMES & CONFIG ---\nvar THEMES = [\n  { id:'Modern',       name:'Modern',       posterBg:'#ffffff', lineCol:'#1a1a1a' },\n  { id:'Black',        name:'Schwarz',      posterBg:'#0a0a0a', lineCol:'#ffffff' },\n  { id:'Night',        name:'Nacht',        posterBg:'#1a2a3a', lineCol:'#e0e0e0' },\n  { id:'Mint',         name:'Mint',         posterBg:'#8ecdb8', lineCol:'#ffffff' },\n  { id:'Sand',         name:'Sand',         posterBg:'#f4efe8', lineCol:'#2c2c2c' },\n  { id:'Pink',         name:'Pink',         posterBg:'#edb4c8', lineCol:'#f7285a' },\n  { id:'Desert',       name:'W\u00fcste',        posterBg:'#e0ceb8', lineCol:'#fcf9e5' },\n  { id:'RetroSunset',  name:'Retro Sunset', posterBg:'#f7bf3d', lineCol:'#f7285a' },\n];\n\n\/\/ GLOBALE PREISE UND MATS LADEN\nvar SIZES = window.UWA_GLOBAL_SIZES;\nvar MATS = ['poster','framed','canvas'];\nvar MAT_LABELS = {poster:'Poster',framed:'Gerahmtes Poster',canvas:'Leinwand'};\n\nvar audioCache = {};\nvar S = { mat: 'poster', sizeIdx: 1, themeId: 'Mint', shape: 1, frame: 'black', orient: 'portrait', seed: 'onelovebobmarley', previewUrl: '' };\n\nfunction getTheme(){ return THEMES.find(function(t){ return t.id===S.themeId; })||THEMES[0]; }\n\n\/\/ --- PROCEDURAL GENERATOR (Fallback) ---\nfunction getSeed(str) {\n    var hash = 0;\n    for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }\n    return hash;\n}\nfunction mulberry32(a) {\n    return function() {\n      var t = a += 0x6D2B79F5;\n      t = Math.imul(t ^ t >>> 15, t | 1);\n      t ^= t + Math.imul(t ^ t >>> 7, t | 61);\n      return ((t ^ t >>> 14) >>> 0) \/ 4294967296;\n    }\n}\nfunction smoothNoise(rands, x) {\n    var i = Math.floor(x);\n    var f = x - i;\n    var r1 = rands[i % rands.length];\n    var r2 = rands[(i+1) % rands.length];\n    var u = f * f * (3.0 - 2.0 * f);\n    return r1 * (1 - u) + r2 * u;\n}\n\nfunction getProceduralData(rands, samples) {\n    var data = [];\n    for (var i = 0; i < samples; i++) {\n        var t_ratio = i \/ samples;\n        var edgeEnv = Math.pow(Math.sin(t_ratio * Math.PI), 0.8); \n        var structure = smoothNoise(rands, t_ratio * 8); \n        var detail = (smoothNoise(rands, t_ratio * 80) * 0.4 + smoothNoise(rands, t_ratio * 200) * 0.6);\n        var val = edgeEnv * (0.2 + structure * 0.8) * detail * 2.5;\n        data.push(Math.min(1, Math.max(0.01, val)));\n    }\n    return data;\n}\n\nfunction drawLoading(ctx, W, H, t) {\n    ctx.fillStyle = t.lineCol;\n    ctx.globalAlpha = 0.5;\n    ctx.font = '24px \"Poppins\"';\n    ctx.textAlign = 'center';\n    ctx.fillText('Generiere Audiodaten...', W\/2, H\/2);\n    ctx.globalAlpha = 1.0;\n}\n\nfunction drawWaveform(ctx, W, H, t, data, shape) {\n    var renderData = data;\n    \n    if (shape === 3 || shape === 4) {\n        \/\/ Take the first half of the array (first 15 seconds)\n        renderData = data.slice(0, Math.floor(data.length \/ 2));\n    }\n    \n    var marginX = 16 * RESOLUTION;\n    var startX = marginX, endX = W - marginX, drawW = endX - startX, cy = H \/ 2;\n    var maxAmp = H * 0.35; \n    \n    \/\/ For circular shapes, calculate max radius fitting into the bounds\n    var cx = W \/ 2;\n    var rCy = H \/ 2; \n    var maxAllowedRadius = Math.min(W \/ 2, H \/ 2) - marginX; \n    \n    if (shape === 3) {\n        \/\/ SOUNDWAVE (Classic Filled, linear)\n        var step = drawW \/ (renderData.length - 1);\n        ctx.fillStyle = t.lineCol;\n        ctx.beginPath();\n        ctx.moveTo(startX, cy);\n        for(var i=0; i<renderData.length; i++) ctx.lineTo(startX + i*step, cy - renderData[i]*maxAmp);\n        for(var i=renderData.length-1; i>=0; i--) ctx.lineTo(startX + i*step, cy + renderData[i]*maxAmp);\n        ctx.closePath();\n        ctx.fill();\n    } \n    else if (shape === 4) {\n        \/\/ BLOCKS (Distinct bars, linear)\n        ctx.fillStyle = t.lineCol;\n        var targetBars = 75; \n        var stepSize = Math.max(1, Math.floor(renderData.length \/ targetBars));\n        var actualBars = Math.floor(renderData.length \/ stepSize);\n        var renderStep = drawW \/ actualBars;\n        var barW = renderStep * 0.8; \n        \n        for(var i=0; i<actualBars; i++) {\n            var maxD = 0;\n            for(var j=0; j<stepSize; j++) {\n                if(renderData[i*stepSize + j] > maxD) maxD = renderData[i*stepSize + j];\n            }\n            var amp = Math.max(H * 0.005, maxD * maxAmp); \n            var x = startX + i * renderStep;\n            ctx.fillRect(x - barW\/2, cy - amp, barW, amp*2);\n        }\n    } \n    else if (shape === 5) {\n        \/\/ RING (Circular Blocks)\n        var minR = Math.min(W, H) * 0.22;\n        var maxRadialAmp = maxAllowedRadius - minR;\n        \n        ctx.fillStyle = t.lineCol;\n        var targetBars = 160; \n        var stepSize = Math.max(1, Math.floor(data.length \/ targetBars));\n        var actualBars = Math.floor(data.length \/ stepSize);\n        var angleStep = (Math.PI * 2) \/ actualBars;\n        var barW = (2 * Math.PI * minR \/ actualBars) * 0.65; \n        \n        for(var i=0; i<actualBars; i++) {\n            var maxD = 0;\n            for(var j=0; j<stepSize; j++) {\n                if(data[i*stepSize + j] > maxD) maxD = data[i*stepSize + j];\n            }\n            var amp = Math.max(H * 0.005, maxD * maxRadialAmp); \n            var a = i * angleStep - Math.PI\/2; \n            \n            ctx.save();\n            ctx.translate(cx, rCy);\n            ctx.rotate(a);\n            ctx.fillRect(minR, -barW\/2, amp, barW);\n            ctx.restore();\n        }\n    }\n    else if (shape === 6) {\n        \/\/ NOVA (Filled Polar Polygon)\n        var minR = Math.min(W, H) * 0.22;\n        var maxRadialAmp = maxAllowedRadius - minR;\n        \n        ctx.fillStyle = t.lineCol;\n        ctx.beginPath();\n        var points = data.length; \n        var angleStep = (Math.PI * 2) \/ points;\n\n        for (var i = 0; i <= points; i++) {\n            var idx = i % points;\n            var a = idx * angleStep - Math.PI\/2;\n            var amp = Math.max(H * 0.005, data[idx] * maxRadialAmp);\n            var r = minR + amp;\n            ctx.lineTo(cx + r * Math.cos(a), rCy + r * Math.sin(a));\n        }\n        for (var i = points; i >= 0; i--) {\n            var idx = i % points;\n            var a = idx * angleStep - Math.PI\/2;\n            ctx.lineTo(cx + minR * Math.cos(a), rCy + minR * Math.sin(a));\n        }\n        ctx.closePath();\n        ctx.fill();\n    }\n    else if (shape === 7) {\n        \/\/ VINYL (Continuous modulating spiral)\n        var vMinR = Math.min(W, H) * 0.08;\n        var vMaxR = maxAllowedRadius;\n        var turns = 20;\n        var points = data.length * 3; \/\/ Smooth high res curve\n        \n        ctx.strokeStyle = t.lineCol;\n        ctx.lineWidth = Math.max(2, W * 0.0015);\n        ctx.lineJoin = 'round';\n        ctx.lineCap = 'round';\n        ctx.beginPath();\n\n        for (var i = 0; i <= points; i++) {\n            var t_ratio = i \/ points;\n            var dataIdx = Math.min(data.length - 1, Math.floor(t_ratio * data.length));\n            var a = t_ratio * Math.PI * 2 * turns - Math.PI\/2;\n            var baseR = vMinR + t_ratio * (vMaxR - vMinR);\n\n            var mod = (data[dataIdx] - 0.5) * (vMaxR * 0.05); \n            var r = baseR + mod;\n            ctx.lineTo(cx + r * Math.cos(a), rCy + r * Math.sin(a));\n        }\n        ctx.stroke();\n\n        \/\/ Inner Label\n        ctx.fillStyle = t.lineCol;\n        ctx.beginPath();\n        ctx.arc(cx, rCy, vMinR * 0.7, 0, Math.PI * 2);\n        ctx.fill();\n    }\n}\n\n\/\/ --- DRAWING ENGINE ---\nfunction generateCanvas() {\n    var canvas = document.getElementById('music-layer');\n    var ctx = canvas.getContext('2d');\n    var isL = S.orient === 'landscape';\n    \n    var vis = document.querySelector('.music-visual');\n    var cw = vis.clientWidth;\n    var ch = vis.clientHeight;\n    \n    if (cw < 50) cw = isL ? 504 : 378;\n    if (ch < 50) ch = isL ? 250 : 380;\n\n    var W = canvas.width = cw * RESOLUTION;\n    var H = canvas.height = ch * RESOLUTION;\n    \n    var t = getTheme();\n    ctx.fillStyle = t.posterBg;\n    ctx.fillRect(0,0,W,H);\n    ctx.strokeStyle = t.lineCol;\n    ctx.lineJoin = 'round';\n    ctx.lineCap = 'round';\n\n    var seedStr = S.seed || \"onelovebobmarley\";\n    var prng = mulberry32(getSeed(seedStr));\n    var rands = []; for(var i=0; i<2000; i++) rands.push(prng());\n\n    if(S.shape === 0) {\n        \/\/ CONCENTRIC\n        var cx = W\/2, cy = H\/2;\n        var maxR = Math.min(W, H)\/2 * 0.85;\n        var circles = 22;\n        ctx.lineWidth = Math.max(3, W * 0.002);\n        for(var c=1; c<=circles; c++) {\n            ctx.beginPath();\n            var baseR = maxR * (c\/circles);\n            for(var a=0; a<=Math.PI*2.01; a+=0.04) {\n                var noiseVal = (smoothNoise(rands, c*30 + a*8) - 0.5) * 60 * (c\/circles);\n                var r = baseR + noiseVal;\n                var x = cx + r * Math.cos(a);\n                var y = cy + r * Math.sin(a);\n                if(a===0) ctx.moveTo(x, y);\n                else ctx.lineTo(x, y);\n            }\n            ctx.closePath();\n            ctx.stroke();\n        }\n    }\n    else if (S.shape === 1) {\n        \/\/ SPIRAL\n        var cx = W\/2, cy = H\/2;\n        var maxR = Math.min(W, H)\/2 * 0.85;\n        var turns = 35;\n        var steps = turns * 80;\n        ctx.lineWidth = Math.max(3, W * 0.002);\n        ctx.beginPath();\n        for(var i=0; i<=steps; i++) {\n            var angle = (i\/80) * Math.PI * 2;\n            var baseR = maxR * (i\/steps);\n            var env = Math.sin(Math.PI * (i\/steps)); \n            var noiseVal = (smoothNoise(rands, i*0.1) - 0.5) * 80 * env;\n            var r = baseR + noiseVal;\n            var x = cx + r * Math.cos(angle);\n            var y = cy + r * Math.sin(angle);\n            if(i===0) ctx.moveTo(x, y);\n            else ctx.lineTo(x, y);\n        }\n        ctx.stroke();\n    }\n    else if (S.shape === 2) {\n        \/\/ RADIAL \/ STARBURST (Single Color)\n        var cx = W\/2, cy = H\/2;\n        var lines = 280;\n        var maxR = Math.min(W, H)\/2 * 0.85;\n        ctx.strokeStyle = t.lineCol; \n        ctx.lineWidth = Math.max(2, W * 0.0015);\n        for(var i=0; i<lines; i++) {\n            var angle = (i\/lines) * Math.PI * 2;\n            var rIdx = Math.floor(rands[i]*1000);\n            var r = maxR * (0.15 + 0.85 * rands[rIdx]); \n            ctx.beginPath();\n            ctx.moveTo(cx + 15*Math.cos(angle), cy + 15*Math.sin(angle));\n            ctx.lineTo(cx + r*Math.cos(angle), cy + r*Math.sin(angle));\n            ctx.stroke();\n        }\n    }\n    else if (S.shape >= 3 && S.shape <= 7) {\n        \/\/ AUDIO SHAPES\n        if (S.previewUrl && audioCache[S.previewUrl] && audioCache[S.previewUrl] !== \"error\" && audioCache[S.previewUrl] !== \"loading\") {\n            drawWaveform(ctx, W, H, t, audioCache[S.previewUrl], S.shape);\n        } \n        else if (S.previewUrl && !audioCache[S.previewUrl]) {\n            audioCache[S.previewUrl] = \"loading\";\n            drawLoading(ctx, W, H, t);\n            \n            fetch(S.previewUrl)\n                .then(function(res) { return res.arrayBuffer(); })\n                .then(function(buf) {\n                    var actx = new (window.AudioContext || window.webkitAudioContext)();\n                    return actx.decodeAudioData(buf);\n                })\n                .then(function(audioBuf) {\n                    var rawDataFull = audioBuf.getChannelData(0);\n                    \n                    \/\/ ALWAYS use the first 15 seconds (50% of the 30s preview) for all audio shapes\n                    var startIdx = 0;\n                    var endIdx = Math.floor(rawDataFull.length * 0.50);\n                    var rawData = rawDataFull.slice(startIdx, endIdx);\n                    \n                    var samples = 400; \/\/ Generate high res cache\n                    var blockSize = Math.floor(rawData.length \/ samples);\n                    var peaks = [];\n                    for (var i = 0; i < samples; i++) {\n                        var peak = 0;\n                        for (var j = 0; j < blockSize; j++) {\n                            var val = Math.abs(rawData[blockSize * i + j]);\n                            if(val > peak) peak = val;\n                        }\n                        peaks.push(Math.max(0.01, peak));\n                    }\n                    \n                    var smoothed = [];\n                    for(var i=0; i<samples; i++) {\n                        var prev = i>0 ? peaks[i-1] : peaks[i];\n                        var next = i<samples-1 ? peaks[i+1] : peaks[i];\n                        smoothed.push((prev + peaks[i]*2 + next) \/ 4);\n                    }\n                    \n                    var maxVal = Math.max.apply(null, smoothed);\n                    if(maxVal === 0) maxVal = 1;\n                    audioCache[S.previewUrl] = smoothed.map(function(n) { return Math.pow(n \/ maxVal, 0.8); });\n                    updatePosterDesign(); \n                })\n                .catch(function(err){\n                    console.error(\"Audio decoding failed, using fallback.\", err);\n                    audioCache[S.previewUrl] = \"error\";\n                    updatePosterDesign();\n                });\n        } \n        else if (S.previewUrl && audioCache[S.previewUrl] === \"loading\") {\n            drawLoading(ctx, W, H, t);\n        } \n        else {\n            var procData = getProceduralData(rands, 400);\n            drawWaveform(ctx, W, H, t, procData, S.shape);\n        }\n    }\n}\n\n\/\/ --- URL PARAMS ---\n(function(){\n  var p = new URLSearchParams(window.location.search);\n  if(p.get('mat')) S.mat = p.get('mat');\n  if(p.get('size')) S.sizeIdx = parseInt(p.get('size'));\n  if(p.get('theme')) S.themeId = p.get('theme');\n  if(p.get('shape')) {\n      var sh = parseInt(p.get('shape'));\n      S.shape = (sh >= 0 && sh <= 7) ? sh : 0; \n  }\n  if(p.get('frame')) S.frame = p.get('frame');\n  if(p.get('orient')) S.orient = p.get('orient');\n  if(p.get('seed')) S.seed = p.get('seed');\n  if(p.get('previewUrl')) S.previewUrl = decodeURIComponent(p.get('previewUrl'));\n  \n  if(p.get('h1')) document.getElementById('inp-h1').value = decodeURIComponent(p.get('h1'));\n  if(p.get('h2')) document.getElementById('inp-h2').value = decodeURIComponent(p.get('h2'));\n  if(p.get('b1')) document.getElementById('inp-b1').value = decodeURIComponent(p.get('b1'));\n  if(p.get('b2')) document.getElementById('inp-b2').value = decodeURIComponent(p.get('b2'));\n})();\n\nfunction doUpdateUrl(){\n  var p = new URLSearchParams();\n  p.set('mat', S.mat); p.set('size', S.sizeIdx); p.set('theme', S.themeId); p.set('shape', S.shape);\n  p.set('orient', S.orient);\n  p.set('seed', S.seed);\n  p.set('previewUrl', encodeURIComponent(S.previewUrl));\n  if(S.mat === 'framed') p.set('frame', S.frame);\n  p.set('h1', encodeURIComponent(document.getElementById('inp-h1').value));\n  p.set('h2', encodeURIComponent(document.getElementById('inp-h2').value));\n  p.set('b1', encodeURIComponent(document.getElementById('inp-b1').value));\n  p.set('b2', encodeURIComponent(document.getElementById('inp-b2').value));\n  history.replaceState(null,'','?'+p.toString());\n}\n\nfunction updatePosterDesign(){\n  var t = getTheme();\n  var inner = document.getElementById('pme-poster-inner');\n  inner.style.backgroundColor = t.posterBg;\n  document.getElementById('music-footer').style.color = t.lineCol;\n  generateCanvas();\n}\n\nfunction updateContent(){\n  document.getElementById('out-h1').textContent = document.getElementById('inp-h1').value.toUpperCase();\n  document.getElementById('out-h2').textContent = document.getElementById('inp-h2').value.toUpperCase();\n  document.getElementById('out-b1').textContent = document.getElementById('inp-b1').value.toUpperCase();\n  document.getElementById('out-b2').textContent = document.getElementById('inp-b2').value.toUpperCase();\n  \n  generateCanvas();\n  doUpdateUrl();\n}\n\n\/\/ LIVE SEARCH SONG\nvar stimer;\ndocument.getElementById('inp-search').addEventListener('input', function(){\n  clearTimeout(stimer);\n  if(this.value.length < 2) { \n      document.getElementById('song-results').classList.remove('show'); \n      return; \n  }\n  stimer = setTimeout(doSongSearch, 500);\n});\n\nfunction doSongSearch(){\n  var q = document.getElementById('inp-search').value.trim();\n  if(!q) return;\n  var box = document.getElementById('song-results');\n  fetch('https:\/\/itunes.apple.com\/search?term='+encodeURIComponent(q)+'&entity=song&limit=5')\n    .then(function(r){ return r.json(); })\n    .then(function(d){\n      if(!d.results||!d.results.length){ box.innerHTML='<div class=\"pme-ri\">Keine Ergebnisse<\/div>'; box.classList.add('show'); return; }\n      box.innerHTML = d.results.map(function(t){\n        var name=(t.trackName||q).replace(\/\"\/g,'');\n        var artist=(t.artistName||'').replace(\/\"\/g,'');\n        var album=(t.collectionName||name).replace(\/\"\/g,'');\n        var year=t.releaseDate ? t.releaseDate.substring(0,4) : '';\n        var preview=t.previewUrl || '';\n        return '<div class=\"pme-ri\" data-name=\"'+name+'\" data-artist=\"'+artist+'\" data-album=\"'+album+'\" data-year=\"'+year+'\" data-preview=\"'+preview+'\"><strong>'+name+'<\/strong><span>'+artist+'<\/span><\/div>';\n      }).join('');\n      box.classList.add('show');\n      \n      box.querySelectorAll('.pme-ri').forEach(function(item){\n        item.addEventListener('click', function(){\n          var name=this.getAttribute('data-name');\n          var artist=this.getAttribute('data-artist');\n          var album=this.getAttribute('data-album');\n          var year=this.getAttribute('data-year');\n          var preview=this.getAttribute('data-preview');\n          \n          document.getElementById('inp-search').value=name + ' - ' + artist;\n          document.getElementById('inp-h1').value=name;\n          document.getElementById('inp-h2').value=artist;\n          document.getElementById('inp-b1').value=album;\n          document.getElementById('inp-b2').value=year;\n          \n          S.seed = (name + artist).toLowerCase().replace(\/\\s\/g,'');\n          S.previewUrl = preview;\n          \n          box.classList.remove('show');\n          updateContent();\n        });\n      });\n    }).catch(function(){});\n}\n\ndocument.addEventListener('click', function(e){\n  if(!e.target.closest('#inp-search')&&!e.target.closest('#song-results')){\n      var box = document.getElementById('song-results');\n      if(box) box.classList.remove('show');\n  }\n});\n\n\/\/ POPULAR SONGS CLICK\ndocument.querySelectorAll('.pme-pop li').forEach(function(li){\n    li.addEventListener('click', function(){\n        var name = this.getAttribute('data-name');\n        var artist = this.getAttribute('data-artist');\n        var album = this.getAttribute('data-album');\n        var year = this.getAttribute('data-year');\n        \n        document.getElementById('inp-search').value = name + ' - ' + artist;\n        document.getElementById('inp-h1').value = name;\n        document.getElementById('inp-h2').value = artist;\n        document.getElementById('inp-b1').value = album;\n        document.getElementById('inp-b2').value = year;\n        \n        S.seed = (name + artist).toLowerCase().replace(\/\\s\/g,'');\n        S.previewUrl = ''; \n        updateContent();\n        \n        fetch('https:\/\/itunes.apple.com\/search?term='+encodeURIComponent(name+' '+artist)+'&entity=song&limit=1')\n        .then(function(r){ return r.json(); })\n        .then(function(d){\n            if(d.results && d.results.length > 0) {\n                S.previewUrl = d.results[0].previewUrl || '';\n                updateContent();\n            }\n        }).catch(function(){});\n    });\n});\n\n['inp-h1', 'inp-h2', 'inp-b1', 'inp-b2'].forEach(function(id){\n    document.getElementById(id).addEventListener('input', updateContent);\n});\n\n\/\/ --- PINS (Theme & Shape) ---\nfunction renderThemePins(){\n  var c = document.getElementById('theme-pins');\n  c.innerHTML = THEMES.map(function(t){\n    var active = t.id===S.themeId?' active':'';\n    return '<button class=\"pme-thm'+active+'\" data-id=\"'+t.id+'\">'\n      +'<div class=\"pme-thm-circle\" style=\"background-color:'+t.posterBg+'; border-color: '+ (t.posterBg==='#ffffff' ? '#e0e0e0' : t.posterBg) +'; box-shadow: inset 0 0 0 2px '+t.lineCol+';\"><\/div>'\n      +'<span>'+t.name+'<\/span>'\n      +'<\/button>';\n  }).join('');\n  \n  c.querySelectorAll('.pme-thm').forEach(function(pin){\n    pin.addEventListener('click', function(){\n      c.querySelectorAll('.pme-thm').forEach(function(p){ p.classList.remove('active'); });\n      this.classList.add('active');\n      S.themeId = this.getAttribute('data-id');\n      updatePosterDesign(); doUpdateUrl();\n    });\n  });\n}\n\ndocument.querySelectorAll('.shape-btn').forEach(function(btn) {\n    btn.addEventListener('click', function(){\n        document.querySelectorAll('.shape-btn').forEach(function(b){ b.classList.remove('active'); });\n        this.classList.add('active');\n        S.shape = parseInt(this.getAttribute('data-shape'));\n        updatePosterDesign(); doUpdateUrl();\n    });\n});\n\n\/\/ Sync shape initial state\ndocument.querySelectorAll('.shape-btn').forEach(function(b){ b.classList.remove('active'); });\nvar actBtn = document.querySelector('.shape-btn[data-shape=\"'+S.shape+'\"]');\nif(actBtn) actBtn.classList.add('active');\n\n\/\/ --- NAV ---\ndocument.querySelectorAll('.pme-nav-btn, .pme-mobbtn').forEach(function(btn){\n  btn.addEventListener('click', function(){\n    var panel = this.getAttribute('data-panel');\n    document.querySelectorAll('.pme-section').forEach(function(s){ s.classList.remove('active'); });\n    document.querySelectorAll('.pme-nav-btn,.pme-mobbtn').forEach(function(b){ b.classList.remove('active'); });\n    document.getElementById('sec-'+panel).classList.add('active');\n    document.querySelectorAll('[data-panel=\"'+panel+'\"]').forEach(function(b){ b.classList.add('active'); });\n  });\n});\n\ndocument.querySelectorAll('.pme-frame').forEach(function(btn) {\n  btn.addEventListener('click', function() {\n    document.querySelectorAll('.pme-frame').forEach(function(b) { b.classList.remove('active'); });\n    this.classList.add('active');\n    S.frame = this.getAttribute('data-frame');\n    updateDims();\n    doUpdateUrl();\n  });\n});\n\ndocument.querySelectorAll('.pme-or').forEach(function(btn) {\n  btn.addEventListener('click', function() {\n    document.querySelectorAll('.pme-or').forEach(function(b) { b.classList.remove('active'); });\n    this.classList.add('active');\n    S.orient = this.getAttribute('data-orient');\n    updateDims(); doUpdateUrl();\n  });\n});\n\nif (S.orient) {\n  document.querySelectorAll('.pme-or').forEach(function(btn) {\n    btn.classList.toggle('active', btn.getAttribute('data-orient') === S.orient);\n  });\n}\n\n\/\/ --- SIZES AND MATS ---\nfunction renderMaterials(){\n  var optsDiv = document.getElementById('mat-options');\n  var selText = document.getElementById('mat-selected-text');\n  var selDiv  = document.getElementById('mat-selected');\n  function selectMat(mat){\n    S.mat=mat;\n    var sizes = SIZES[S.mat] || [];\n    S.sizeIdx = 0;\n    for (var i = 0; i < sizes.length; i++) {\n      if (sizes[i].l.includes('30x40cm')) { S.sizeIdx = i; break; }\n    }\n    selText.textContent = MAT_LABELS[mat];\n    optsDiv.style.display='none';\n    renderSizes(); doUpdateUrl();\n  }\n  optsDiv.innerHTML = MATS.map(function(m){\n    return '<div class=\"size-opt\" data-mat=\"'+m+'\" style=\"padding:10px 12px;font-size:14px;font-family:\\'Poppins\\',sans-serif;cursor:pointer;border-bottom:1px solid #f0f0f0;\">'+MAT_LABELS[m]+'<\/div>';\n  }).join('');\n  optsDiv.querySelectorAll('.size-opt').forEach(function(el){\n    el.addEventListener('mouseenter', function() { this.style.background='#f5f5f5'; });\n    el.addEventListener('mouseleave', function() { this.style.background=''; });\n    el.addEventListener('click',function(){ selectMat(this.getAttribute('data-mat')); });\n  });\n  selDiv.onclick = function(e){ e.stopPropagation(); optsDiv.style.display=optsDiv.style.display==='none'?'block':'none'; };\n  document.addEventListener('click',function(){ optsDiv.style.display='none'; });\n  selText.textContent = MAT_LABELS[S.mat];\n}\n\nfunction buildLabel(s){\n  return s.l+'\\u00a0\\u00a0<s style=\"color:#999\">'+s.orig.toFixed(2)+'\\u00a0\\u20ac<\/s>\\u00a0\\u00a0<span style=\"color:#e00\">'+s.p.toFixed(2)+'\\u00a0\\u20ac<\/span>';\n}\n\nfunction renderSizes(){\n  var sizes=SIZES[S.mat]||[];\n  var optsDiv=document.getElementById('size-options');\n  var selText=document.getElementById('size-selected-text');\n  var selDiv=document.getElementById('size-selected');\n  var frameWrap = document.getElementById('frame-opt-wrap');\n  \n  if (frameWrap) frameWrap.style.display = (S.mat === 'framed') ? 'block' : 'none';\n  \n  function selectIdx(i){\n    S.sizeIdx=i; selText.innerHTML=buildLabel(sizes[i]);\n    optsDiv.style.display='none';\n    updatePrice(); updateDims(); doUpdateUrl();\n  }\n  optsDiv.innerHTML=sizes.map(function(s,i){\n    return '<div class=\"size-opt\" data-idx=\"'+i+'\" style=\"padding:10px 12px;font-size:14px;font-family:\\'Poppins\\',sans-serif;cursor:pointer;border-bottom:1px solid #f0f0f0;\">'+buildLabel(s)+'<\/div>';\n  }).join('');\n  optsDiv.querySelectorAll('.size-opt').forEach(function(el){\n    el.addEventListener('mouseenter', function() { this.style.background='#f5f5f5'; });\n    el.addEventListener('mouseleave', function() { this.style.background=''; });\n    el.addEventListener('click',function(){ selectIdx(parseInt(this.getAttribute('data-idx'))); });\n  });\n  selDiv.onclick=function(e){ e.stopPropagation(); optsDiv.style.display=optsDiv.style.display==='none'?'block':'none'; };\n  document.addEventListener('click',function(){ optsDiv.style.display='none'; });\n  selectIdx(S.sizeIdx<sizes.length?S.sizeIdx:0);\n}\n\nfunction updatePrice(){\n  var s=(SIZES[S.mat]||[])[S.sizeIdx];\n  if(!s) return;\n  document.getElementById('p-old').textContent=s.orig.toFixed(2).replace('.',',')+' EUR';\n  document.getElementById('p-new').textContent=s.p.toFixed(2).replace('.',',')+' EUR';\n}\n\nfunction updateDims(){\n  var s=(SIZES[S.mat]||[])[S.sizeIdx];\n  if(!s) return;\n  \n  var isL = S.orient === 'landscape';\n  document.getElementById('dim-w').textContent = (isL ? s.h : s.w) + ' cm';\n  document.getElementById('dim-h').textContent = (isL ? s.w : s.h) + ' cm';\n  \n  var BASE = 504;\n  var pw = isL ? s.h : s.w;\n  var ph = isL ? s.w : s.h;\n  var longer = Math.max(pw,ph), shorter = Math.min(pw,ph);\n  \n  var baseW = isL ? BASE : Math.round(BASE * shorter \/ longer);\n  var baseH = isL ? Math.round(BASE * shorter \/ longer) : BASE;\n  var poster = document.getElementById('pme-poster');\n\n  if (isL) {\n    poster.style.width = BASE + 'px';\n    poster.style.height = Math.round(BASE * shorter \/ longer) + 'px';\n  } else {\n    poster.style.width = Math.round(BASE * shorter \/ longer) + 'px';\n    poster.style.height = BASE + 'px';\n  }\n\n  if (S.mat === 'framed') {\n    var thicknessMm = (s.l.includes('13x18cm')) ? 10 : 14;\n    var framePx = thicknessMm * (BASE \/ (longer * 10));\n\n    poster.style.borderWidth = framePx + 'px';\n    poster.style.borderStyle = 'solid';\n\n    if (S.frame === 'wood') {\n      poster.style.borderColor = '#d7c5b3';\n      poster.style.borderImage = \"url(\\\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.01 0.4' numOctaves='2' result='noise'\/%3E%3CfeColorMatrix type='matrix' values='1 0 0 0 0 0 0.8 0 0 0 0 0.5 0 0 0 0 0 0 0.15 0' in='noise'\/%3E%3C\/filter%3E%3Crect width='100%25' height='100%25' fill='%23d7c5b3'\/%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'\/%3E%3C\/svg%3E\\\") 10 stretch\";\n    } else if (S.frame === 'white') {\n      poster.style.borderImage = \"\";\n      poster.style.borderColor = '#ffffff';\n    } else { \/\/ black\n      poster.style.borderImage = \"\";\n      poster.style.borderColor = '#1a1a1a';\n    }\n    \n    poster.style.width = (baseW + framePx * 2) + 'px';\n    poster.style.height = (baseH + framePx * 2) + 'px';\n    poster.classList.add('is-framed');\n  } else {\n    poster.style.border = '';\n    poster.style.borderImage = '';\n    poster.style.width = baseW + 'px';\n    poster.style.height = baseH + 'px';\n    poster.classList.remove('is-framed');\n  }\n\n  setTimeout(function(){ fitPoster(); updatePosterDesign(); }, 60);\n}\n\nfunction fitPoster(){\n  var outer=document.getElementById('pme-outer');\n  var poster=document.getElementById('pme-poster');\n  var container = document.getElementById('pme-canvas');\n  if(!container||!poster.offsetWidth) return;\n  \n  var isMob = window.innerWidth <= 768;\n  \n  \/\/ Padding exakt auf 50\/50 f\u00fcr Mobile gesetzt\n  var aH = container.clientHeight - (isMob ? 50 : 60);\n  var aW = container.clientWidth - (isMob ? 50 : 80);\n  \n  var scale = Math.min(aH \/ poster.offsetHeight, aW \/ poster.offsetWidth, isMob ? 2.5 : 1.4);\n  outer.style.transform='scale('+scale+')';\n  outer.style.transformOrigin='center center';\n}\n\nfunction setHeight(){\n  var el=document.getElementById('pme-root');\n  if(!el) return;\n  var isMob = window.innerWidth <= 768;\n  if (isMob) {\n    el.style.height = 'auto'; \n    if(document.querySelector('.pme-panel')) document.querySelector('.pme-panel').style.height = '';\n    if(document.querySelector('.pme-nav')) document.querySelector('.pme-nav').style.height = '';\n    if(document.getElementById('pme-canvas')) document.getElementById('pme-canvas').style.height = '';\n  } else {\n    var r=el.getBoundingClientRect();\n    var h=Math.max(window.innerHeight-r.top,500);\n    el.style.height=h+'px';\n    if(document.querySelector('.pme-panel')) document.querySelector('.pme-panel').style.height=h+'px';\n    if(document.querySelector('.pme-nav')) document.querySelector('.pme-nav').style.height=h+'px';\n    if(document.getElementById('pme-canvas')) document.getElementById('pme-canvas').style.height=h+'px';\n  }\n  setTimeout(function(){ fitPoster(); },100);\n}\n\nwindow.addEventListener('load',setHeight);\nwindow.addEventListener('resize',function(){ setHeight(); });\nsetTimeout(setHeight,300);\n\n\/\/ --- CART EXPORT ---\ndocument.getElementById('btn-cart').addEventListener('click', function() {\n  var btn = this;\n  var origHTML = btn.innerHTML; \n  btn.innerHTML = 'L\u00e4dt...';\n  btn.style.pointerEvents = 'none';\n\n  var s = (SIZES[S.mat] || [])[S.sizeIdx] || {};\n  \n  var cartData = {\n    action: 'uwa_add_to_cart',\n    product_id: '1527', \n    poster_title: document.getElementById('out-h1').textContent,\n    poster_subtitle: document.getElementById('out-h2').textContent,\n    poster_coordinates: document.getElementById('out-b1').textContent + ' ' + document.getElementById('out-b2').textContent,\n    poster_lat: '',\n    poster_lng: '',\n    poster_zoom: '',\n    poster_style: S.themeId,\n    poster_shape: S.shape,\n    poster_material: S.mat,\n    poster_frame: (S.mat === 'framed') ? S.frame : '',\n    poster_size: s.l || '',\n    poster_orientation: S.orient,\n    poster_price: s.p || '',\n    poster_original_price: s.orig || '',\n    poster_url: window.location.href\n  };\n\n  function buildThumbnail() {\n    try {\n      var musicCanvas = document.getElementById('music-layer');\n      var posterEl = document.getElementById('pme-poster');\n      var posterW = posterEl.offsetWidth;\n      var posterH = posterEl.offsetHeight;\n\n      var out = document.createElement('canvas');\n      out.width = posterW;\n      out.height = posterH;\n      var ctx = out.getContext('2d');\n\n      ctx.fillStyle = '#ffffff';\n      ctx.fillRect(0, 0, posterW, posterH);\n\n      var pad = 16;\n      var innerW = posterW - pad * 2;\n      var innerH = posterH - pad * 2;\n\n      var t = getTheme();\n      ctx.fillStyle = t.posterBg;\n      ctx.fillRect(pad, pad, innerW, innerH);\n\n      var visEl = document.querySelector('.music-visual');\n      var visH = visEl.offsetHeight;\n      \n      if (musicCanvas && musicCanvas.width > 0) {\n        ctx.drawImage(musicCanvas, \n          0, 0, musicCanvas.width, musicCanvas.height,\n          pad, pad + 10, innerW, visH\n        );\n      }\n\n      var h1 = document.getElementById('out-h1').textContent || '';\n      var h2 = document.getElementById('out-h2').textContent || '';\n      var b1 = document.getElementById('out-b1').textContent || '';\n      var b2 = document.getElementById('out-b2').textContent || '';\n\n      var footerY = pad + 10 + visH;\n\n      ctx.textAlign = 'center';\n      ctx.fillStyle = t.lineCol;\n\n      ctx.font = 'bold 14px Rubik, sans-serif';\n      ctx.fillText(h1.toUpperCase(), posterW\/2, footerY + 28);\n\n      ctx.font = 'bold 8px Rubik, sans-serif';\n      ctx.globalAlpha = 0.9;\n      ctx.fillText(h2.toUpperCase(), posterW\/2, footerY + 45);\n\n      ctx.font = 'bold 7px Rubik, sans-serif';\n      ctx.fillText(b1.toUpperCase(), posterW\/2, footerY + 75);\n\n      ctx.font = 'bold 8px Rubik, sans-serif';\n      ctx.globalAlpha = 1.0;\n      ctx.fillText(b2.toUpperCase(), posterW\/2, footerY + 87);\n\n      return out.toDataURL('image\/jpeg', 0.92);\n    } catch(e) {\n      console.error(e);\n      return null;\n    }\n  }\n\n  cartData.poster_thumbnail = buildThumbnail() || '';\n\n  var body = Object.keys(cartData).map(function(k) {\n    return encodeURIComponent(k) + '=' + encodeURIComponent(cartData[k]);\n  }).join('&');\n\n  fetch('https:\/\/zulumaps.com\/wp-admin\/admin-ajax.php', {\n    method: 'POST',\n    headers: {'Content-Type': 'application\/x-www-form-urlencoded'},\n    body: body\n  }).then(function(r) { return r.json(); })\n  .then(function(res) {\n    if (res.success) {\n      window.location.href = 'https:\/\/zulumaps.com\/cart\/';\n    } else {\n      console.error(\"Cart Error:\", res);\n      alert(\"Fehler beim Hinzuf\u00fcgen zum Warenkorb.\");\n      btn.innerHTML = origHTML;\n      btn.style.pointerEvents = 'auto';\n    }\n  }).catch(function(err) {\n    console.error(\"Fetch Error:\", err);\n    alert(\"Es gab einen Verbindungsfehler.\");\n    btn.innerHTML = origHTML;\n    btn.style.pointerEvents = 'auto';\n  });\n});\n\n\/\/ INIT CALL\nfetch('https:\/\/itunes.apple.com\/search?term=One+Love+Bob+Marley&entity=song&limit=1')\n.then(function(r){ return r.json(); })\n.then(function(d){\n    if(d.results && d.results.length > 0) {\n        S.previewUrl = d.results[0].previewUrl || '';\n        updatePosterDesign();\n    }\n}).catch(function(){});\n\nrenderThemePins();\nrenderMaterials();\nrenderSizes();\nupdateContent();\nupdatePosterDesign();\n\nif (S.frame) {\n  document.querySelectorAll('.pme-frame').forEach(function(btn) {\n    btn.classList.toggle('active', btn.getAttribute('data-frame') === S.frame);\n  });\n}\nif (S.mat === 'framed') {\n  document.getElementById('pme-poster').classList.add('is-framed');\n}\n\n})();\n<\/script><\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2191","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/pages\/2191","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/comments?post=2191"}],"version-history":[{"count":10,"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/pages\/2191\/revisions"}],"predecessor-version":[{"id":3068,"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/pages\/2191\/revisions\/3068"}],"wp:attachment":[{"href":"https:\/\/zulumaps.com\/de\/wp-json\/wp\/v2\/media?parent=2191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}