{"id":18223,"date":"2020-03-12T05:58:44","date_gmt":"2020-03-12T05:58:44","guid":{"rendered":"https:\/\/radiojingles24.com\/?post_type=product&#038;p=18223"},"modified":"2026-06-01T14:33:03","modified_gmt":"2026-06-01T14:33:03","slug":"sung-audio","status":"publish","type":"product","link":"https:\/\/radiojingles24.com\/it\/prodotto\/sung-audio\/","title":{"rendered":"Jingles cantati"},"content":{"rendered":"\n\t\t<div id=\"fws_6a219f16e556b\"  data-midnight=\"dark\"  data-bg-mobile-hidden=\"\" class=\"wpb_row vc_row-fluid vc_row top-level standard_section   \"  style=\"padding-top: 0px; padding-bottom: 0px; \"><div class=\"row-bg-wrap\" data-bg-animation=\"none\"><div class=\"inner-wrap \"><div class=\"row-bg    \"  style=\"\"><\/div><\/div><div class=\"row-bg-overlay\" ><\/div><\/div><div class=\"col span_12 dark left\">\n\t<div  class=\"vc_col-sm-12 wpb_column column_container vc_column_container col no-extra-padding\"  data-t-w-inherits=\"default\" data-border-radius=\"none\" data-shadow=\"none\" data-border-animation=\"\" data-border-animation-delay=\"\" data-border-width=\"none\" data-border-style=\"solid\" data-border-color=\"\" data-bg-cover=\"\" data-padding-pos=\"all\" data-has-bg-color=\"false\" data-bg-color=\"\" data-bg-opacity=\"1\" data-hover-bg=\"\" data-hover-bg-opacity=\"1\" data-animation=\"\" data-delay=\"0\"><div class=\"column-bg-overlay\"><\/div>\n\t\t<div class=\"vc_column-inner\">\n\t\t\t<div class=\"wpb_wrapper\">\n\t\t\t\t\n\t<div class=\"wpb_raw_code wpb_content_element wpb_raw_html\" >\n\t\t<div class=\"wpb_wrapper\">\n\t\t\t<!-- ==========================================================\n     RADIO JINGLES 24 \u2014 SUNG WIZARD v3.9.6 (multi-jingle via RJ24 plugin AJAX)\n\n     v3.9.6 changes (June 2026):\n       - Real fix for footer-lift not firing. The handler used to run\n         immediately at script parse time, but the wizard's inline script\n         is in the middle of the document \u2014 the WP footer (.rj24-foot)\n         comes AFTER it in source order. So document.querySelector for\n         the footer returned null, the function early-exited, and no\n         scroll listener was ever attached. Result: bar overlapped footer.\n       - Now defers attachment via DOMContentLoaded + window.load + a\n         polling retry, so the handler always finds the footer eventually.\n\n     v3.9.5 changes\n\n     v3.9.5 changes (June 2026):\n       - REAL fix for \"sticky bar disappears under footer\". The bar was\n         being trapped inside .rjsw-screen because the rjswFade animation\n         used transform:translateY(8px) \u2014 any ancestor transform breaks\n         position:fixed (the containing block becomes the transformed\n         ancestor instead of the viewport). The bar was being positioned\n         relative to the tall screen container, putting it off-screen.\n       - rjswFade keyframe: removed translateY, opacity-only fade now.\n       - footer-lift handler: clamps the lift so the bar always stays\n         within the viewport (\"remain at all times\"). When you scroll deep\n         into the footer, the bar pins to the top of the viewport rather\n         than disappearing upward.\n\n     v3.9.4 changes\n\n     v3.9.4 changes (June 2026):\n       - Sticky buy bar is now footer-aware: lifts up smoothly when the\n         page footer enters the bottom of the viewport, instead of\n         floating over the footer content. Uses a passive scroll listener\n         (cheap, no layout thrash) + transform: translateY for GPU compositing.\n\n     v3.9.3 changes\n\n     v3.9.3 changes (June 2026):\n       - Removed -\u00a310 discount from all Kiss beds (Kiss now priced same as\n         every other bed: standard bundle pricing). The discount property has\n         been deleted from each Kiss entry, so the 'SAVE \u00a310' badge will no\n         longer appear on Kiss bed cards in the wizard.\n\n     v3.9.2 changes\n\n     v3.9.2 changes (June 2026):\n       - Added Epic 6, 7, 8 beds\n       - Added Kiss 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20 (each -\u00a310 discount)\n       - Fixed sticky buy bar: was position:sticky;top:0 (collapsing to 0 height)\n         now position:fixed;bottom:0 matching \/product\/commercial-production\/\n       - .rjsw-screen gets padding-bottom:96px so content doesn't sit under bar\n       - Hides sticky bars from inactive screens\n       - Kept Sky & Virgin references per business decision\n\n     ARCHITECTURE:\n       Earlier approaches drove one hidden TMEPO product repeatedly to\n       create multiple cart lines. Every transport (native redirect,\n       wc-ajax, iframe) failed because that fights WooCommerce structurally.\n\n       v3.9.0 uses a proper foundation: a companion plugin (RJ24 Sung\n       Jingles) registers a custom AJAX action `rj24_add_jingle` and a\n       simple \"Sung Jingle\" product (\u00a369, ID 116709). The plugin exposes\n       window.RJ24_CFG = { ajaxUrl, nonce, productId, cartUrl }.\n\n       Flow: pick beds (multi) -> singer -> per-jingle lyrics ->\n       format\/extras -> review -> Add to cart. Each jingle is POSTed\n       individually to rj24_add_jingle (one call = one cart line, with\n       full detail attached as cart_item_data). The loop verifies the\n       returned cart_count grows after each add, then redirects to cart.\n\n       Pricing: bundle by jingle count (1=\u00a369, 2=\u00a3108, 3=\u00a3147, 4=\u00a3199, then \u00a349 each),\n       ENFORCED SERVER-SIDE by the plugin across the whole cart, with\n       singer (+\u00a325) and extras (+\u00a315) premiums on top, and Kiss bed\n       discounts (-\u00a310). The wizard shows estimates that match the plugin.\n       Full Station Package (\u00a3199) is one line item.\n\n       No TMEPO, no wc-ajax, no iframe, no native-redirect. The reliable\n       plugin endpoint is the only cart path.\n\n     NOTE: fillTMEPO\/resetTMEPO remain defined but UNUSED (legacy, harmless).\n     ========================================================== -->\n\n<style>\n\/* ===== KILL ALL REMAINING WOOCOMMERCE CHROME =====\n   NOTE: We do NOT hide .woocommerce-tabs or #tab-description because\n   the sung wizard's Raw HTML block lives INSIDE the description tab\n   on the sung product page. Hiding those would hide the wizard. *\/\n.single-product .product-images,\n.single-product .images,\n.single-product .woocommerce-product-gallery,\n.single-product div.product .images,\n.single-product .product .product_meta,\n.single-product .product-category,\n.single-product .product-on-sale,\n.single-product .woocommerce-product-details__short-description {\n  display: none !important;\n}\n\n\/* Force the description tab to be visible (since our wizard lives in it) *\/\nhtml body .single-product #tab-description,\nhtml body .single-product .panel.entry-content,\nhtml body .single-product .woocommerce-tabs,\nhtml body .single-product .wc-tabs-wrapper {\n  display: block !important;\n  visibility: visible !important;\n  opacity: 1 !important;\n  width: 100% !important;\n  height: auto !important;\n  min-height: 0 !important;\n  max-height: none !important;\n  overflow: visible !important;\n  position: static !important;\n}\n\n\/* Hide the tab navigation row so users don't see the \"Description\" tab label *\/\nhtml body .single-product .wc-tabs,\nhtml body .single-product ul.tabs,\nhtml body .single-product .tabs.wc-tabs,\nhtml body .single-product .qode-shortcodes-product-tabs > ul,\nhtml body .single-product .product-tabs > .tabs {\n  display: none !important;\n}\n\n.single-product .product .row .col_one_third,\n.single-product .product .row .col_two_third {\n  width: 100% !important;\n  max-width: 100% !important;\n  flex: 1 1 100% !important;\n}\n\n\/* Hide PayPal\/share floating widgets *\/\n.single-product .ppcp-button-container,\n.single-product .ppc-button-wrapper,\n.single-product .paypal-button-container,\n.single-product [class*=\"ppcp-\"],\n.single-product [id*=\"paypal-button\"],\n.single-product .a2a_kit,\n.single-product .a2a_floating_style,\n.single-product .addtoany_share_save_container,\n.single-product .share-buttons,\n.single-product .social-share-floating,\n.single-product div[class*=\"share\"][class*=\"float\"],\n.single-product div[class*=\"float\"][class*=\"share\"] {\n  display: none !important;\n  visibility: hidden !important;\n  opacity: 0 !important;\n  pointer-events: none !important;\n}\n\n\/* Hide TMEPO (EXACT match to spoken's working CSS) *\/\n.single-product form.cart .tm-extra-product-options,\n.single-product form.cart .tm-extra-product-options-fields,\n.single-product form.cart .tc-extra-product-options,\n.single-product form.cart > .quantity,\n.single-product form.cart > .single_add_to_cart_button,\n.single-product form.cart > button[name=\"add-to-cart\"] {\n  position: absolute !important;\n  left: -99999px !important;\n  top: -99999px !important;\n  width: 1px !important;\n  height: 1px !important;\n  overflow: hidden !important;\n  opacity: 0 !important;\n  pointer-events: none !important;\n}\n\n\/* CRITICAL: TMEPO containers default to display:none until the description tab\n   becomes active. Since our wizard lives in the description tab (force-shown\n   above), we must ALSO force the TMEPO containers themselves to be visible\n   in the DOM so our JS can find inputs inside them.\n   We use 'block' (not 'none') so getComputedStyle reports them as renderable\n   even though they're positioned offscreen. *\/\nhtml body .single-product #tm-extra-product-options,\nhtml body .single-product #tm-extra-product-options-fields,\nhtml body .single-product .tm-extra-product-options,\nhtml body .single-product .tm-extra-product-options-fields,\nhtml body .single-product .tc-extra-product-options,\nhtml body .single-product .tc-extra-product-options-inner {\n  display: block !important;\n  visibility: visible !important;\n}\n\n\/* Force form.cart visible too, in case the theme hides it inside description tab *\/\nhtml body .single-product form.cart,\nhtml body .single-product form.cart.myadc {\n  display: block !important;\n  visibility: visible !important;\n}\n\n.single-product form.cart {\n  position: static !important;\n  width: 100% !important;\n  height: auto !important;\n  max-height: none !important;\n  overflow: visible !important;\n  opacity: 1 !important;\n  padding: 0 !important;\n  background: transparent !important;\n  box-shadow: none !important;\n  display: block !important;\n  margin: 0 !important;\n}\n\n.single-product .product_title {\n  display: none !important;\n}\n\n\/* ===== WIZARD STYLES ===== *\/\n\/* Force the wizard container to have proper dimensions regardless of\n   parent WPBakery\/Bridge theme wrapping. Some Bridge wrappers render\n   the Raw HTML block inside a flex\/grid that collapses to 0 width. *\/\nhtml body #rj-sung-wizard.rjsw {\n  display: block !important;\n  width: 100% !important;\n  min-width: 100% !important;\n  min-height: 600px !important;\n  position: relative !important;\n  clear: both !important;\n  float: none !important;\n  visibility: visible !important;\n  opacity: 1 !important;\n}\n\n.rjsw {\n  --rj-pink: #EC4899;\n  --rj-pink-dark: #DB2777;\n  --rj-pink-light: #FCE7F3;\n  --rj-pink-lighter: #FDF2F8;\n  --rj-purple: #8B5CF6;\n  --rj-gold: #F59E0B;\n  --rj-gold-light: #FEF3C7;\n  --rj-ink: #1E293B;\n  --rj-text: #475569;\n  --rj-muted: #94A3B8;\n  --rj-line: #E2E8F0;\n  --rj-bg: #FAFAFB;\n  --rj-card: #FFFFFF;\n  --rj-green: #10B981;\n  --rj-green-bg: #D1FAE5;\n  --rj-red: #DC2626;\n  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n  color: var(--rj-ink);\n  max-width: 1080px;\n  margin: 0 auto;\n  padding: 0 16px 40px;\n  -webkit-font-smoothing: antialiased;\n  line-height: 1.5;\n  \/* RJ24-EDIT (Nov 2026): sung-audio page has ~75px of unexplained gap above\n     the wizard from theme chrome. Pull the wizard up tight against the nav. *\/\n  --rjsw-headpad: 0px; padding-top: var(--rjsw-headpad);\n  margin-top: -60px;\n}\n@media (max-width: 1000px) { .rjsw { --rjsw-headpad: 0px; margin-top: -50px; } }\n@media (max-width: 600px) { .rjsw { --rjsw-headpad: 0px; margin-top: -40px; } }\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Anton&display=swap');\n.rjsw * { box-sizing: border-box; }\n\n\/* HERO *\/\n.rjsw-hero{ border-radius:24px; overflow:hidden; background-color:#160F1F;\n  background-image:radial-gradient(120% 140% at 12% 18%, rgba(236,72,153,0.55) 0%, rgba(236,72,153,0) 55%),radial-gradient(120% 140% at 92% 10%, rgba(245,158,11,0.30) 0%, rgba(245,158,11,0) 50%),linear-gradient(135deg,#241733 0%,#0F0A18 100%);\n  padding:46px 32px; text-align:center; margin:6px 0 22px; position:relative; isolation:isolate; }\n.rjsw-hero::after{ content:\"\"; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,0.05) 1px,transparent 1px); background-size:22px 22px; opacity:.5; z-index:-1; }\n.rjsw-hero-eyebrow{ display:inline-block; background:rgba(255,255,255,0.12); border:1px solid rgba(255,255,255,0.25); color:#fff; padding:6px 16px; border-radius:999px; font-size:11px; font-weight:800; letter-spacing:.12em; text-transform:uppercase; margin-bottom:16px; }\n.rjsw-hero h1{ font-family:'Anton',sans-serif; text-transform:uppercase; font-size:clamp(38px,7vw,72px); line-height:.92; color:#fff; margin:0; text-shadow:0 6px 40px rgba(236,72,153,.45); }\n.rjsw-hero h1 .pop{ background:linear-gradient(180deg,#FBCFE8 0%,#EC4899 60%,#DB2777 100%); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }\n.rjsw-hero p{ color:rgba(255,255,255,0.82); font-size:15px; margin:14px auto 0; max-width:540px; }\n\n\/* BUNDLE PROMO \u2014 bold version *\/\n.rjsw-promo {\n  background: linear-gradient(135deg, var(--rj-pink) 0%, var(--rj-pink-dark) 100%);\n  border-radius: 16px;\n  padding: 18px 24px; margin: 0 auto 26px;\n  display: flex; align-items: center; justify-content: center; gap: 16px;\n  flex-wrap: wrap; text-align: center;\n  box-shadow: 0 12px 30px rgba(236,72,153,0.3);\n}\n.rjsw-promo-label{ display:flex; align-items:center; gap:8px; font-family:'Anton',sans-serif; font-size:18px; color:#fff; text-transform:uppercase; letter-spacing:.02em; }\n.rjsw-promo-star { color: #FBBF24; flex-shrink: 0; font-size: 20px; }\n.rjsw-promo-tiers{ display:flex; gap:10px; flex-wrap:wrap; justify-content:center; }\n.rjsw-promo-tier{ display:flex; flex-direction:column; align-items:center; line-height:1.15; background:rgba(255,255,255,0.16); border:1px solid rgba(255,255,255,0.3); border-radius:12px; padding:8px 14px; color:#fff; min-width:74px; }\n.rjsw-promo-tier .q{ font-size:11px; font-weight:600; opacity:.85; text-transform:uppercase; letter-spacing:.04em; }\n.rjsw-promo-tier .p{ font-size:17px; font-weight:800; }\n\n\/* MONTHLY PROMOS row *\/\n.rjsw-monthly{ display:grid; grid-template-columns:1fr 1fr; gap:14px; margin:0 0 26px; }\n@media (max-width:640px){ .rjsw-monthly{ grid-template-columns:1fr; } }\n.rjsw-mpromo{ display:flex; align-items:center; gap:16px; text-decoration:none; border:2px solid var(--rj-line); border-radius:16px; padding:18px 20px; transition:border-color .15s, transform .12s, box-shadow .15s; background:#fff; }\n.rjsw-mpromo:hover{ border-color:var(--rj-pink); transform:translateY(-3px); box-shadow:0 14px 30px rgba(236,72,153,.16); }\n.rjsw-mpromo.prem{ background:linear-gradient(180deg,var(--rj-gold-light) 0%,#fff 60%); border-color:var(--rj-gold); }\n.rjsw-mpromo-badge{ font-family:'Anton',sans-serif; font-size:24px; color:var(--rj-pink-dark); line-height:1; flex:none; }\n.rjsw-mpromo.prem .rjsw-mpromo-badge{ color:var(--rj-gold); }\n.rjsw-mpromo-body{ flex:1; }\n.rjsw-mpromo-tag{ font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase; color:var(--rj-muted); }\n.rjsw-mpromo-name{ font-size:16px; font-weight:800; color:var(--rj-ink); margin:2px 0; }\n.rjsw-mpromo-desc{ font-size:12.5px; color:var(--rj-text); line-height:1.45; }\n.rjsw-mpromo-go{ flex:none; font-weight:800; font-size:13px; color:#fff; background:var(--rj-pink); border-radius:999px; padding:9px 16px; white-space:nowrap; }\n.rjsw-mpromo.prem .rjsw-mpromo-go{ background:var(--rj-gold); }\n\n.rjsw-mode-picker {\n  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;\n  margin-bottom: 24px;\n}\n@media (max-width: 720px) { .rjsw-mode-picker { grid-template-columns: 1fr; } }\n.rjsw-mode-card {\n  background: white; border: 2px solid var(--rj-line);\n  border-radius: 18px; padding: 28px 24px;\n  text-align: center; cursor: pointer; transition: all 0.2s;\n  position: relative;\n}\n.rjsw-mode-card:hover {\n  border-color: var(--rj-pink); transform: translateY(-3px);\n  box-shadow: 0 12px 30px rgba(236, 72, 153, 0.18);\n}\n.rjsw-mode-card.featured {\n  border: 2px solid var(--rj-gold);\n  background: linear-gradient(180deg, var(--rj-gold-light) 0%, white 50%);\n}\n.rjsw-mode-card.featured:hover { border-color: var(--rj-pink); }\n.rjsw-mode-badge {\n  position: absolute; top: -12px; left: 50%; transform: translateX(-50%);\n  background: var(--rj-gold); color: white;\n  padding: 4px 14px; border-radius: 999px;\n  font-size: 11px; font-weight: 800;\n  text-transform: uppercase; letter-spacing: 0.06em;\n  box-shadow: 0 2px 8px rgba(245, 158, 11, 0.4); white-space: nowrap;\n}\n.rjsw-mode-icon {\n  width: 64px; height: 64px; margin: 0 auto 12px;\n  border-radius: 50%; background: var(--rj-pink-lighter);\n  display: flex; align-items: center; justify-content: center;\n  font-size: 28px;\n}\n.rjsw-mode-card.featured .rjsw-mode-icon { background: var(--rj-gold-light); }\n.rjsw-mode-title {\n  font-size: 22px; font-weight: 800; color: var(--rj-ink);\n  margin: 0 0 6px; letter-spacing: -0.02em;\n}\n.rjsw-mode-desc { font-size: 14px; color: var(--rj-text); margin: 0 0 14px; }\n.rjsw-mode-price {\n  font-size: 16px; font-weight: 800; color: var(--rj-pink-dark);\n  margin: 0 0 18px;\n}\n.rjsw-mode-btn {\n  background: var(--rj-pink); color: white; border: none;\n  padding: 12px 24px; border-radius: 999px;\n  font-weight: 700; font-size: 14px; cursor: pointer;\n  font-family: inherit; transition: all 0.15s;\n  box-shadow: 0 2px 8px rgba(236, 72, 153, 0.25);\n}\n.rjsw-mode-btn:hover { background: var(--rj-pink-dark); transform: translateY(-1px); }\n\n.rjsw-screen { display: none; animation: rjswFade 0.3s ease; padding-bottom: 96px; }\n.rjsw-screen.active { display: block; }\n\/* v3.9.5: opacity-only fade. translateY removed because any transform on\n   .rjsw-screen creates a new containing block, which breaks position:fixed\n   for the .rjsw-sticky-cta inside it (the bar ends up positioned relative\n   to the tall screen container instead of the viewport). *\/\n@keyframes rjswFade {\n  from { opacity: 0; }\n  to   { opacity: 1; }\n}\n.rjsw-screen-head {\n  display: flex; justify-content: space-between; align-items: center;\n  gap: 12px; margin-bottom: 18px; flex-wrap: wrap;\n}\n.rjsw-screen-title {\n  font-size: 24px; font-weight: 800; color: var(--rj-ink);\n  letter-spacing: -0.02em; margin: 0;\n}\n.rjsw-screen-sub { font-size: 14px; color: var(--rj-text); margin: 0; }\n\n\/* v3.9.2: fixed-bottom sticky CTA matching \/product\/commercial-production\/.\n   Only the active screen's bar shows; .rjsw-screen has padding-bottom so\n   content doesn't sit under the bar. *\/\n.rjsw-sticky-cta {\n  position: fixed; bottom: 0; left: 0; right: 0; z-index: 9999;\n  background: white; border-top: 2px solid var(--rj-pink);\n  padding: 14px 20px;\n  margin: 0;\n  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;\n  box-shadow: 0 -6px 20px rgba(236, 72, 153, 0.18);\n  \/* v3.9.4: footer-aware lift uses transform:translateY set by JS *\/\n  transform: translateY(0);\n  transition: transform 0.08s linear;\n  will-change: transform;\n}\n.rjsw-screen:not(.active) .rjsw-sticky-cta { display: none; }\n.rjsw-sticky-cta-info { flex: 1; min-width: 200px; }\n.rjsw-sticky-cta-label {\n  font-size: 10px; color: var(--rj-muted);\n  text-transform: uppercase; letter-spacing: 0.08em;\n  font-weight: 700; margin-bottom: 2px;\n}\n.rjsw-sticky-cta-total {\n  font-size: 22px; font-weight: 800;\n  color: var(--rj-pink-dark); line-height: 1.1;\n}\n.rjsw-sticky-cta-save {\n  font-size: 12px; color: var(--rj-green); font-weight: 700; margin-top: 2px;\n}\n.rjsw-sticky-cta-back {\n  background: white; color: var(--rj-text);\n  border: 2px solid var(--rj-line);\n  padding: 10px 18px; border-radius: 999px;\n  font-weight: 700; font-size: 13px; cursor: pointer;\n  font-family: inherit; transition: all 0.15s; white-space: nowrap;\n}\n.rjsw-sticky-cta-back:hover { border-color: var(--rj-text); color: var(--rj-ink); }\n.rjsw-sticky-cta-btn {\n  background: var(--rj-pink); color: white; border: none;\n  padding: 12px 24px; border-radius: 999px;\n  font-weight: 700; font-size: 15px; cursor: pointer;\n  font-family: inherit; transition: all 0.15s;\n  box-shadow: 0 2px 8px rgba(236, 72, 153, 0.3);\n  white-space: nowrap;\n}\n.rjsw-sticky-cta-btn:hover:not(:disabled) {\n  background: var(--rj-pink-dark); transform: translateY(-1px);\n}\n.rjsw-sticky-cta-btn:disabled { opacity: 0.45; cursor: not-allowed; }\n@media (max-width: 540px) {\n  .rjsw-sticky-cta { padding: 12px 14px; gap: 10px; }\n  .rjsw-sticky-cta-total { font-size: 18px; }\n  .rjsw-sticky-cta-btn { padding: 10px 18px; font-size: 13px; }\n  .rjsw-sticky-cta-back { padding: 8px 14px; font-size: 12px; }\n}\n\n.rjsw-pack-bar {\n  background: var(--rj-bg); border: 1px dashed var(--rj-line);\n  border-radius: 12px; padding: 12px 16px;\n  margin-bottom: 16px;\n  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;\n  min-height: 50px;\n}\n.rjsw-pack-bar.empty {\n  background: white; color: var(--rj-muted);\n  justify-content: center; font-size: 13px;\n}\n.rjsw-pack-bar-label {\n  font-size: 12px; font-weight: 700; color: var(--rj-ink); margin-right: 4px;\n}\n.rjsw-pack-tier-msg {\n  font-size: 11px; color: var(--rj-pink-dark); font-weight: 700;\n  margin-left: auto; padding: 3px 10px;\n  background: var(--rj-pink-lighter); border-radius: 999px;\n}\n.rjsw-pack-tier-msg.unlocked { background: var(--rj-green-bg); color: #047857; }\n.rjsw-pack-chip {\n  background: white; border: 1px solid var(--rj-pink);\n  padding: 4px 6px 4px 10px; border-radius: 999px;\n  font-size: 11px; font-weight: 600; color: var(--rj-ink);\n  display: inline-flex; align-items: center; gap: 4px;\n}\n.rjsw-pack-chip-remove {\n  width: 16px; height: 16px; border-radius: 50%;\n  background: var(--rj-pink-lighter); color: var(--rj-pink-dark);\n  border: none; cursor: pointer;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 9px; font-weight: 700; font-family: inherit;\n  padding: 0; transition: all 0.15s;\n}\n.rjsw-pack-chip-remove:hover { background: var(--rj-pink); color: white; }\n\n.rjsw-filters {\n  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;\n  margin-bottom: 16px;\n}\n.rjsw-filter-label {\n  font-size: 12px; font-weight: 700; color: var(--rj-muted);\n  text-transform: uppercase; letter-spacing: 0.06em; margin-right: 4px;\n}\n.rjsw-filter-pill {\n  background: white; border: 2px solid var(--rj-line);\n  padding: 6px 14px; border-radius: 999px;\n  font-weight: 700; font-size: 12px;\n  cursor: pointer; color: var(--rj-text);\n  font-family: inherit; transition: all 0.15s;\n}\n.rjsw-filter-pill:hover { border-color: var(--rj-pink); color: var(--rj-pink-dark); }\n.rjsw-filter-pill.active {\n  background: var(--rj-pink); color: white; border-color: var(--rj-pink);\n}\n\n.rjsw-bed-grid {\n  display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;\n}\n@media (max-width: 900px) { .rjsw-bed-grid { grid-template-columns: repeat(4, 1fr); } }\n@media (max-width: 720px) { .rjsw-bed-grid { grid-template-columns: repeat(3, 1fr); } }\n@media (max-width: 480px) { .rjsw-bed-grid { grid-template-columns: repeat(2, 1fr); } }\n.rjsw-bed-card {\n  background: white; border: 2px solid var(--rj-line);\n  border-radius: 12px; padding: 12px;\n  text-align: center; transition: all 0.15s;\n  position: relative;\n  display: flex; flex-direction: column; align-items: center; gap: 6px;\n  min-height: 110px;\n}\n.rjsw-bed-card:hover {\n  border-color: var(--rj-pink);\n  box-shadow: 0 4px 12px rgba(236, 72, 153, 0.1);\n}\n.rjsw-bed-card.in-pack { background: var(--rj-pink-lighter); border-color: var(--rj-pink); }\n.rjsw-bed-type-tag {\n  position: absolute; top: 6px; left: 6px;\n  font-size: 9px; font-weight: 700; color: var(--rj-muted);\n  background: var(--rj-bg); padding: 2px 6px;\n  border-radius: 999px; text-transform: uppercase; letter-spacing: 0.04em;\n}\n.rjsw-bed-discount {\n  position: absolute; top: 6px; right: 6px;\n  font-size: 9px; font-weight: 800;\n  color: #047857; background: var(--rj-green-bg);\n  padding: 2px 6px; border-radius: 999px;\n}\n.rjsw-bed-card-name {\n  font-weight: 700; color: var(--rj-ink); font-size: 13px;\n  margin-top: 18px; line-height: 1.3;\n}\n.rjsw-bed-actions { display: flex; gap: 6px; margin-top: auto; }\n.rjsw-bed-btn {\n  width: 32px; height: 32px; border-radius: 50%;\n  border: none; cursor: pointer;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 12px; font-family: inherit; font-weight: 700;\n  padding: 0; transition: all 0.15s;\n}\n.rjsw-bed-btn.play {\n  background: white; border: 1px solid var(--rj-line); color: var(--rj-text);\n}\n.rjsw-bed-btn.play:hover { border-color: var(--rj-pink); color: var(--rj-pink-dark); }\n.rjsw-bed-btn.play.playing { background: var(--rj-purple); color: white; border-color: var(--rj-purple); }\n.rjsw-bed-btn.add { background: var(--rj-pink); color: white; }\n.rjsw-bed-btn.add:hover { background: var(--rj-pink-dark); transform: scale(1.05); }\n.rjsw-bed-btn.remove {\n  background: var(--rj-bg); color: var(--rj-pink-dark);\n  border: 1px solid var(--rj-pink);\n}\n.rjsw-bed-btn.remove:hover { background: var(--rj-pink); color: white; }\n.rjsw-bed-count {\n  position: absolute; bottom: 8px; right: 8px;\n  background: var(--rj-pink); color: white;\n  width: 22px; height: 22px; border-radius: 50%;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 11px; font-weight: 800;\n  box-shadow: 0 2px 4px rgba(236, 72, 153, 0.3);\n}\n\n.rjsw-pkg-grid {\n  display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px;\n}\n@media (max-width: 900px) { .rjsw-pkg-grid { grid-template-columns: repeat(2, 1fr); } }\n@media (max-width: 540px) { .rjsw-pkg-grid { grid-template-columns: 1fr; } }\n.rjsw-pkg-card {\n  background: white; border: 2px solid var(--rj-line);\n  border-radius: 12px; padding: 14px;\n  cursor: pointer; transition: all 0.15s;\n  position: relative;\n}\n.rjsw-pkg-card:hover { border-color: var(--rj-pink); }\n.rjsw-pkg-card.selected {\n  border-color: var(--rj-pink); background: var(--rj-pink-lighter);\n}\n.rjsw-pkg-card.selected::after {\n  content: '\u2713'; position: absolute; top: 8px; right: 8px;\n  width: 24px; height: 24px; background: var(--rj-pink); color: white;\n  border-radius: 50%; display: flex; align-items: center;\n  justify-content: center; font-weight: 700; font-size: 13px;\n}\n.rjsw-pkg-thumb {\n  width: 100%; aspect-ratio: 3\/2;\n  background: var(--rj-bg); border-radius: 8px;\n  object-fit: cover; margin-bottom: 10px;\n}\n.rjsw-pkg-name {\n  font-weight: 700; color: var(--rj-ink); font-size: 14px;\n  margin-bottom: 8px; line-height: 1.3;\n}\n.rjsw-pkg-listen {\n  display: inline-flex; align-items: center; gap: 5px;\n  background: white; border: 1px solid var(--rj-line);\n  padding: 5px 12px; border-radius: 999px; font-size: 11px;\n  cursor: pointer; color: var(--rj-text); font-family: inherit;\n  font-weight: 600; transition: all 0.15s;\n}\n.rjsw-pkg-listen:hover { border-color: var(--rj-pink); color: var(--rj-pink-dark); }\n.rjsw-pkg-listen.playing { background: var(--rj-purple); color: white; border-color: var(--rj-purple); }\n\n.rjsw-singers {\n  display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px;\n}\n@media (max-width: 640px) { .rjsw-singers { grid-template-columns: 1fr; } }\n.rjsw-singer {\n  background: white; border: 2px solid var(--rj-line);\n  border-radius: 14px; padding: 18px 14px;\n  text-align: center; cursor: pointer; transition: all 0.2s;\n  position: relative;\n}\n.rjsw-singer:hover {\n  border-color: var(--rj-pink); transform: translateY(-2px);\n  box-shadow: 0 6px 16px rgba(236, 72, 153, 0.12);\n}\n.rjsw-singer.selected {\n  border-color: var(--rj-pink);\n  background: linear-gradient(180deg, white 0%, var(--rj-pink-lighter) 100%);\n}\n.rjsw-singer.selected::after {\n  content: '\u2713'; position: absolute; top: 8px; right: 8px;\n  width: 22px; height: 22px; background: var(--rj-pink); color: white;\n  border-radius: 50%; display: flex; align-items: center;\n  justify-content: center; font-weight: 700; font-size: 12px;\n}\n.rjsw-singer-avatar-wrap {\n  position: relative; width: 84px; height: 84px; margin: 4px auto 12px;\n}\n.rjsw-singer-avatar {\n  width: 100%; height: 100%; border-radius: 50%;\n  object-fit: cover; background: var(--rj-pink-light); display: block;\n}\n.rjsw-singer-play {\n  position: absolute; bottom: -2px; right: -2px;\n  width: 28px; height: 28px; background: var(--rj-pink); color: white;\n  border: 3px solid white; border-radius: 50%; cursor: pointer;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 11px; transition: all 0.15s; font-family: inherit;\n  padding: 0; box-shadow: 0 2px 8px rgba(236, 72, 153, 0.4);\n}\n.rjsw-singer-play:hover { background: var(--rj-pink-dark); transform: scale(1.1); }\n.rjsw-singer-play.playing { background: var(--rj-purple); }\n.rjsw-singer-name { font-size: 16px; font-weight: 700; color: var(--rj-ink); margin: 0 0 4px; }\n.rjsw-singer-creds {\n  font-size: 11px; color: var(--rj-muted); line-height: 1.4;\n  margin-bottom: 8px; min-height: 30px;\n}\n.rjsw-singer-delivery {\n  font-size: 10px; color: var(--rj-text); font-weight: 600;\n  text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 8px;\n}\n.rjsw-singer-price { font-weight: 800; color: var(--rj-pink-dark); font-size: 14px; }\n.rjsw-singer-price.free { color: var(--rj-green); }\n\n.rjsw-lyrics-block {\n  background: white; border: 1px solid var(--rj-line);\n  border-radius: 12px; padding: 14px; margin-bottom: 12px;\n}\n.rjsw-lyrics-block-head {\n  display: flex; align-items: center; gap: 10px;\n  margin-bottom: 10px; font-size: 13px;\n}\n.rjsw-lyrics-block-num {\n  width: 22px; height: 22px; border-radius: 50%;\n  background: var(--rj-pink); color: white;\n  display: flex; align-items: center; justify-content: center;\n  font-weight: 700; font-size: 11px; flex-shrink: 0;\n}\n.rjsw-lyrics-block-name { font-weight: 700; color: var(--rj-ink); flex: 1; }\n.rjsw-lyrics-block-type {\n  font-size: 11px; color: var(--rj-muted); font-weight: 600;\n  text-transform: uppercase; letter-spacing: 0.04em;\n}\n.rjsw-textarea {\n  width: 100%; padding: 12px 14px; border: 2px solid var(--rj-line);\n  border-radius: 10px; font-family: inherit;\n  font-size: 14px; min-height: 70px; resize: vertical;\n  transition: border-color 0.15s; background: white;\n  color: var(--rj-ink); line-height: 1.5;\n}\n.rjsw-textarea:focus {\n  outline: none; border-color: var(--rj-pink);\n  box-shadow: 0 0 0 4px rgba(236, 72, 153, 0.1);\n}\n.rjsw-hint { font-size: 12px; color: var(--rj-muted); margin: 4px 0 0; }\n\n.rjsw-fmt-bar {\n  background: white; border: 1px solid var(--rj-line);\n  border-radius: 12px; padding: 14px 18px; margin-bottom: 14px;\n}\n.rjsw-fmt-head {\n  font-size: 14px; font-weight: 700; color: var(--rj-ink); margin-bottom: 8px;\n}\n.rjsw-fmt-options { display: flex; gap: 6px; flex-wrap: wrap; }\n.rjsw-fmt-opt {\n  background: white; border: 1px solid var(--rj-line);\n  padding: 7px 16px; border-radius: 999px;\n  font-size: 13px; font-weight: 700; cursor: pointer;\n  color: var(--rj-text); transition: all 0.15s; font-family: inherit;\n}\n.rjsw-fmt-opt:hover { border-color: var(--rj-pink); color: var(--rj-pink-dark); }\n.rjsw-fmt-opt.selected {\n  background: var(--rj-pink); color: white; border-color: var(--rj-pink);\n}\n.rjsw-extras {\n  display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 18px;\n}\n@media (max-width: 640px) { .rjsw-extras { grid-template-columns: 1fr; } }\n.rjsw-extra {\n  background: white; border: 2px solid var(--rj-line);\n  border-radius: 10px; padding: 12px 14px;\n  cursor: pointer; transition: all 0.15s;\n  display: flex; align-items: center; gap: 10px;\n}\n.rjsw-extra:hover { border-color: var(--rj-pink); }\n.rjsw-extra.selected {\n  border-color: var(--rj-pink); background: var(--rj-pink-lighter);\n}\n.rjsw-extra-check {\n  width: 20px; height: 20px; border-radius: 5px;\n  border: 2px solid var(--rj-line); flex-shrink: 0;\n  display: flex; align-items: center; justify-content: center;\n  background: white; transition: all 0.15s; font-size: 11px;\n}\n.rjsw-extra.selected .rjsw-extra-check {\n  background: var(--rj-pink); border-color: var(--rj-pink);\n  color: white; font-weight: 700;\n}\n.rjsw-extra-info { flex: 1; min-width: 0; }\n.rjsw-extra-name { font-weight: 700; color: var(--rj-ink); font-size: 13px; }\n.rjsw-extra-desc { font-size: 11px; color: var(--rj-text); line-height: 1.4; }\n.rjsw-extra-price { font-weight: 800; color: var(--rj-pink-dark); font-size: 13px; flex-shrink: 0; }\n\n.rjsw-review-card {\n  background: white; border: 1px solid var(--rj-line);\n  border-radius: 12px; padding: 18px;\n}\n.rjsw-review-row {\n  display: flex; justify-content: space-between; align-items: flex-start;\n  padding: 10px 0; border-bottom: 1px solid var(--rj-line); gap: 12px;\n}\n.rjsw-review-row:first-child { padding-top: 0; }\n.rjsw-review-row:last-child { border-bottom: none; }\n.rjsw-review-label {\n  color: var(--rj-muted); font-size: 11px; font-weight: 700;\n  text-transform: uppercase; letter-spacing: 0.06em; flex-shrink: 0;\n}\n.rjsw-review-value {\n  color: var(--rj-ink); font-weight: 600; text-align: right;\n  max-width: 65%; word-wrap: break-word; font-size: 13px;\n}\n.rjsw-review-value-muted { color: var(--rj-muted); font-weight: 400; }\n.rjsw-review-tier {\n  background: var(--rj-green-bg); color: #047857;\n  border-radius: 10px; padding: 12px 16px;\n  font-size: 13px; font-weight: 700;\n  margin: 14px 0; text-align: center;\n}\n\n.rjsw-modal-overlay {\n  position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n  background: rgba(15, 23, 42, 0.7); display: none;\n  align-items: center; justify-content: center;\n  z-index: 100000; padding: 16px;\n}\n.rjsw-modal-overlay.open { display: flex; animation: rjswFadeIn 0.2s ease; }\n@keyframes rjswFadeIn { from { opacity: 0; } to { opacity: 1; } }\n.rjsw-modal {\n  background: white; border-radius: 18px;\n  padding: 28px; max-width: 500px; width: 100%;\n  max-height: 90vh; overflow-y: auto;\n  animation: rjswSlideUp 0.3s ease;\n}\n@keyframes rjswSlideUp {\n  from { opacity: 0; transform: translateY(20px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n.rjsw-modal-loader {\n  width: 36px; height: 36px;\n  border: 3px solid var(--rj-line);\n  border-top-color: var(--rj-pink);\n  border-radius: 50%; margin: 0 auto 16px;\n  animation: rjswSpin 0.8s linear infinite;\n}\n@keyframes rjswSpin { to { transform: rotate(360deg); } }\n.rjsw-modal h3 {\n  margin: 0 0 6px; font-size: 22px; color: var(--rj-ink);\n  text-align: center; font-weight: 800;\n}\n.rjsw-modal-sub {\n  color: var(--rj-text); margin: 0 0 18px;\n  font-size: 14px; text-align: center;\n}\n.rjsw-modal-progress {\n  background: var(--rj-bg); border-radius: 10px;\n  padding: 10px; font-size: 13px; color: var(--rj-text);\n  text-align: center; margin: 12px 0;\n}\n\nhtml body .rjsw-toast {\n  position: fixed !important; top: 24px !important; left: 50% !important;\n  transform: translateX(-50%) translateY(-200%) !important;\n  background: #EC4899 !important; color: white !important;\n  padding: 14px 28px !important; border-radius: 999px !important;\n  font-weight: 700 !important; z-index: 100001 !important;\n  transition: transform 0.3s !important;\n  box-shadow: 0 10px 30px rgba(236, 72, 153, 0.5) !important;\n  max-width: 90vw !important; text-align: center !important; font-size: 14px !important;\n}\nhtml body .rjsw-toast.show { transform: translateX(-50%) translateY(0) !important; }\nhtml body .rjsw-toast.success {\n  background: #10B981 !important;\n  box-shadow: 0 10px 30px rgba(16, 185, 129, 0.5) !important;\n}\nhtml body .rjsw-toast.error {\n  background: #DC2626 !important;\n  box-shadow: 0 10px 30px rgba(220, 38, 38, 0.5) !important;\n}\n\n\/* RJ24-EDIT (Nov 2026): trust panel + GBP note *\/\n.rjsw .rjsw-trust{\n  display:grid; grid-template-columns:repeat(5,1fr); gap:10px;\n  margin:0 0 18px; padding:14px;\n  background:#fff; border:1px solid var(--rj-line); border-radius:14px;\n}\n.rjsw .rjsw-trust-item{\n  display:flex; flex-direction:column; align-items:center; text-align:center;\n  padding:8px 6px; gap:4px;\n}\n.rjsw .rjsw-trust-ico{ font-size:22px; line-height:1; margin-bottom:2px; }\n.rjsw .rjsw-trust-h{ font-size:12.5px; font-weight:800; color:var(--rj-ink); letter-spacing:.01em; line-height:1.25; }\n.rjsw .rjsw-trust-d{ font-size:11px; color:var(--rj-text); line-height:1.4; }\n@media (max-width: 880px){\n  .rjsw .rjsw-trust{ grid-template-columns:repeat(2,1fr); }\n}\n@media (max-width: 480px){\n  .rjsw .rjsw-trust{ grid-template-columns:1fr; }\n}\n\n.rjsw .rjsw-gbp{\n  display:flex; align-items:center; gap:10px;\n  background:#FBF7F2; border:1px solid #F3E8DD; border-left:3px solid var(--rj-pink);\n  border-radius:10px; padding:10px 16px; margin:0 auto 22px;\n  max-width:560px; font-size:12.5px; line-height:1.5; color:var(--rj-text);\n}\n.rjsw .rjsw-gbp-ico{ font-size:15px; flex-shrink:0; }\n.rjsw .rjsw-gbp-text strong{ color:var(--rj-ink); font-weight:700; }\n\/* \/RJ24-EDIT *\/\n<\/style>\n\n<div class=\"rjsw\" id=\"rj-sung-wizard\">\n\n  <div class=\"rjsw-hero\">\n    <div class=\"rjsw-hero-eyebrow\">Sung by singers for BBC Radio 1 &amp; 2, Sky &amp; Virgin<\/div>\n    <h1>Sung Jingles<br><span class=\"pop\">that sound huge.<\/span><\/h1>\n    <p>Your station name, professionally sung and produced. Build your own custom pack, or grab one of this month's ready-made offers below.<\/p>\n  <\/div>\n\n  <!-- RJ24-EDIT (Nov 2026): \"What you get\" trust panel -->\n  <div class=\"rjsw-trust\">\n    <div class=\"rjsw-trust-item\"><span class=\"rjsw-trust-ico\">\ud83c\udfa4<\/span><span class=\"rjsw-trust-h\">BBC-trained singers<\/span><span class=\"rjsw-trust-d\">Bea &amp; Mo have sung for Radio 1, 2, Sky &amp; Virgin<\/span><\/div>\n    <div class=\"rjsw-trust-item\"><span class=\"rjsw-trust-ico\">\u23f1<\/span><span class=\"rjsw-trust-h\">From 48 hours<\/span><span class=\"rjsw-trust-d\">Rosie &amp; Bea in 48h; Mo in 3&ndash;5 days<\/span><\/div>\n    <div class=\"rjsw-trust-item\"><span class=\"rjsw-trust-ico\">\ud83d\udcc2<\/span><span class=\"rjsw-trust-h\">MP3, WAV &amp; AIFF<\/span><span class=\"rjsw-trust-d\">Studio-quality files in your chosen format<\/span><\/div>\n    <div class=\"rjsw-trust-item\"><span class=\"rjsw-trust-ico\">\u221e<\/span><span class=\"rjsw-trust-h\">Royalty-free forever<\/span><span class=\"rjsw-trust-d\">Yours to broadcast as long as your station runs<\/span><\/div>\n    <div class=\"rjsw-trust-item\"><span class=\"rjsw-trust-ico\">\ud83c\udfb6<\/span><span class=\"rjsw-trust-h\">FREE acapella included<\/span><span class=\"rjsw-trust-d\">Every sung jingle comes with the acapella version too<\/span><\/div>\n  <\/div>\n  <!-- \/RJ24-EDIT -->\n\n  <div class=\"rjsw-promo\">\n    <span class=\"rjsw-promo-label\"><span class=\"rjsw-promo-star\">\u2605<\/span> Bundle &amp; save<\/span>\n    <span class=\"rjsw-promo-tiers\">\n      <span class=\"rjsw-promo-tier\"><span class=\"q\">1 jingle<\/span><span class=\"p\">\u00a369<\/span><\/span>\n      <span class=\"rjsw-promo-tier\"><span class=\"q\">2 jingles<\/span><span class=\"p\">\u00a3108<\/span><\/span>\n      <span class=\"rjsw-promo-tier\"><span class=\"q\">3 jingles<\/span><span class=\"p\">\u00a3147<\/span><\/span>\n      <span class=\"rjsw-promo-tier\"><span class=\"q\">4 jingles<\/span><span class=\"p\">\u00a3199<\/span><\/span>\n      <span class=\"rjsw-promo-tier\"><span class=\"q\">5+ jingles<\/span><span class=\"p\">\u00a349 each<\/span><\/span>\n    <\/span>\n  <\/div>\n\n  <!-- MONTHLY PROMOS \u2014 deliberately vague so they never need editing.\n       The two offer products live at fixed URLs; their price\/content changes\n       monthly but these cards just point to them. -->\n  <div class=\"rjsw-monthly\">\n    <a class=\"rjsw-mpromo\" href=\"\/product\/sung-jingle-offer\/\">\n      <span class=\"rjsw-mpromo-badge\">\u00a359<\/span>\n      <span class=\"rjsw-mpromo-body\">\n        <span class=\"rjsw-mpromo-tag\">This month's offer<\/span>\n        <span class=\"rjsw-mpromo-name\">Sung Jingle Special<\/span>\n        <span class=\"rjsw-mpromo-desc\">A bespoke sung jingle on this month's featured bed &mdash; our best-value sung deal.<\/span>\n      <\/span>\n      <span class=\"rjsw-mpromo-go\">View &rarr;<\/span>\n    <\/a>\n    <a class=\"rjsw-mpromo prem\" href=\"\/product\/offer-sung-jingle\/\">\n      <span class=\"rjsw-mpromo-badge\">\u00a379<\/span>\n      <span class=\"rjsw-mpromo-body\">\n        <span class=\"rjsw-mpromo-tag\">Premium offer<\/span>\n        <span class=\"rjsw-mpromo-name\">Premium Sung Jingle<\/span>\n        <span class=\"rjsw-mpromo-desc\">Our premium monthly offer &mdash; choose your singer on a premium bed.<\/span>\n      <\/span>\n      <span class=\"rjsw-mpromo-go\">View &rarr;<\/span>\n    <\/a>\n  <\/div>\n\n  <!-- RJ24-EDIT (Nov 2026): GBP currency note -->\n  <div class=\"rjsw-gbp\">\n    <span class=\"rjsw-gbp-ico\">\ud83d\udcb1<\/span>\n    <span class=\"rjsw-gbp-text\">Prices shown in <strong>GBP (\u00a3)<\/strong>. Not in the UK? Your local currency is calculated at checkout, at today's exchange rate.<\/span>\n  <\/div>\n  <!-- \/RJ24-EDIT -->\n\n  <div class=\"rjsw-screen active\" id=\"rjsw-screen-mode\">\n    <div class=\"rjsw-mode-picker\">\n      <div class=\"rjsw-mode-card\" data-mode=\"custom\">\n        <div class=\"rjsw-mode-icon\">\ud83c\udfb5<\/div>\n        <h3 class=\"rjsw-mode-title\">Build a Custom Pack<\/h3>\n        <p class=\"rjsw-mode-desc\">Mix any beds from AC, CHR, Oldies or Acapella. Bundle discounts at 2\/3\/4 jingles.<\/p>\n        <div class=\"rjsw-mode-price\">from \u00a369<\/div>\n        <button type=\"button\" class=\"rjsw-mode-btn\">Start building \u2192<\/button>\n      <\/div>\n      <div class=\"rjsw-mode-card featured\" data-mode=\"package\">\n        <div class=\"rjsw-mode-badge\">\u2605 Best Value<\/div>\n        <div class=\"rjsw-mode-icon\">\ud83d\udce6<\/div>\n        <h3 class=\"rjsw-mode-title\">Full Station Package<\/h3>\n        <p class=\"rjsw-mode-desc\">Complete ready-made multi-cut package. Choose from 17 packages.<\/p>\n        <div class=\"rjsw-mode-price\">\u00a3199<\/div>\n        <button type=\"button\" class=\"rjsw-mode-btn\">Choose package \u2192<\/button>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"rjsw-screen\" id=\"rjsw-screen-browse\">\n    <div class=\"rjsw-sticky-cta\">\n      <button type=\"button\" class=\"rjsw-sticky-cta-back\" data-back=\"mode\">\u2190 Change pack<\/button>\n      <div class=\"rjsw-sticky-cta-info\">\n        <div class=\"rjsw-sticky-cta-label\">Pack total<\/div>\n        <div class=\"rjsw-sticky-cta-total\" id=\"rjsw-cta-total-browse\">\u00a30.00<\/div>\n        <div class=\"rjsw-sticky-cta-save\" id=\"rjsw-cta-save-browse\"><\/div>\n      <\/div>\n      <button type=\"button\" class=\"rjsw-sticky-cta-btn\" data-next=\"singer\" id=\"rjsw-cta-btn-browse\" disabled>Continue \u2192<\/button>\n    <\/div>\n    <div class=\"rjsw-screen-head\">\n      <div>\n        <h2 class=\"rjsw-screen-title\">Pick your music beds<\/h2>\n        <p class=\"rjsw-screen-sub\">Tap + to add. Mix any types. Bundle discount applies automatically.<\/p>\n      <\/div>\n    <\/div>\n    <div class=\"rjsw-pack-bar\" id=\"rjsw-pack-bar\"><\/div>\n    <div class=\"rjsw-filters\">\n      <span class=\"rjsw-filter-label\">Style:<\/span>\n      <button type=\"button\" class=\"rjsw-filter-pill active\" data-filter=\"all\">All<\/button>\n      <button type=\"button\" class=\"rjsw-filter-pill\" data-filter=\"ac\">AC \/ Hot AC<\/button>\n      <button type=\"button\" class=\"rjsw-filter-pill\" data-filter=\"chr\">CHR<\/button>\n      <button type=\"button\" class=\"rjsw-filter-pill\" data-filter=\"oldies\">Oldies &amp; Country<\/button>\n      <button type=\"button\" class=\"rjsw-filter-pill\" data-filter=\"acapella\">Acapella<\/button>\n    <\/div>\n    <div class=\"rjsw-bed-grid\" id=\"rjsw-bed-grid\"><\/div>\n  <\/div>\n\n  <div class=\"rjsw-screen\" id=\"rjsw-screen-package\">\n    <div class=\"rjsw-sticky-cta\">\n      <button type=\"button\" class=\"rjsw-sticky-cta-back\" data-back=\"mode\">\u2190 Change pack<\/button>\n      <div class=\"rjsw-sticky-cta-info\">\n        <div class=\"rjsw-sticky-cta-label\">Package total<\/div>\n        <div class=\"rjsw-sticky-cta-total\" id=\"rjsw-cta-total-package\">\u00a30.00<\/div>\n      <\/div>\n      <button type=\"button\" class=\"rjsw-sticky-cta-btn\" data-next=\"singer\" id=\"rjsw-cta-btn-package\" disabled>Continue \u2192<\/button>\n    <\/div>\n    <div class=\"rjsw-screen-head\">\n      <div>\n        <h2 class=\"rjsw-screen-title\">Pick a Full Station Package<\/h2>\n        <p class=\"rjsw-screen-sub\">Each is a complete multi-cut set, \u00a3199 total.<\/p>\n      <\/div>\n    <\/div>\n    <div class=\"rjsw-pkg-grid\" id=\"rjsw-pkg-grid\"><\/div>\n  <\/div>\n\n  <div class=\"rjsw-screen\" id=\"rjsw-screen-singer\">\n    <div class=\"rjsw-sticky-cta\">\n      <button type=\"button\" class=\"rjsw-sticky-cta-back\" data-back=\"browse-or-package\">\u2190 Back<\/button>\n      <div class=\"rjsw-sticky-cta-info\">\n        <div class=\"rjsw-sticky-cta-label\">Total<\/div>\n        <div class=\"rjsw-sticky-cta-total\" id=\"rjsw-cta-total-singer\">\u00a30.00<\/div>\n        <div class=\"rjsw-sticky-cta-save\" id=\"rjsw-cta-save-singer\"><\/div>\n      <\/div>\n      <button type=\"button\" class=\"rjsw-sticky-cta-btn\" data-next=\"lyrics\" id=\"rjsw-cta-btn-singer\" disabled>Continue \u2192<\/button>\n    <\/div>\n    <div class=\"rjsw-screen-head\">\n      <div>\n        <h2 class=\"rjsw-screen-title\">Pick a singer<\/h2>\n        <p class=\"rjsw-screen-sub\" id=\"rjsw-singer-sub\">One singer for your whole pack.<\/p>\n      <\/div>\n    <\/div>\n    <div class=\"rjsw-singers\" id=\"rjsw-singers\"><\/div>\n  <\/div>\n\n  <div class=\"rjsw-screen\" id=\"rjsw-screen-lyrics\">\n    <div class=\"rjsw-sticky-cta\">\n      <button type=\"button\" class=\"rjsw-sticky-cta-back\" data-back=\"singer\">\u2190 Back<\/button>\n      <div class=\"rjsw-sticky-cta-info\">\n        <div class=\"rjsw-sticky-cta-label\">Total<\/div>\n        <div class=\"rjsw-sticky-cta-total\" id=\"rjsw-cta-total-lyrics\">\u00a30.00<\/div>\n        <div class=\"rjsw-sticky-cta-save\" id=\"rjsw-cta-save-lyrics\"><\/div>\n      <\/div>\n      <button type=\"button\" class=\"rjsw-sticky-cta-btn\" data-next=\"fmt\" id=\"rjsw-cta-btn-lyrics\" disabled>Continue \u2192<\/button>\n    <\/div>\n    <div class=\"rjsw-screen-head\">\n      <div>\n        <h2 class=\"rjsw-screen-title\">Lyrics<\/h2>\n        <p class=\"rjsw-screen-sub\" id=\"rjsw-lyrics-sub\">Keep each jingle to 5\u20138 words for best quality.<\/p>\n      <\/div>\n    <\/div>\n    <div id=\"rjsw-lyrics-blocks\"><\/div>\n    <div class=\"rjsw-lyrics-block\">\n      <div class=\"rjsw-lyrics-block-head\">\n        <div class=\"rjsw-lyrics-block-name\">Pronunciation \/ Instructions (optional)<\/div>\n      <\/div>\n      <textarea class=\"rjsw-textarea\" id=\"rjsw-instructions\" placeholder=\"e.g. Pronounce KIX like 'kicks'. Upbeat mood.\"><\/textarea>\n    <\/div>\n  <\/div>\n\n  <div class=\"rjsw-screen\" id=\"rjsw-screen-fmt\">\n    <div class=\"rjsw-sticky-cta\">\n      <button type=\"button\" class=\"rjsw-sticky-cta-back\" data-back=\"lyrics\">\u2190 Back<\/button>\n      <div class=\"rjsw-sticky-cta-info\">\n        <div class=\"rjsw-sticky-cta-label\">Total<\/div>\n        <div class=\"rjsw-sticky-cta-total\" id=\"rjsw-cta-total-fmt\">\u00a30.00<\/div>\n        <div class=\"rjsw-sticky-cta-save\" id=\"rjsw-cta-save-fmt\"><\/div>\n      <\/div>\n      <button type=\"button\" class=\"rjsw-sticky-cta-btn\" data-next=\"review\" id=\"rjsw-cta-btn-fmt\">Review \u2192<\/button>\n    <\/div>\n    <div class=\"rjsw-screen-head\">\n      <div>\n        <h2 class=\"rjsw-screen-title\">Format &amp; extras<\/h2>\n        <p class=\"rjsw-screen-sub\">Pick your file format and any optional add-ons.<\/p>\n      <\/div>\n    <\/div>\n    <div class=\"rjsw-fmt-bar\">\n      <div class=\"rjsw-fmt-head\">File format<\/div>\n      <div class=\"rjsw-fmt-options\">\n        <button type=\"button\" class=\"rjsw-fmt-opt selected\" data-format=\"MP3\">MP3<\/button>\n        <button type=\"button\" class=\"rjsw-fmt-opt\" data-format=\"WAVE\">WAV<\/button>\n        <button type=\"button\" class=\"rjsw-fmt-opt\" data-format=\"AIFF\">AIFF<\/button>\n      <\/div>\n    <\/div>\n    <div id=\"rjsw-extras-container\"><\/div>\n  <\/div>\n\n  <div class=\"rjsw-screen\" id=\"rjsw-screen-review\">\n    <div class=\"rjsw-sticky-cta\">\n      <button type=\"button\" class=\"rjsw-sticky-cta-back\" data-back=\"fmt\">\u2190 Back<\/button>\n      <div class=\"rjsw-sticky-cta-info\">\n        <div class=\"rjsw-sticky-cta-label\">Total<\/div>\n        <div class=\"rjsw-sticky-cta-total\" id=\"rjsw-cta-total-review\">\u00a30.00<\/div>\n        <div class=\"rjsw-sticky-cta-save\" id=\"rjsw-cta-save-review\"><\/div>\n      <\/div>\n      <button type=\"button\" class=\"rjsw-sticky-cta-btn\" data-next=\"cart\" id=\"rjsw-cta-btn-review\">Add to cart \ud83d\uded2<\/button>\n    <\/div>\n    <div class=\"rjsw-screen-head\">\n      <div>\n        <h2 class=\"rjsw-screen-title\">Review &amp; add to cart<\/h2>\n        <p class=\"rjsw-screen-sub\">Check everything looks right.<\/p>\n      <\/div>\n    <\/div>\n    <div id=\"rjsw-review-container\"><\/div>\n  <\/div>\n<\/div>\n\n<div class=\"rjsw-modal-overlay\" id=\"rjsw-cart-modal\">\n  <div class=\"rjsw-modal\">\n    <div class=\"rjsw-modal-loader\"><\/div>\n    <h3 id=\"rjsw-modal-title\">Adding to cart\u2026<\/h3>\n    <p class=\"rjsw-modal-sub\" id=\"rjsw-modal-sub\">Please don't close this tab.<\/p>\n    <div class=\"rjsw-modal-progress\" id=\"rjsw-modal-progress\"><\/div>\n  <\/div>\n<\/div>\n\n<div class=\"rjsw-toast\" id=\"rjsw-toast\"><\/div>\n\n<script>\n(function() {\n  'use strict';\n\n  \/\/ ===== DATA =====\n  const BEDS = [\n    { id: 'tameside-1', type: 'ac', name: 'Tameside 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/06\/Tameside-Radio-2021_01_ID.mp3' },\n    { id: 'tameside-2', type: 'ac', name: 'Tameside 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/06\/Tameside-Radio-2021_02_ID.mp3' },\n    { id: 'tameside-3', type: 'ac', name: 'Tameside 3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/06\/Tameside-Radio-2021_03_ID.mp3' },\n    { id: 'tameside-4', type: 'ac', name: 'Tameside 4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/06\/Tameside-Radio-2021_04_ID.mp3' },\n    { id: 'giant-1', type: 'ac', name: 'The Giant 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/THE-GIANT_Cut-1.mp3' },\n    { id: 'giant-2', type: 'ac', name: 'The Giant 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/THE-GIANT_Cut-2.mp3' },\n    { id: 'giant-3', type: 'ac', name: 'The Giant 3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/THE-GIANT_Cut-3.mp3' },\n    { id: 'giant-4', type: 'ac', name: 'The Giant 4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/THE-GIANT_Cut-4.mp3' },\n    { id: 'hotac-1', type: 'ac', name: 'Hot AC\/AC 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/AC2_Bea-new-.mp3' },\n    { id: 'hotac-2', type: 'ac', name: 'Hot AC\/AC 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/ac-3-nfrs-with-bed-long.wav' },\n    { id: 'hotac-3', type: 'ac', name: 'Hot AC\/AC 3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/AC4_Bea-new.mp3' },\n    { id: 'hotac-4', type: 'ac', name: 'Hot AC\/AC 4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/ac5-nfrs-with-bed-long-.wav' },\n    { id: 'hotac-5', type: 'ac', name: 'Hot AC\/AC 5', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/AC6.mp3' },\n    { id: 'hotac-6', type: 'ac', name: 'Hot AC\/AC 6', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/AC7.mp3' },\n    { id: 'hotac-7', type: 'ac', name: 'Hot AC\/AC 7', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/AC8-Breeze-104_BeaMo.mp3' },\n    { id: 'ac-hour', type: 'ac', name: 'AC Hour Opener', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/HOUR-OPENER_Mo_1.mp3' },\n    { id: 'ac-news', type: 'ac', name: 'AC News Jingle', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/NEWS_Mo.mp3' },\n    { id: 'ac-weather', type: 'ac', name: 'AC Weather Jingle', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/B106-WEATHER-long-with-bed-.mp3' },\n    { id: 'chr-1', type: 'chr', name: 'CHR-1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR8_Bea-new.mp3' },\n    { id: 'chr-2', type: 'chr', name: 'CHR-2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR-9_Bea-new.mp3' },\n    { id: 'chr-3', type: 'chr', name: 'CHR-3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR-10_Bea-new.mp3' },\n    { id: 'chr-4', type: 'chr', name: 'CHR-4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR-11_Bea-new.mp3' },\n    { id: 'chr-5', type: 'chr', name: 'CHR-5', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR12-BAY-FM_BeaMo.mp3' },\n    { id: 'chr-6', type: 'chr', name: 'CHR-6', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR-13_Mo.mp3' },\n    { id: 'chr-7', type: 'chr', name: 'CHR-7', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/CHR3-Hot98_BMo.mp3' },\n    { id: 'oldies-1', type: 'oldies', name: 'Oldies-1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/03\/Demo-Oldies-1.mp3' },\n    { id: 'oldies-2', type: 'oldies', name: 'Oldies-2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/oldies-2.mp3' },\n    { id: 'oldies-3', type: 'oldies', name: 'Oldies-3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Oldies3.wav' },\n    { id: 'oldies-4', type: 'oldies', name: 'Oldies-4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/oldies-4.wav' },\n    { id: 'oldies-5', type: 'oldies', name: 'Oldies-5', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/03\/Demo-Oldies-5.mp3' },\n    { id: 'oldies-6', type: 'oldies', name: 'Oldies-6', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/oldies-6-with-bed.mp3' },\n    { id: 'oldies-7', type: 'oldies', name: 'Oldies-7', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/tj-radio-with-bed-oldies-7.mp3' },\n    { id: 'oldies-8', type: 'oldies', name: 'Oldies-8', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Oldies-8_Bea-new.mp3' },\n    { id: 'oldies-9', type: 'oldies', name: 'Oldies-9', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/oldies-9-with-bed.mp3' },\n    { id: 'oldies-10', type: 'oldies', name: 'Oldies-10', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Oldies10-_Oldies105_Bea.mp3' },\n    { id: 'oldies-11', type: 'oldies', name: 'Oldies-11', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/03\/Demo-Oldies-11.mp3' },\n    { id: 'oldies-12', type: 'oldies', name: 'Oldies-12', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/03\/Tempo-FM_01.mp3' },\n    { id: 'oldies-13', type: 'oldies', name: 'Oldies-13', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/03\/Tempo-FM_02.mp3' },\n    { id: 'oldies-14', type: 'oldies', name: 'Oldies-14', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Oldies-14_Bea-new.mp3' },\n    { id: 'oldies-15', type: 'oldies', name: 'Oldies-15', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Oldies15_Mo.mp3' },\n    { id: 'oldies-xmas', type: 'oldies', name: 'Oldies-Christmas', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/Radio-Melody-Bea-Redo.mp3' },\n    { id: 'epic-1', type: 'oldies', name: 'Epic 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/03\/John-Quincy-Epic-1.mp3' },\n    { id: 'epic-2', type: 'oldies', name: 'Epic 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/epic-2-demo.mp3' },\n    { id: 'epic-3', type: 'oldies', name: 'Epic 3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/epic-3-demo.mp3' },\n    { id: 'epic-4', type: 'oldies', name: 'Epic 4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/epic-4-demo.mp3' },\n    { id: 'epic-5', type: 'oldies', name: 'Epic 5', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/epic-5-demo.mp3' },\n    { id: 'epic-6', type: 'oldies', name: 'Epic 6', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/epic-6-demo.mp3' },\n    { id: 'epic-7', type: 'oldies', name: 'Epic 7', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/cinque-Ports-radio-Epic-7.mp3' },\n    { id: 'epic-8', type: 'oldies', name: 'Epic 8', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/epic-8-demo.mp3' },\n    { id: 'jazz-1', type: 'oldies', name: 'Jazz 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/BED-3-with-bed-.mp3' },\n    { id: 'jazz-2', type: 'oldies', name: 'Jazz 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/BED-8-with-bed-.mp3' },\n    { id: 'country-1', type: 'oldies', name: 'Country 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/WILD-COUNTRY_cut1J6_main-mix.mp3' },\n    { id: 'country-2', type: 'oldies', name: 'Country 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/WILD-COUNTRY_cut2J7_main-mix.mp3' },\n    { id: 'country-3', type: 'oldies', name: 'Country 3', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/WILD-COUNTRY_cut3J8_main-mix.mp3' },\n    { id: 'country-4', type: 'oldies', name: 'Country 4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/WILD-COUNTRY_cut4J5_main-mix.mp3' },\n    { id: 'kiss-1', type: 'oldies', name: 'Kiss 1', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-1.wav' },\n    { id: 'kiss-2', type: 'oldies', name: 'Kiss 2', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-2.mp3' },\n    { id: 'kiss-4', type: 'oldies', name: 'Kiss 4', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-4.wav' },\n    { id: 'kiss-5', type: 'oldies', name: 'Kiss 5', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-5.wav' },\n    { id: 'kiss-6', type: 'oldies', name: 'Kiss 6', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-6.mp3' },\n    { id: 'kiss-7', type: 'oldies', name: 'Kiss 7', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-7.mp3' },\n    { id: 'kiss-8', type: 'oldies', name: 'Kiss 8', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-8.mp3' },\n    { id: 'kiss-9', type: 'oldies', name: 'Kiss 9', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-9.mp3' },\n    { id: 'kiss-10', type: 'oldies', name: 'Kiss 10', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-10.wav' },\n    { id: 'kiss-11', type: 'oldies', name: 'Kiss 11', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-11.wav' },\n    { id: 'kiss-12', type: 'oldies', name: 'Kiss 12', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-12.mp3' },\n    { id: 'kiss-13', type: 'oldies', name: 'Kiss 13', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-13.wav' },\n    { id: 'kiss-14', type: 'oldies', name: 'Kiss 14', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-14.wav' },\n    { id: 'kiss-15', type: 'oldies', name: 'Kiss 15', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-15.wav' },\n    { id: 'kiss-16', type: 'oldies', name: 'Kiss 16', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-16.mp3' },\n    { id: 'kiss-17', type: 'oldies', name: 'Kiss 17', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-17.wav' },\n    { id: 'kiss-18', type: 'oldies', name: 'Kiss 18', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-18.mp3' },\n    { id: 'kiss-20', type: 'oldies', name: 'Kiss 20', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-20.mp3' },\n    { id: 'acapella', type: 'acapella', name: 'Acapella (no bed)', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Jingle-Demo-Aca.mp3' }\n  ];\n\n  const TYPE_LABELS = { ac: 'AC', chr: 'CHR', oldies: 'Oldies', acapella: 'Acap.' };\n  const TYPE_TMEPO = {\n    ac: 'AC \/ Hot AC',\n    chr: 'CHR',\n    oldies: 'Oldies & Country',\n    acapella: 'Acapella Only',\n    package: 'Full Jingle'\n  };\n\n  const PACKAGES = [\n    { id: 'tameside', name: 'Tameside Radio Hot AC', tmepoMatch: 'Tameside Radio Hot AC Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/06\/tameside-300x194.jpg', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/06\/tameside-demo.mp3' },\n    { id: 'giant', name: 'The Giant \u2013 AC', tmepoMatch: 'The Giant - AC Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Original123-300x196.jpg', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Packages_Giant.mp3' },\n    { id: 'nfrs', name: 'NFRS Radio \u2013 AC', tmepoMatch: 'NFRS Radio - AC Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Screenshot-2021-05-18-at-12.33.17-300x198.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Packages_NFRS.mp3' },\n    { id: 'smooth', name: 'Smooth AC', tmepoMatch: 'Smooth AC - AC Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Screenshot-2021-05-18-at-12.36.46-300x198.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Packages_Smooth-AC.mp3' },\n    { id: 'razor', name: 'Razor CHR', tmepoMatch: 'Razor CHR - CHR Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Screenshot-2021-05-18-at-12.37.41-300x198.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/Packages_Razor.mp3' },\n    { id: 'oldies-pkg-1', name: 'Oldies 1', tmepoMatch: 'Oldies 1 - Oldies Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/11\/jpeg-03-300x195.jpg', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/08\/Oldies-1019-Demo.mp3' },\n    { id: 'oldies-pkg-2', name: 'Oldies 2', tmepoMatch: 'Oldies 2 - Oldies Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/11\/jpeg-03-300x195.jpg', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/11\/Sung-Oldies-2.mp3' },\n    { id: 'mellow', name: 'Mellow Mix', tmepoMatch: 'Mellow Mix - Oldies Package', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/03\/Screenshot-2022-03-15-at-09.51.35-300x196.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/03\/Mellow-Demo.mp3' },\n    { id: 'epic-ac-1', name: 'Epic AC 1', tmepoMatch: 'Epic - AC 1', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/6456004ede9f4f989bbbf59b8c06a0c5.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/Epic_AC-1.mp3' },\n    { id: 'epic-wild', name: 'Epic Wild Country', tmepoMatch: 'Epic - Wild Country', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/6456004ede9f4f989bbbf59b8c06a0c5-2.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2023\/09\/Wild-Country-Package-Demo.mp3' },\n    { id: 'epic-ac-3', name: 'Epic AC 3', tmepoMatch: 'Epic - AC 3', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/6456004ede9f4f989bbbf59b8c06a0c5-3.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/Epic_AC-3.mp3' },\n    { id: 'epic-ac-4', name: 'Epic AC 4', tmepoMatch: 'Epic - AC 4', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/6456004ede9f4f989bbbf59b8c06a0c5-4.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/Epic-4-Pack.mp3' },\n    { id: 'epic-ac-5', name: 'Epic AC 5', tmepoMatch: 'Epic - AC 5', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/6456004ede9f4f989bbbf59b8c06a0c5-5.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/Epic-5-Package.mp3' },\n    { id: 'epic-country', name: 'Epic Country', tmepoMatch: 'Epic - Country', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/6456004ede9f4f989bbbf59b8c06a0c5-6.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/11\/Epic_Country-1.mp3' },\n    { id: 'kiss-pkg-1', name: 'Kiss Package 1', tmepoMatch: 'Kiss Package 1', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/03\/THE-BEST-200-x-200-px.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-Package_01.mp3' },\n    { id: 'kiss-pkg-2', name: 'Kiss Package 2', tmepoMatch: 'Kiss Package 2', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/03\/THE-BEST-200-x-200-px-2.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-Package_02.mp3' },\n    { id: 'kiss-pkg-3', name: 'Kiss Package 3', tmepoMatch: 'Kiss Package 3', image: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/03\/THE-BEST-200-x-200-px-3.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2025\/06\/Kiss-Package_03.mp3' }\n  ];\n\n  const SINGERS = [\n    { id: 'rosie', name: 'Rosie', creds: 'Bright, versatile, warm', delivery: '48 hours', singlePrice: 0, packagePrice: 0, avatar: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/i.30-300x300.jpg', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/05\/Rosie-Sung-Demo.mp3', tmepoMatch: 'Rosie' },\n    { id: 'bea', name: 'Bea', creds: 'BBC R1 &amp; R2, Sky, Virgin', delivery: '48 hours', singlePrice: 25, packagePrice: 100, avatar: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2020\/10\/Bighead-Caric-300x300.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/SHOWREEL_Bea.mp3', tmepoMatch: 'Bea' },\n    { id: 'mo', name: 'Mo', creds: 'The Beat 92.5, jingle co\\'s', delivery: '3\u20135 days', singlePrice: 25, packagePrice: 100, avatar: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2021\/05\/05-05-2021-fiverr-toon-300x300.png', demo: 'https:\/\/radiojingles24.com\/wp-content\/uploads\/2022\/02\/SHOWREEL-Mo-new2022.mp3', tmepoMatch: 'Mo' }\n  ];\n\n  const EXTRAS_SINGLE = [\n    { id: 'shotgun', name: 'Add Shotgun ID', desc: 'Quick punchy station ID cut. Per jingle.', price: 15, tmepoMatch: 'Shotgun' },\n    { id: 'ramp', name: 'Add Ramp\/Tag', desc: 'Talk-up ramp or tag cut. Per jingle.', price: 15, tmepoMatch: 'Ramp' }\n  ];\n  const EXTRAS_PACKAGE = [\n    { id: 'shotgun4', name: 'Add 4\u00d7 Shotgun IDs', desc: 'Four quick station ID cuts.', price: 49, tmepoMatch: '4x Shotgun' },\n    { id: 'ramp4', name: 'Add 4\u00d7 Ramp\/Tags', desc: 'Four talk-up ramps or tags.', price: 49, tmepoMatch: '4x Ramp' },\n    { id: 'fullstation', name: 'Upgrade to Full Station', desc: 'News, weather, travel + 6 sweepers. We\\'ll contact you for scripts.', price: 109, tmepoMatch: 'Full Station' }\n  ];\n\n  \/\/ ===== STATE =====\n  const state = {\n    screen: 'mode', mode: null, pack: [],\n    selectedPackage: null, packageLyrics: '',\n    singer: null, format: 'MP3', instructions: '',\n    extras: {}, filter: 'all',\n    currentAudio: null, currentPlayBtn: null\n  };\n\n  \/\/ ===== HELPERS =====\n  const $ = (s, c) => (c || document).querySelector(s);\n  const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));\n  const escapeHtml = s => { const d = document.createElement('div'); d.textContent = s == null ? '' : String(s); return d.innerHTML; };\n  const isCustom = () => state.mode === 'custom';\n  const isPackage = () => state.mode === 'package';\n  const getExtras = () => isPackage() ? EXTRAS_PACKAGE : EXTRAS_SINGLE;\n\n  \/\/ v3.9.0 multi-jingle bundle pricing. Base bundle by count; the PLUGIN is\n  \/\/ the source of truth at the cart, the wizard shows matching estimates.\n  const BUNDLE = { 1: 69, 2: 108, 3: 147, 4: 199 };\n  const EXTRA_PER_JINGLE = 49; \/\/ each jingle beyond 4\n  function bundleBase(count) {\n    if (count <= 0) return 0;\n    if (count >= 4) return BUNDLE[4] + (count - 4) * EXTRA_PER_JINGLE;\n    return BUNDLE[count];\n  }\n\n  \/\/ Per-jingle premium relative to \u00a369 base: singer upcharge + extras +\n  \/\/ bed discount (e.g. Kiss -\u00a310). This is what each jingle sends as its\n  \/\/ rj24_price (69 + premium), which the plugin uses for the on-top amount.\n  function jinglePremium(j) {\n    let p = 0;\n    if (j && j.bed && j.bed.discount) p += j.bed.discount; \/\/ negative for Kiss\n    return p;\n  }\n  function calcSingerCost() {\n    if (!state.singer) return 0;\n    if (isPackage()) return state.singer.packagePrice;\n    return state.singer.singlePrice * Math.max(1, state.pack.length);\n  }\n  function calcExtrasCost() {\n    let total = 0;\n    const extras = getExtras();\n    extras.forEach(ex => {\n      if (state.extras[ex.id]) {\n        if (isCustom() && (ex.id === 'shotgun' || ex.id === 'ramp')) {\n          total += ex.price * Math.max(1, state.pack.length);\n        } else {\n          total += ex.price;\n        }\n      }\n    });\n    return total;\n  }\n  function calcTotal() {\n    let total = 0;\n    if (isCustom()) {\n      const count = state.pack.length;\n      if (count === 0) return 0;\n      total = bundleBase(count);\n      \/\/ bed-level discounts (Kiss) across the pack\n      state.pack.forEach(j => { total += jinglePremium(j); });\n    } else if (isPackage()) {\n      total = state.selectedPackage ? 199 : 0;\n    }\n    total += calcSingerCost();\n    total += calcExtrasCost();\n    return Math.max(0, total);\n  }\n  function calcSaving() {\n    if (!isCustom()) return 0;\n    const count = state.pack.length;\n    if (count < 2) return 0;\n    const full = 69 * count;\n    return Math.max(0, full - bundleBase(count));\n  }\n  function getTierMessage() {\n    const count = state.pack.length;\n    if (count === 0) return null;\n    if (count === 1) return { msg: 'Add 1 more \u2014 2 jingles for \u00a3108 (save \u00a330)', unlocked: false };\n    if (count === 2) return { msg: 'Add 1 more \u2014 3 jingles for \u00a3147 (save \u00a360)', unlocked: false };\n    if (count === 3) return { msg: '\u2605 1 more \u2014 4 jingles for \u00a3199 (save \u00a377)', unlocked: false };\n    return { msg: '\u2713 Bundle price unlocked \u2014 \u00a3199 for 4, then \u00a349 each', unlocked: true };\n  }\n\n  function showToast(msg, type) {\n    const t = $('#rjsw-toast');\n    t.textContent = msg;\n    t.className = 'rjsw-toast show' + (type ? ' ' + type : '');\n    clearTimeout(t._timer);\n    t._timer = setTimeout(() => t.classList.remove('show'), 3500);\n  }\n  function stopAudio() {\n    if (state.currentAudio) { try { state.currentAudio.pause(); } catch(e){} state.currentAudio = null; }\n    if (state.currentPlayBtn) {\n      state.currentPlayBtn.classList.remove('playing');\n      state.currentPlayBtn.textContent = state.currentPlayBtn.dataset.origText || '\u25b6';\n      state.currentPlayBtn = null;\n    }\n  }\n  function attachAudio(btn, url) {\n    btn.dataset.origText = btn.textContent;\n    btn.addEventListener('click', e => {\n      e.stopPropagation();\n      const wasPlaying = btn === state.currentPlayBtn && state.currentAudio && !state.currentAudio.paused;\n      stopAudio();\n      if (wasPlaying) return;\n      const a = new Audio(url);\n      a.play().catch(() => showToast('Could not play demo', 'error'));\n      state.currentAudio = a;\n      state.currentPlayBtn = btn;\n      btn.classList.add('playing');\n      btn.textContent = '\u23f8';\n      a.addEventListener('ended', () => {\n        btn.classList.remove('playing');\n        btn.textContent = btn.dataset.origText || '\u25b6';\n        state.currentAudio = null; state.currentPlayBtn = null;\n      });\n    });\n  }\n  function scrollUp() {\n    setTimeout(() => {\n      const top = $('#rj-sung-wizard').getBoundingClientRect().top + window.pageYOffset - 20;\n      window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });\n    }, 50);\n  }\n  function showScreen(name) {\n    state.screen = name;\n    $$('.rjsw-screen').forEach(s => s.classList.remove('active'));\n    $('#rjsw-screen-' + name).classList.add('active');\n    updateAllCTAs();\n    scrollUp();\n  }\n  function allLyricsFilled() {\n    if (isCustom()) return state.pack.every(j => j.lyrics && j.lyrics.trim().length > 0);\n    return state.packageLyrics.trim().length > 0;\n  }\n  function updateAllCTAs() {\n    const total = calcTotal();\n    const save = calcSaving();\n    const totalStr = '\u00a3' + total.toFixed(2);\n    const saveStr = save > 0 ? '\u2605 Save \u00a3' + save : '';\n    ['browse','package','singer','lyrics','fmt','review'].forEach(scr => {\n      const tEl = document.getElementById('rjsw-cta-total-' + scr);\n      const sEl = document.getElementById('rjsw-cta-save-' + scr);\n      if (tEl) tEl.textContent = totalStr;\n      if (sEl) sEl.textContent = saveStr;\n    });\n    const browseBtn = $('#rjsw-cta-btn-browse'); if (browseBtn) browseBtn.disabled = state.pack.length === 0;\n    const pkgBtn = $('#rjsw-cta-btn-package'); if (pkgBtn) pkgBtn.disabled = !state.selectedPackage;\n    const singerBtn = $('#rjsw-cta-btn-singer'); if (singerBtn) singerBtn.disabled = !state.singer;\n    const lyricsBtn = $('#rjsw-cta-btn-lyrics'); if (lyricsBtn) lyricsBtn.disabled = !allLyricsFilled();\n  }\n\n  function renderPackBar() {\n    const bar = $('#rjsw-pack-bar');\n    if (state.pack.length === 0) {\n      bar.className = 'rjsw-pack-bar empty';\n      bar.innerHTML = 'Your pack is empty. Tap + on any bed below to start.';\n      return;\n    }\n    bar.className = 'rjsw-pack-bar';\n    let html = '<span class=\"rjsw-pack-bar-label\">' + state.pack.length + ' jingle' + (state.pack.length === 1 ? '' : 's') + ':<\/span>';\n    state.pack.forEach((j, i) => {\n      html += '<span class=\"rjsw-pack-chip\">' + escapeHtml(j.bed.name) +\n        '<button type=\"button\" class=\"rjsw-pack-chip-remove\" data-idx=\"'+i+'\" title=\"Remove\">\u2715<\/button><\/span>';\n    });\n    const tier = getTierMessage();\n    if (tier) html += '<span class=\"rjsw-pack-tier-msg ' + (tier.unlocked ? 'unlocked' : '') + '\">' + tier.msg + '<\/span>';\n    bar.innerHTML = html;\n    $$('.rjsw-pack-chip-remove', bar).forEach(btn => {\n      btn.addEventListener('click', () => {\n        state.pack.splice(parseInt(btn.dataset.idx, 10), 1);\n        renderPackBar(); renderBedGrid(); updateAllCTAs();\n      });\n    });\n  }\n  function renderBedGrid() {\n    const grid = $('#rjsw-bed-grid');\n    const beds = state.filter === 'all' ? BEDS : BEDS.filter(b => b.type === state.filter);\n    grid.innerHTML = beds.map(b => {\n      const count = state.pack.filter(j => j.bed.id === b.id).length;\n      const inPack = count > 0;\n      const typeTag = TYPE_LABELS[b.type] || b.type.toUpperCase();\n      const discount = b.discount ? '<span class=\"rjsw-bed-discount\">SAVE \u00a3' + Math.abs(b.discount) + '<\/span>' : '';\n      const countBadge = count > 1 ? '<div class=\"rjsw-bed-count\">' + count + '<\/div>' : '';\n      return '<div class=\"rjsw-bed-card ' + (inPack ? 'in-pack' : '') + '\" data-id=\"'+b.id+'\">' +\n        '<div class=\"rjsw-bed-type-tag\">' + typeTag + '<\/div>' + discount +\n        '<div class=\"rjsw-bed-card-name\">' + escapeHtml(b.name) + '<\/div>' +\n        '<div class=\"rjsw-bed-actions\">' +\n          '<button type=\"button\" class=\"rjsw-bed-btn play\" data-demo=\"'+b.demo+'\">\u25b6<\/button>' +\n          (inPack ?\n            '<button type=\"button\" class=\"rjsw-bed-btn remove\" data-id=\"'+b.id+'\">\u2212<\/button>' +\n            '<button type=\"button\" class=\"rjsw-bed-btn add\" data-id=\"'+b.id+'\">+<\/button>' :\n            '<button type=\"button\" class=\"rjsw-bed-btn add\" data-id=\"'+b.id+'\">+<\/button>'\n          ) +\n        '<\/div>' + countBadge +\n      '<\/div>';\n    }).join('');\n    $$('.rjsw-bed-btn.play', grid).forEach(btn => attachAudio(btn, btn.dataset.demo));\n    $$('.rjsw-bed-btn.add', grid).forEach(btn => {\n      btn.addEventListener('click', e => {\n        e.stopPropagation();\n        const bed = BEDS.find(b => b.id === btn.dataset.id);\n        state.pack.push({ bed: bed, lyrics: '' });\n        renderPackBar(); renderBedGrid(); updateAllCTAs();\n        showToast('\"' + bed.name + '\" added \u2014 ' + state.pack.length + ' in pack', 'success');\n      });\n    });\n    $$('.rjsw-bed-btn.remove', grid).forEach(btn => {\n      btn.addEventListener('click', e => {\n        e.stopPropagation();\n        const id = btn.dataset.id;\n        for (let i = state.pack.length - 1; i >= 0; i--) {\n          if (state.pack[i].bed.id === id) { state.pack.splice(i, 1); break; }\n        }\n        renderPackBar(); renderBedGrid(); updateAllCTAs();\n      });\n    });\n  }\n  function renderPackages() {\n    const grid = $('#rjsw-pkg-grid');\n    grid.innerHTML = PACKAGES.map(p => {\n      const sel = state.selectedPackage && state.selectedPackage.id === p.id;\n      return '<div class=\"rjsw-pkg-card ' + (sel ? 'selected' : '') + '\" data-id=\"'+p.id+'\">' +\n        '<img decoding=\"async\" class=\"rjsw-pkg-thumb\" src=\"'+p.image+'\" alt=\"'+escapeHtml(p.name)+'\" loading=\"lazy\">' +\n        '<div class=\"rjsw-pkg-name\">' + escapeHtml(p.name) + '<\/div>' +\n        '<button type=\"button\" class=\"rjsw-pkg-listen\" data-demo=\"'+p.demo+'\">\u25b6 Listen<\/button>' +\n      '<\/div>';\n    }).join('');\n    $$('.rjsw-pkg-card', grid).forEach(card => {\n      card.addEventListener('click', e => {\n        if (e.target.closest('.rjsw-pkg-listen')) return;\n        state.selectedPackage = PACKAGES.find(p => p.id === card.dataset.id);\n        renderPackages(); updateAllCTAs();\n      });\n    });\n    $$('.rjsw-pkg-listen', grid).forEach(btn => attachAudio(btn, btn.dataset.demo));\n  }\n  function renderSingers() {\n    const grid = $('#rjsw-singers');\n    const pkg = isPackage();\n    const count = isCustom() ? Math.max(1, state.pack.length) : 1;\n    $('#rjsw-singer-sub').innerHTML = pkg ?\n      'One singer for your Full Package. Rosie included, Bea\/Mo <b>+\u00a3100<\/b> for the package.' :\n      'One singer across all ' + count + ' jingle' + (count === 1 ? '' : 's') + '. Rosie included, Bea\/Mo <b>+\u00a325 per jingle<\/b>.';\n    grid.innerHTML = SINGERS.map(s => {\n      const sel = state.singer && state.singer.id === s.id;\n      const unitPrice = pkg ? s.packagePrice : s.singlePrice;\n      let priceHtml;\n      if (unitPrice === 0) priceHtml = '<div class=\"rjsw-singer-price free\">Included<\/div>';\n      else if (pkg) priceHtml = '<div class=\"rjsw-singer-price\">+\u00a3' + unitPrice + '<\/div>';\n      else priceHtml = '<div class=\"rjsw-singer-price\">+\u00a3' + unitPrice + (count > 1 ? ' \u00d7 ' + count + ' = \u00a3' + (unitPrice * count) : '') + '<\/div>';\n      return '<div class=\"rjsw-singer ' + (sel ? 'selected' : '') + '\" data-id=\"'+s.id+'\">' +\n        '<div class=\"rjsw-singer-avatar-wrap\">' +\n          '<img decoding=\"async\" class=\"rjsw-singer-avatar\" src=\"'+s.avatar+'\" alt=\"'+escapeHtml(s.name)+'\" loading=\"lazy\">' +\n          '<button class=\"rjsw-singer-play\" type=\"button\" data-demo=\"'+s.demo+'\">\u25b6<\/button>' +\n        '<\/div>' +\n        '<div class=\"rjsw-singer-name\">'+escapeHtml(s.name)+'<\/div>' +\n        '<div class=\"rjsw-singer-creds\">'+s.creds+'<\/div>' +\n        '<div class=\"rjsw-singer-delivery\">\u23f1 '+s.delivery+'<\/div>' +\n        priceHtml +\n      '<\/div>';\n    }).join('');\n    $$('.rjsw-singer', grid).forEach(el => {\n      el.addEventListener('click', e => {\n        if (e.target.closest('.rjsw-singer-play')) return;\n        state.singer = SINGERS.find(x => x.id === el.dataset.id);\n        renderSingers(); updateAllCTAs();\n      });\n    });\n    $$('.rjsw-singer-play', grid).forEach(btn => attachAudio(btn, btn.dataset.demo));\n  }\n  function renderLyrics() {\n    const container = $('#rjsw-lyrics-blocks');\n    if (isPackage()) {\n      $('#rjsw-lyrics-sub').innerHTML = 'For Full Packages, number your lyrics 1\u20134 (one line per cut).';\n      container.innerHTML = '<div class=\"rjsw-lyrics-block\">' +\n        '<div class=\"rjsw-lyrics-block-head\">' +\n          '<div class=\"rjsw-lyrics-block-num\">\ud83d\udcdd<\/div>' +\n          '<div class=\"rjsw-lyrics-block-name\">' + escapeHtml(state.selectedPackage.name) + ' lyrics<\/div>' +\n        '<\/div>' +\n        '<textarea class=\"rjsw-textarea\" id=\"rjsw-pkg-lyrics\" placeholder=\"1. Your station name\\n2. Slogan or tag\\n3. Frequency\\n4. Final shout\">' + escapeHtml(state.packageLyrics) + '<\/textarea>' +\n        '<p class=\"rjsw-hint\">Keep each line to 5\u20138 words for best quality.<\/p>' +\n      '<\/div>';\n      $('#rjsw-pkg-lyrics').addEventListener('input', e => {\n        state.packageLyrics = e.target.value; updateAllCTAs();\n      });\n    } else {\n      $('#rjsw-lyrics-sub').textContent = 'One textbox per jingle. Keep each to 5\u20138 words.';\n      container.innerHTML = state.pack.map((j, i) => {\n        return '<div class=\"rjsw-lyrics-block\">' +\n          '<div class=\"rjsw-lyrics-block-head\">' +\n            '<div class=\"rjsw-lyrics-block-num\">' + (i + 1) + '<\/div>' +\n            '<div class=\"rjsw-lyrics-block-name\">' + escapeHtml(j.bed.name) + '<\/div>' +\n            '<div class=\"rjsw-lyrics-block-type\">' + (TYPE_LABELS[j.bed.type] || '') + '<\/div>' +\n          '<\/div>' +\n          '<textarea class=\"rjsw-textarea\" data-idx=\"'+i+'\" placeholder=\"e.g. KIX 99 \u2014 the hits that move you\">' + escapeHtml(j.lyrics) + '<\/textarea>' +\n        '<\/div>';\n      }).join('');\n      $$('textarea[data-idx]', container).forEach(ta => {\n        ta.addEventListener('input', e => {\n          state.pack[parseInt(e.target.dataset.idx, 10)].lyrics = e.target.value;\n          updateAllCTAs();\n        });\n      });\n    }\n    $('#rjsw-instructions').value = state.instructions;\n  }\n  function renderExtras() {\n    const container = $('#rjsw-extras-container');\n    const items = getExtras();\n    const count = isCustom() ? Math.max(1, state.pack.length) : 1;\n    const perJingle = isCustom();\n    container.innerHTML = '<div style=\"font-size:14px;font-weight:700;color:var(--rj-ink);margin-bottom:10px;\">Optional extras<\/div>' +\n      '<div class=\"rjsw-extras\">' + items.map(ex => {\n        const sel = !!state.extras[ex.id];\n        const isPerJ = perJingle && (ex.id === 'shotgun' || ex.id === 'ramp');\n        const priceLabel = (isPerJ && count > 1) ? '+\u00a3' + ex.price + ' \u00d7 ' + count + ' = \u00a3' + (ex.price * count) : '+\u00a3' + ex.price;\n        return '<div class=\"rjsw-extra '+(sel?'selected':'')+'\" data-id=\"'+ex.id+'\">' +\n          '<div class=\"rjsw-extra-check\">'+(sel?'\u2713':'')+'<\/div>' +\n          '<div class=\"rjsw-extra-info\">' +\n            '<div class=\"rjsw-extra-name\">'+escapeHtml(ex.name)+'<\/div>' +\n            '<div class=\"rjsw-extra-desc\">'+escapeHtml(ex.desc)+'<\/div>' +\n          '<\/div>' +\n          '<div class=\"rjsw-extra-price\">'+priceLabel+'<\/div>' +\n        '<\/div>';\n      }).join('') + '<\/div>';\n    $$('.rjsw-extra', container).forEach(el => {\n      el.addEventListener('click', () => {\n        state.extras[el.dataset.id] = !state.extras[el.dataset.id];\n        renderExtras(); updateAllCTAs();\n      });\n    });\n  }\n  function renderReview() {\n    const container = $('#rjsw-review-container');\n    const extras = getExtras();\n    const selExtras = extras.filter(ex => state.extras[ex.id]);\n    let html = '<div class=\"rjsw-review-card\">';\n    if (isCustom()) {\n      html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Pack<\/span><span class=\"rjsw-review-value\">' +\n        state.pack.length + ' jingle' + (state.pack.length === 1 ? '' : 's') + '<\/span><\/div>';\n      state.pack.forEach((j, i) => {\n        html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">#' + (i+1) + ' ' + (TYPE_LABELS[j.bed.type] || '') + '<\/span><span class=\"rjsw-review-value\">' +\n          escapeHtml(j.bed.name) + ' <span class=\"rjsw-review-value-muted\">\"' + escapeHtml((j.lyrics || '').substring(0, 40)) + (j.lyrics && j.lyrics.length > 40 ? '\u2026' : '') + '\"<\/span><\/span><\/div>';\n      });\n    } else {\n      html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Package<\/span><span class=\"rjsw-review-value\">' +\n        escapeHtml(state.selectedPackage.name) + ' <span class=\"rjsw-review-value-muted\">\u00a3199<\/span><\/span><\/div>';\n      html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Lyrics<\/span><span class=\"rjsw-review-value\" style=\"font-style:italic\">' + escapeHtml(state.packageLyrics) + '<\/span><\/div>';\n    }\n    html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Singer<\/span><span class=\"rjsw-review-value\">' +\n      escapeHtml(state.singer.name) + ' <span class=\"rjsw-review-value-muted\">' + state.singer.delivery + '<\/span><\/span><\/div>';\n    if (state.instructions) html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Notes<\/span><span class=\"rjsw-review-value\">' + escapeHtml(state.instructions) + '<\/span><\/div>';\n    html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Format<\/span><span class=\"rjsw-review-value\">' + state.format + '<\/span><\/div>';\n    if (selExtras.length) html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Extras<\/span><span class=\"rjsw-review-value\">' + selExtras.map(ex => escapeHtml(ex.name)).join(' \u00b7 ') + '<\/span><\/div>';\n    html += '<div class=\"rjsw-review-row\"><span class=\"rjsw-review-label\">Total<\/span><span class=\"rjsw-review-value\" style=\"font-size:18px;color:var(--rj-pink-dark);font-weight:800;\">\u00a3' + calcTotal().toFixed(2) + '<\/span><\/div>';\n    html += '<\/div>';\n    if (isCustom() && calcSaving() > 0) html += '<div class=\"rjsw-review-tier\">\u2605 You save \u00a3' + calcSaving() + ' with the bundle price<\/div>';\n    container.innerHTML = html;\n  }\n\n  \/\/ ==========================================================\n  \/\/ TMEPO \u2014 DIRECT COPY from spoken wizard, plus sung-specific extensions\n  \/\/ ==========================================================\n\n  \/**\n   * Returns true if the element (or its associated label, for radio\/checkbox)\n   * is NOT hidden by display:none on an ancestor.\n   *\n   * TMEPO ALWAYS sets display:none on the native <input> and <span.tc-input-wrap>\n   * because it renders custom styled buttons. So we must check via the LABEL,\n   * which is the visual element TMEPO toggles.\n   *\/\n  function isVisible(el) {\n    if (!el) return false;\n    \/\/ For radio\/checkbox inputs, the input itself is always display:none in TMEPO.\n    \/\/ We check via its associated label instead.\n    let target = el;\n    if (el.tagName === 'INPUT' && (el.type === 'radio' || el.type === 'checkbox')) {\n      const lblFor = el.id ? document.querySelector('label[for=\"' + el.id + '\"]') : null;\n      const lblParent = el.closest('label');\n      target = lblFor || lblParent || el.parentElement;\n    }\n    let cur = target;\n    while (cur && cur.tagName !== 'BODY') {\n      \/\/ Stop walking up when we hit our hidden TMEPO container wrapper\n      if (cur.classList && cur.classList.contains('tm-extra-product-options-fields')) {\n        return true;\n      }\n      const cs = getComputedStyle(cur);\n      if (cs.display === 'none') return false;\n      cur = cur.parentElement;\n    }\n    return true;\n  }\n\n  function findTMEPOInput(sectionHint, optionHint, requireVisible) {\n    \/\/ Search all heading levels (sung product may use h3\/h4 instead of h2)\n    const headings = document.querySelectorAll('.tm-extra-product-options-fields h2, .tm-extra-product-options-fields h3, .tm-extra-product-options-fields h4');\n    \/\/ If requireVisible (default true), collect all matches first, then return the visible one\n    const matches = [];\n    for (const heading of headings) {\n      if (heading.textContent.toLowerCase().indexOf(sectionHint.toLowerCase()) === -1) continue;\n      \/\/ Skip headings that are themselves inside hidden sections\n      if (requireVisible !== false && !isVisible(heading)) continue;\n      let section = heading.parentElement;\n      while (section && section.tagName !== 'BODY') {\n        const inputs = section.querySelectorAll('input[type=\"checkbox\"], input[type=\"radio\"]');\n        if (inputs.length > 0) {\n          for (const inp of inputs) {\n            const lblFor = inp.id ? document.querySelector('label[for=\"' + inp.id + '\"]') : null;\n            const lblParent = inp.closest('label');\n            const lblText = ((lblFor || lblParent || {}).textContent || '').trim().toLowerCase();\n            if (lblText.indexOf(optionHint.toLowerCase()) === -1) continue;\n            \/\/ Visibility filter\n            if (requireVisible !== false && !isVisible(inp)) continue;\n            matches.push(inp);\n          }\n          break;\n        }\n        section = section.parentElement;\n      }\n    }\n    return matches[0] || null;\n  }\n\n  function findTMEPOTextarea(hint, requireVisible) {\n    const allLabels = document.querySelectorAll('.tm-extra-product-options-fields label');\n    for (const lbl of allLabels) {\n      if (lbl.textContent.toLowerCase().indexOf(hint.toLowerCase()) === -1) continue;\n      if (requireVisible !== false && !isVisible(lbl)) continue;\n      const id = lbl.getAttribute('for');\n      if (id) {\n        const ta = document.getElementById(id);\n        if (ta && ta.tagName === 'TEXTAREA' && (requireVisible === false || isVisible(ta))) return ta;\n      }\n      const sibling = lbl.parentElement.querySelector('textarea');\n      if (sibling && (requireVisible === false || isVisible(sibling))) return sibling;\n    }\n    \/\/ Last-resort fallback: any visible textarea by position\n    const textareas = Array.from(document.querySelectorAll('.tm-extra-product-options-fields textarea'))\n      .filter(ta => requireVisible === false || isVisible(ta));\n    if (hint === 'script' || hint === 'type') return textareas[0] || null;\n    if (hint === 'instruction') return textareas[1] || null;\n    return null;\n  }\n\n  function fireChange(inp) {\n    inp.dispatchEvent(new Event('change', { bubbles: true }));\n    if (window.jQuery) try { window.jQuery(inp).trigger('change'); } catch (e) {}\n  }\n\n  function findBedInput(bedName) {\n    const inputs = document.querySelectorAll('.tm-extra-product-options-fields input[type=\"checkbox\"], .tm-extra-product-options-fields input[type=\"radio\"]');\n    for (const inp of inputs) {\n      if (!isVisible(inp)) continue;\n      const lblFor = inp.id ? document.querySelector('label[for=\"' + inp.id + '\"]') : null;\n      const lblText = lblFor ? lblFor.textContent.trim() : '';\n      if (lblText.toLowerCase().indexOf(bedName.toLowerCase()) === 0) {\n        return inp;\n      }\n    }\n    return null;\n  }\n\n  \/**\n   * fillTMEPO for ONE jingle. Returns Promise<missing[]>.\n   * Async because TMEPO conditional UI needs time to render after type selection.\n   *\/\n  async function fillTMEPO(typeKey, bedName, lyrics, packageTmepoMatch) {\n    const missing = [];\n    const tmepoTypeLabel = TYPE_TMEPO[typeKey];\n\n    \/\/ 1. Type \u2014 click and wait for conditional UI\n    if (tmepoTypeLabel) {\n      const t = findTMEPOInput('type of sung jingle', tmepoTypeLabel);\n      if (t) {\n        if (!t.checked) { t.checked = true; fireChange(t); }\n      } else {\n        missing.push('Type: ' + tmepoTypeLabel);\n      }\n    }\n    \/\/ Wait for TMEPO to render the bed\/package section based on type\n    await wait(800);\n\n    \/\/ 2. Bed or Package\n    if (typeKey === 'package' && packageTmepoMatch) {\n      const p = findTMEPOInput('pick a package', packageTmepoMatch);\n      if (p) {\n        if (!p.checked) { p.checked = true; fireChange(p); }\n      } else {\n        missing.push('Package: ' + packageTmepoMatch);\n      }\n      await wait(400);\n    } else if (typeKey !== 'acapella' && bedName) {\n      const b = findBedInput(bedName);\n      if (b) {\n        if (!b.checked) { b.checked = true; fireChange(b); }\n      } else {\n        missing.push('Bed: ' + bedName);\n      }\n      await wait(400);\n    }\n\n    \/\/ 3. Singer\n    if (state.singer) {\n      const s = findTMEPOInput('singer', state.singer.tmepoMatch);\n      if (s) {\n        if (!s.checked) { s.checked = true; fireChange(s); }\n      } else {\n        missing.push('Singer: ' + state.singer.name);\n      }\n    }\n    await wait(200);\n\n    \/\/ 4. Script\n    const script = findTMEPOTextarea('script') || findTMEPOTextarea('type in your');\n    if (script) {\n      script.value = lyrics;\n      script.dispatchEvent(new Event('input', { bubbles: true }));\n      fireChange(script);\n    }\n\n    \/\/ 5. Instructions\n    const instr = findTMEPOTextarea('instruction');\n    if (instr) {\n      instr.value = state.instructions;\n      instr.dispatchEvent(new Event('input', { bubbles: true }));\n      fireChange(instr);\n    }\n\n    \/\/ 6. Format\n    const fmt = findTMEPOInput('format', state.format);\n    if (fmt) {\n      if (!fmt.checked) { fmt.checked = true; fireChange(fmt); }\n    }\n\n    \/\/ 7. Extras\n    const extras = getExtras();\n    extras.forEach(ex => {\n      const inp = findTMEPOInput('extra', ex.tmepoMatch);\n      if (!inp) return;\n      const desired = !!state.extras[ex.id];\n      if (inp.checked !== desired) { inp.checked = desired; fireChange(inp); }\n    });\n\n    \/\/ Final settle before submit\n    await wait(400);\n    return missing;\n  }\n\n  function wait(ms) { return new Promise(r => setTimeout(r, ms)); }\n\n  \/**\n   * Wipe all TMEPO field state. Called between jingles in a multi-jingle\n   * pack so that each submission starts from a clean slate \u2014 otherwise\n   * the previous jingle's hidden-but-checked radios get smuggled into the\n   * next POST and either cause WC to dedupe or produce garbled options.\n   *\/\n  function resetTMEPO() {\n    \/\/ Uncheck every TMEPO radio and checkbox\n    const inputs = document.querySelectorAll(\n      '.tm-extra-product-options-fields input[type=\"radio\"], ' +\n      '.tm-extra-product-options-fields input[type=\"checkbox\"]'\n    );\n    inputs.forEach(inp => {\n      if (inp.checked) {\n        inp.checked = false;\n        try { inp.dispatchEvent(new Event('change', { bubbles: true })); } catch(e) {}\n        if (window.jQuery) try { window.jQuery(inp).trigger('change'); } catch(e) {}\n      }\n    });\n\n    \/\/ Blank every TMEPO textarea\n    const textareas = document.querySelectorAll('.tm-extra-product-options-fields textarea');\n    textareas.forEach(ta => {\n      if (ta.value !== '') {\n        ta.value = '';\n        try { ta.dispatchEvent(new Event('input', { bubbles: true })); } catch(e) {}\n        try { ta.dispatchEvent(new Event('change', { bubbles: true })); } catch(e) {}\n        if (window.jQuery) try { window.jQuery(ta).trigger('change'); } catch(e) {}\n      }\n    });\n  }\n\n  \/\/ ==========================================================\n  \/\/ CART SUBMISSION \u2014 EXACT spoken pattern\n  \/\/ Multi-jingle: sessionStorage carries pack across page reloads.\n  \/\/ Each page load = one jingle add = one fresh TMEPO state.\n  \/\/ ==========================================================\n\n  \/\/ Multi-jingle queue\/sessionStorage machinery removed in v3.8.0.\n  \/\/ Each wizard run now adds exactly ONE jingle, so no cross-reload state\n  \/\/ is needed. Only the error key remains, for surfacing add failures.\n  \/\/ ==========================================================\n\n  const SS_ERROR_KEY = 'rj24_last_error';\n\n  function recordError(stage, idx, details) {\n    try {\n      sessionStorage.setItem(SS_ERROR_KEY, JSON.stringify({\n        stage: stage, jingle: idx, details: details, time: Date.now()\n      }));\n    } catch(e) {}\n  }\n  function readRecentError() {\n    try {\n      const raw = sessionStorage.getItem(SS_ERROR_KEY);\n      if (!raw) return null;\n      const err = JSON.parse(raw);\n      if (Date.now() - err.time > 300000) { sessionStorage.removeItem(SS_ERROR_KEY); return null; }\n      return err;\n    } catch(e) { return null; }\n  }\n  function clearError() { sessionStorage.removeItem(SS_ERROR_KEY); }\n\n  \/\/ ============================================================\n  \/\/ v3.8.1 \u2014 SINGLE-JINGLE ADD via NATIVE form submit -> \/cart\/\n  \/\/ ============================================================\n  \/\/ We add exactly ONE jingle per wizard run via WooCommerce's native\n  \/\/ add-to-cart (real button click -> server processes -> redirect to\n  \/\/ \/cart\/). This is the most reliable add path on the site and the only\n  \/\/ one proven to commit the item. wc-ajax and the fragments endpoint are\n  \/\/ broken here (return malformed HTML), so all AJAX\/verify code was removed.\n  \/\/ ============================================================\n  \/\/ ============================================================\n  \/\/ v3.9.0 \u2014 MULTI-JINGLE add via the RJ24 plugin AJAX action.\n  \/\/ ============================================================\n  \/\/ Each jingle is posted to the plugin's rj24_add_jingle endpoint\n  \/\/ (admin-ajax.php). One call = one cart line. No TMEPO, no wc-ajax, no\n  \/\/ iframe. The plugin returns clean JSON {success, cart_count, cart_url}.\n  \/\/ We submit the pack sequentially, verifying cart_count grows each time,\n  \/\/ then redirect to the cart. Bundle pricing is applied server-side by\n  \/\/ the plugin; the wizard's totals are estimates that will match.\n  \/\/ ============================================================\n\n  \/\/ Build the per-jingle payload list from current state.\n  function buildJingleQueue() {\n    const singerName = state.singer ? state.singer.name : '';\n    const extrasList = getExtras().filter(ex => state.extras[ex.id]).map(ex => ex.name).join(', ');\n\n    if (isPackage()) {\n      \/\/ Full Station Package = one line. Price is the \u00a3199 package + singer pkg price + extras.\n      const price = calcTotal(); \/\/ whole package total (single item)\n      return [{\n        type: 'Full Station Package',\n        bed: state.selectedPackage.name,\n        singer: singerName,\n        lyrics: state.packageLyrics,\n        format: state.format,\n        extras: extrasList,\n        instructions: state.instructions,\n        price: price\n      }];\n    }\n\n    \/\/ Custom pack: one payload per jingle. Each jingle's price is the\n    \/\/ \u00a369 base + that bed's discount + singer single price + per-jingle extras.\n    \/\/ (The plugin re-derives bundle pricing across the whole cart; we send a\n    \/\/ per-line price so singer\/extras\/bed-discount premium is captured.)\n    const singerPer = state.singer ? state.singer.singlePrice : 0;\n    let perJingleExtras = 0;\n    getExtras().forEach(ex => {\n      if (state.extras[ex.id] && (ex.id === 'shotgun' || ex.id === 'ramp')) perJingleExtras += ex.price;\n    });\n    return state.pack.map(j => {\n      const bedDisc = (j.bed && j.bed.discount) ? j.bed.discount : 0;\n      const price = 69 + bedDisc + singerPer + perJingleExtras;\n      return {\n        type: TYPE_TMEPO[j.bed.type] || j.bed.type,\n        bed: j.bed.name,\n        singer: singerName,\n        lyrics: j.lyrics,\n        format: state.format,\n        extras: extrasList,\n        instructions: state.instructions,\n        price: price\n      };\n    });\n  }\n\n  \/\/ POST one jingle to the plugin endpoint. Resolves with parsed JSON.\n  async function postJingle(payload) {\n    const cfg = window.RJ24_CFG || {};\n    const body = new URLSearchParams();\n    body.set('action', 'rj24_add_jingle');\n    body.set('nonce', cfg.nonce || '');\n    body.set('rj24_is_sung', '1');\n    if (cfg.productId) body.set('rj24_product_id', String(cfg.productId));\n    body.set('rj24_type', payload.type || '');\n    body.set('rj24_bed', payload.bed || '');\n    body.set('rj24_singer', payload.singer || '');\n    body.set('rj24_lyrics', payload.lyrics || '');\n    body.set('rj24_format', payload.format || 'MP3');\n    body.set('rj24_extras', payload.extras || '');\n    body.set('rj24_instructions', payload.instructions || '');\n    body.set('rj24_price', String(payload.price != null ? payload.price : 69));\n\n    const resp = await fetch(cfg.ajaxUrl, {\n      method: 'POST',\n      credentials: 'same-origin',\n      headers: { 'Content-Type': 'application\/x-www-form-urlencoded' },\n      body: body.toString()\n    });\n    if (!resp.ok) throw new Error('Server returned HTTP ' + resp.status);\n    const data = await resp.json().catch(() => null);\n    if (!data) throw new Error('Unexpected response from server');\n    if (!data.success) throw new Error(data.message || 'Add to cart was rejected');\n    return data;\n  }\n\n  async function startCartFlow() {\n    const cfg = window.RJ24_CFG || {};\n    if (!cfg.ajaxUrl || !cfg.nonce) {\n      return showCartError('The cart isn\\'t ready (configuration missing). Please refresh the page and try again.');\n    }\n\n    const queue = buildJingleQueue();\n    if (queue.length === 0) {\n      return showCartError('Nothing to add. Please pick at least one jingle.');\n    }\n\n    showCartModal(0, queue.length);\n\n    let lastResp = null;\n    for (let i = 0; i < queue.length; i++) {\n      updateCartModalProgress(i, queue.length);\n      try {\n        \/\/ Diagnostic snapshot\n        try { sessionStorage.setItem('rj24_sent_' + (i + 1), JSON.stringify(queue[i]).substring(0, 600)); } catch(e) {}\n\n        const data = await postJingle(queue[i]);\n        lastResp = data;\n\n        try { sessionStorage.setItem('rj24_resp_' + (i + 1), 'ok cart_count=' + data.cart_count); } catch(e) {}\n\n        \/\/ Verify the cart actually grew to at least (i+1) items.\n        if (typeof data.cart_count === 'number' && data.cart_count < (i + 1)) {\n          throw new Error('Cart did not register jingle ' + (i + 1) + ' (count=' + data.cart_count + ')');\n        }\n      } catch (err) {\n        console.error('[RJSW] Jingle ' + (i + 1) + ' failed:', err);\n        recordError('cart_add_failed', i, [err.message || String(err)]);\n        return showCartError('Jingle ' + (i + 1) + ' of ' + queue.length +\n          ' couldn\\'t be added:<br><code style=\"font-size:11px;background:var(--rj-bg);padding:6px;border-radius:4px;display:block;margin:8px 0;\">' +\n          escapeHtml(err.message || 'Unknown error') +\n          '<\/code>' + (i > 0 ? 'The ' + i + ' jingle' + (i === 1 ? '' : 's') + ' before it ' + (i === 1 ? 'is' : 'are') + ' in your cart. ' : '') +\n          'Please try again or contact us.');\n      }\n    }\n\n    \/\/ All jingles added \u2014 go to the cart.\n    $('#rjsw-modal-title').textContent = 'Added to cart \u2713';\n    $('#rjsw-modal-sub').textContent = 'Taking you to your cart\u2026';\n    const loader = $('#rjsw-cart-modal').querySelector('.rjsw-modal-loader');\n    if (loader) loader.style.display = 'none';\n    window.location.href = (lastResp && lastResp.cart_url) ? lastResp.cart_url : (cfg.cartUrl || '\/cart\/');\n  }\n\n  function showCartModal(currentIdx, totalCount) {\n    const modal = $('#rjsw-cart-modal');\n    $('#rjsw-modal-title').textContent = (totalCount && totalCount > 1) ? 'Adding your jingles\u2026' : 'Adding to cart\u2026';\n    $('#rjsw-modal-sub').textContent = 'Please don\\'t close this tab.';\n    const progress = $('#rjsw-modal-progress');\n    if (totalCount && totalCount > 1) {\n      progress.style.display = 'block';\n      progress.textContent = 'Jingle 1 of ' + totalCount;\n    } else {\n      progress.style.display = 'none';\n      progress.textContent = '';\n    }\n    const loader = modal.querySelector('.rjsw-modal-loader');\n    if (loader) loader.style.display = 'block';\n    modal.classList.add('open');\n  }\n\n  function updateCartModalProgress(idx, total) {\n    const progress = $('#rjsw-modal-progress');\n    if (total > 1) {\n      progress.style.display = 'block';\n      progress.textContent = 'Jingle ' + (idx + 1) + ' of ' + total;\n    }\n  }\n\n  function showCartError(html) {\n    const modal = $('#rjsw-cart-modal');\n    modal.classList.add('open');\n    $('#rjsw-modal-title').textContent = 'Couldn\\'t add to cart';\n    $('#rjsw-modal-sub').innerHTML = html;\n    const progress = $('#rjsw-modal-progress');\n    progress.style.display = 'none';\n    const loader = modal.querySelector('.rjsw-modal-loader');\n    if (loader) loader.style.display = 'none';\n  }\n\n  \/\/ Legacy no-op kept so any stray reference doesn't throw.\n  function checkPendingPack() { return false; }\n\n  \/\/ ===== NAVIGATION =====\n  function goNext(target) {\n    if (target === 'singer') {\n      if (isCustom() && state.pack.length === 0) { showToast('Add at least one bed first'); return; }\n      if (isPackage() && !state.selectedPackage) { showToast('Pick a package first'); return; }\n      showScreen('singer'); renderSingers();\n    } else if (target === 'lyrics') {\n      if (!state.singer) { showToast('Pick a singer first'); return; }\n      showScreen('lyrics'); renderLyrics();\n    } else if (target === 'fmt') {\n      if (!allLyricsFilled()) { showToast('Fill in lyrics for every jingle'); return; }\n      showScreen('fmt'); renderExtras();\n    } else if (target === 'review') {\n      showScreen('review'); renderReview();\n    } else if (target === 'cart') {\n      startCartFlow();\n    }\n  }\n  function goBack(target) {\n    if (target === 'mode') {\n      if (state.pack.length > 0 && !confirm('Going back will clear your selection. Continue?')) return;\n      state.pack = []; state.selectedPackage = null; state.mode = null;\n      showScreen('mode');\n    } else if (target === 'browse-or-package') {\n      if (isCustom()) { showScreen('browse'); renderPackBar(); renderBedGrid(); }\n      else { showScreen('package'); renderPackages(); }\n    } else if (target === 'browse') { showScreen('browse'); renderPackBar(); renderBedGrid(); }\n    else if (target === 'package') { showScreen('package'); renderPackages(); }\n    else if (target === 'singer') { showScreen('singer'); renderSingers(); }\n    else if (target === 'lyrics') { showScreen('lyrics'); renderLyrics(); }\n    else if (target === 'fmt') { showScreen('fmt'); renderExtras(); }\n  }\n\n  function init() {\n    if (checkPendingPack()) return;\n\n    const recentErr = readRecentError();\n    if (recentErr) {\n      const detail = Array.isArray(recentErr.details) ? recentErr.details.join('; ') : String(recentErr.details);\n      const stage = recentErr.stage === 'fill_missing_fields' ? 'Missing TMEPO fields' :\n                    recentErr.stage === 'cart_submit_timeout' ? 'Cart submission timeout' : 'Error';\n      const jingleLabel = recentErr.jingle !== undefined && recentErr.jingle !== null ?\n        'Jingle #' + (recentErr.jingle + 1) + ': ' : '';\n      showToast(jingleLabel + stage + ' \\u2014 ' + detail.substring(0, 100), 'error');\n      clearError();\n    }\n\n    $$('.rjsw-mode-card').forEach(card => {\n      card.addEventListener('click', () => {\n        state.mode = card.dataset.mode;\n        if (state.mode === 'custom') { showScreen('browse'); renderPackBar(); renderBedGrid(); }\n        else { showScreen('package'); renderPackages(); }\n      });\n    });\n    $$('.rjsw-sticky-cta-back').forEach(btn => btn.addEventListener('click', () => goBack(btn.dataset.back)));\n    $$('.rjsw-sticky-cta-btn').forEach(btn => btn.addEventListener('click', () => goNext(btn.dataset.next)));\n    $$('.rjsw-filter-pill').forEach(pill => {\n      pill.addEventListener('click', () => {\n        $$('.rjsw-filter-pill').forEach(p => p.classList.remove('active'));\n        pill.classList.add('active');\n        state.filter = pill.dataset.filter;\n        renderBedGrid();\n      });\n    });\n    $$('.rjsw-fmt-opt').forEach(btn => {\n      btn.addEventListener('click', () => {\n        $$('.rjsw-fmt-opt').forEach(b => b.classList.remove('selected'));\n        btn.classList.add('selected');\n        state.format = btn.dataset.format;\n        updateAllCTAs();\n      });\n    });\n    $('#rjsw-instructions').addEventListener('input', e => { state.instructions = e.target.value; });\n    updateAllCTAs();\n  }\n\n  \/\/ v3.9.0: the wizard no longer drives TMEPO \u2014 it adds via the RJ24 plugin\n  \/\/ AJAX endpoint. So we just initialise as soon as the DOM is ready; there's\n  \/\/ no hidden product form to wait for.\n  function boot() {\n    try { init(); }\n    catch (e) { console.error('[RJSW] init error:', e); }\n  }\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);\n  else boot();\n\n  \/\/ ==========================================================\n  \/\/ v3.9.6 \u2014 Footer-aware lift for the fixed-bottom sticky bar.\n  \/\/ Pushes every visible .rjsw-sticky-cta up by the footer-overlap distance\n  \/\/ so the bar stops covering footer content. Lift is clamped so the bar\n  \/\/ remains visible at all times.\n  \/\/\n  \/\/ IMPORTANT: this script runs at HTML parse time, BEFORE the WP footer\n  \/\/ is in the DOM. So we have to defer attachment. We use a 3-tier retry:\n  \/\/ immediate \/ DOMContentLoaded \/ window.load \/ 200ms polling fallback.\n  \/\/ Once a scroll listener is attached, we never re-attach (idempotent).\n  \/\/ ==========================================================\n  (function () {\n    var attached = false;\n    var pollAttempts = 0;\n    function tryAttach() {\n      if (attached) return true;\n      var footer = document.querySelector('.rj24-foot, #footer, .site-footer');\n      if (!footer) return false; \/\/ footer not in DOM yet \u2014 try again later\n\n      var ticking = false;\n      function adjust() {\n        ticking = false;\n        var footerTop = footer.getBoundingClientRect().top;\n        var vh = window.innerHeight;\n        var overlap = vh - footerTop;\n        var bars = document.querySelectorAll('.rjsw-sticky-cta');\n        var visibleBar = null;\n        for (var j = 0; j < bars.length; j++) {\n          if (getComputedStyle(bars[j]).display !== 'none') { visibleBar = bars[j]; break; }\n        }\n        var barH = visibleBar ? visibleBar.offsetHeight : 64;\n        var maxLift = Math.max(0, vh - barH);\n        var lift = Math.max(0, Math.min(overlap, maxLift));\n        for (var i = 0; i < bars.length; i++) {\n          bars[i].style.transform = lift > 0 ? 'translateY(-' + lift + 'px)' : '';\n        }\n      }\n      function onScroll() {\n        if (ticking) return;\n        ticking = true;\n        window.requestAnimationFrame(adjust);\n      }\n      window.addEventListener('scroll', onScroll, { passive: true });\n      window.addEventListener('resize', onScroll, { passive: true });\n      attached = true;\n      adjust();\n      return true;\n    }\n    function pollUntilAttached() {\n      if (tryAttach()) return;\n      pollAttempts++;\n      if (pollAttempts < 25) setTimeout(pollUntilAttached, 200);\n    }\n    \/\/ 1. Try right now (in case footer is already there \u2014 unlikely but cheap)\n    if (!tryAttach()) {\n      \/\/ 2. Try on DOMContentLoaded\n      if (document.readyState === 'loading') {\n        document.addEventListener('DOMContentLoaded', tryAttach);\n      }\n      \/\/ 3. Try on window.load (most reliable \u2014 all resources are in by then)\n      window.addEventListener('load', tryAttach);\n      \/\/ 4. Polling fallback \u2014 covers themes\/plugins that inject footer late\n      setTimeout(pollUntilAttached, 200);\n    }\n  })();\n\n  window.RJSW = state;\n})();\n<\/script>\n\t\t<\/div>\n\t<\/div>\n\n\t\t\t<\/div> \n\t\t<\/div>\n\t<\/div> \n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"\/* ===== KILL ALL REMAINING WOOCOMMERCE CHROME ===== NOTE: We do NOT hide .woocommerce-tabs or #tab-description because the sung wizard's Raw HTML block lives INSIDE the description tab on the...","protected":false},"featured_media":6045,"comment_status":"closed","ping_status":"closed","template":"","meta":[],"product_brand":[],"product_cat":[89,87,481,557,15],"product_tag":[],"class_list":["post-18223","product","type-product","status-publish","has-post-thumbnail","product_cat-jingles","product_cat-packages","product_cat-radio","product_cat-sale","product_cat-uncategorized","tm-has-options","first","instock","taxable","shipping-taxable","purchasable","product-type-simple"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Sung Jingles - Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/radiojingles24.com\/it\/prodotto\/sung-audio\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Sung Jingles - Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles\" \/>\n<meta property=\"og:url\" content=\"https:\/\/radiojingles24.com\/it\/prodotto\/sung-audio\/\" \/>\n<meta property=\"og:site_name\" content=\"Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/radiojingles24\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-01T14:33:03+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/7Asset-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"492\" \/>\n\t<meta property=\"og:image:height\" content=\"492\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@radiojingles24\" \/>\n<meta name=\"twitter:label1\" content=\"Tempo di lettura stimato\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minuto\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/\",\"url\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/\",\"name\":\"Sung Jingles - Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/radiojingles24.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/7Asset-1.png\",\"datePublished\":\"2020-03-12T05:58:44+00:00\",\"dateModified\":\"2026-06-01T14:33:03+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/#primaryimage\",\"url\":\"https:\\\/\\\/radiojingles24.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/7Asset-1.png\",\"contentUrl\":\"https:\\\/\\\/radiojingles24.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/7Asset-1.png\",\"width\":492,\"height\":492},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/product\\\/sung-audio\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/radiojingles24.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Shop\",\"item\":\"https:\\\/\\\/radiojingles24.com\\\/es\\\/shop\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Sung Jingles\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/#website\",\"url\":\"https:\\\/\\\/radiojingles24.com\\\/\",\"name\":\"Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles\",\"description\":\"Radio Jingles, Sung Jingles, DJ Drops, and Podcast Intros for commercial, community and internet radio stations. UK Based. Prices from \u00a35. Order online.\",\"publisher\":{\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/radiojingles24.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"it-IT\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/#organization\",\"name\":\"Radio Jingles 24\",\"url\":\"https:\\\/\\\/radiojingles24.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/radiojingles24.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/RadioJingle1-1.jpg\",\"contentUrl\":\"https:\\\/\\\/radiojingles24.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/RadioJingle1-1.jpg\",\"width\":512,\"height\":158,\"caption\":\"Radio Jingles 24\"},\"image\":{\"@id\":\"https:\\\/\\\/radiojingles24.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/radiojingles24\\\/\",\"https:\\\/\\\/x.com\\\/radiojingles24\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Sung Jingles - Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/radiojingles24.com\/it\/prodotto\/sung-audio\/","og_locale":"it_IT","og_type":"article","og_title":"Sung Jingles - Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles","og_url":"https:\/\/radiojingles24.com\/it\/prodotto\/sung-audio\/","og_site_name":"Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles","article_publisher":"https:\/\/www.facebook.com\/radiojingles24\/","article_modified_time":"2026-06-01T14:33:03+00:00","og_image":[{"width":492,"height":492,"url":"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/7Asset-1.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_site":"@radiojingles24","twitter_misc":{"Tempo di lettura stimato":"1 minuto"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/radiojingles24.com\/product\/sung-audio\/","url":"https:\/\/radiojingles24.com\/product\/sung-audio\/","name":"Sung Jingles - Radio Jingles 24: Radio Jingles | DJ Drops | Podcast Intros | Sung Jingles","isPartOf":{"@id":"https:\/\/radiojingles24.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/radiojingles24.com\/product\/sung-audio\/#primaryimage"},"image":{"@id":"https:\/\/radiojingles24.com\/product\/sung-audio\/#primaryimage"},"thumbnailUrl":"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/7Asset-1.png","datePublished":"2020-03-12T05:58:44+00:00","dateModified":"2026-06-01T14:33:03+00:00","breadcrumb":{"@id":"https:\/\/radiojingles24.com\/product\/sung-audio\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/radiojingles24.com\/product\/sung-audio\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/radiojingles24.com\/product\/sung-audio\/#primaryimage","url":"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/7Asset-1.png","contentUrl":"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/7Asset-1.png","width":492,"height":492},{"@type":"BreadcrumbList","@id":"https:\/\/radiojingles24.com\/product\/sung-audio\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/radiojingles24.com\/"},{"@type":"ListItem","position":2,"name":"Shop","item":"https:\/\/radiojingles24.com\/es\/shop\/"},{"@type":"ListItem","position":3,"name":"Sung Jingles"}]},{"@type":"WebSite","@id":"https:\/\/radiojingles24.com\/#website","url":"https:\/\/radiojingles24.com\/","name":"Radio Jingles 24: Radio Jingles | DJ Drops | Introduzione ai podcast | Jingles cantati","description":"Jingles radiofonici, Jingles cantati, DJ Drops e Podcast Intro per stazioni radio commerciali, comunitarie e Internet. Con sede nel Regno Unito. Prezzi a partire da \u00a3 5. Ordinare on-line.","publisher":{"@id":"https:\/\/radiojingles24.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/radiojingles24.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"it-IT"},{"@type":"Organization","@id":"https:\/\/radiojingles24.com\/#organization","name":"Jingle radio 24","url":"https:\/\/radiojingles24.com\/","logo":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/radiojingles24.com\/#\/schema\/logo\/image\/","url":"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/RadioJingle1-1.jpg","contentUrl":"https:\/\/radiojingles24.com\/wp-content\/uploads\/2019\/11\/RadioJingle1-1.jpg","width":512,"height":158,"caption":"Radio Jingles 24"},"image":{"@id":"https:\/\/radiojingles24.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/radiojingles24\/","https:\/\/x.com\/radiojingles24"]}]}},"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/product\/18223","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/product"}],"about":[{"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/types\/product"}],"replies":[{"embeddable":true,"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/comments?post=18223"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/media\/6045"}],"wp:attachment":[{"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/media?parent=18223"}],"wp:term":[{"taxonomy":"product_brand","embeddable":true,"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/product_brand?post=18223"},{"taxonomy":"product_cat","embeddable":true,"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/product_cat?post=18223"},{"taxonomy":"product_tag","embeddable":true,"href":"https:\/\/radiojingles24.com\/it\/wp-json\/wp\/v2\/product_tag?post=18223"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}