{"product_id":"fabrica-coffee-subscription-classics","title":"Fábrica's Coffee Subscription - Classics","description":"\u003cp\u003ePerfis redondos e reconfortantes - chocolate, frutos secos, caramelo. Embalado do nosso lote mais fresco e enviado em 24-48h.\u003c\/p\u003e\n\u003cdiv id=\"fspdp-classics\" class=\"fspdp-classics-scope\" style=\"display:none\"\u003e\n  \u003ch3 class=\"fspdp-classics-title\"\u003eNa seleção desta semana\u003c\/h3\u003e\n  \u003cdiv class=\"fspdp-classics-grid\"\u003e\n    \u003cdiv class=\"fspdp-classics-coffee\" data-c=\"cEsp\"\u003e\n      \u003cdiv class=\"fspdp-classics-img\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0953\/5553\/5707\/files\/fazenda-dois-irmaos-250g-fabrica-specialty-coffee-bag.webp?v=1784126884\u0026amp;width=420\" alt=\"Fazenda Dois Irmãos - Brazil\" loading=\"lazy\"\u003e\u003c\/div\u003e\n      \u003cspan class=\"fspdp-classics-tag\"\u003eClassics\u003c\/span\u003e\n      \u003ch4\u003eFazenda Dois Irmãos - Brazil\u003c\/h4\u003e\n      \u003cdiv class=\"fspdp-classics-notes\"\u003eHazelnut, cocoa, brown sugar\u003c\/div\u003e\n      \u003cdiv class=\"fspdp-classics-colr\"\u003e\n\u003cspan class=\"fspdp-classics-role\"\u003eEspresso\u003c\/span\u003e\u003cspan class=\"fspdp-classics-cnt\"\u003e\u003c\/span\u003e\n\u003c\/div\u003e\n    \u003c\/div\u003e\n    \u003cdiv class=\"fspdp-classics-coffee\" data-c=\"cFil\"\u003e\n      \u003cdiv class=\"fspdp-classics-img\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0953\/5553\/5707\/files\/estrela-carvalho-brazil-250g-fabrica-specialty-coffee-bag.webp?v=1771497934\u0026amp;width=420\" alt=\"Estrela Carvalho - Brazil\" loading=\"lazy\"\u003e\u003c\/div\u003e\n      \u003cspan class=\"fspdp-classics-tag\"\u003eClassics\u003c\/span\u003e\n      \u003ch4\u003eEstrela Carvalho - Brazil\u003c\/h4\u003e\n      \u003cdiv class=\"fspdp-classics-notes\"\u003eMilk chocolate, almond, caramel\u003c\/div\u003e\n      \u003cdiv class=\"fspdp-classics-colr\"\u003e\n\u003cspan class=\"fspdp-classics-role\"\u003eFilter\u003c\/span\u003e\u003cspan class=\"fspdp-classics-cnt\"\u003e\u003c\/span\u003e\n\u003c\/div\u003e\n    \u003c\/div\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cdiv id=\"fspdp-classics-extra\" class=\"fspdp-classics-scope\" style=\"display:none\"\u003e\n  \u003cdiv class=\"fspdp-classics-how\"\u003e\n    \u003ch3 class=\"fspdp-classics-title\" style=\"margin-top:20px;\"\u003eComo funciona\u003c\/h3\u003e\n    \u003cdiv class=\"fspdp-classics-step\"\u003e\n\u003cspan class=\"fspdp-classics-dot\"\u003e\u003c\/span\u003e\u003cdiv\u003e\n\u003cb\u003eHoje\u003c\/b\u003e\u003cspan\u003eFaz a sua subscrição\u003c\/span\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n    \u003cdiv class=\"fspdp-classics-step\"\u003e\n\u003cspan class=\"fspdp-classics-dot\"\u003e\u003c\/span\u003e\u003cdiv\u003e\n\u003cb\u003eEm 24-48h\u003c\/b\u003e\u003cspan\u003eEmbalado do lote mais fresco e enviado\u003c\/span\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n    \u003cdiv class=\"fspdp-classics-step\"\u003e\n\u003cspan class=\"fspdp-classics-dot g\"\u003e\u003c\/span\u003e\u003cdiv\u003e\n\u003cb\u003e1-2 dias úteis\u003c\/b\u003e\u003cspan\u003eÀ sua porta\u003c\/span\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"fspdp-classics-pills\"\u003e\n    \u003cspan class=\"fspdp-classics-pill\"\u003eEnvio em 24-48h\u003c\/span\u003e\n    \u003cspan class=\"fspdp-classics-pill\"\u003ePortes grátis\u003c\/span\u003e\n    \u003cspan class=\"fspdp-classics-pill\"\u003eFeito à sua medida\u003c\/span\u003e\n    \u003cspan class=\"fspdp-classics-pill\"\u003ePause, salte ou cancele quando quiser\u003c\/span\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\u003cstyle\u003e\n  .fspdp-classics-scope { display: block; width: 100%; padding: 8px 0; }\n  .fspdp-classics-scope .fspdp-classics-title { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; color: #191919; margin: 0 0 12px; }\n  .fspdp-classics-scope .fspdp-classics-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }\n  .fspdp-classics-scope .fspdp-classics-coffee { display: flex; flex-direction: column; padding: 18px 18px 16px; border-radius: 16px; background: rgba(241, 231, 220, 0.8); transition: opacity 0.25s ease; }\n  .fspdp-classics-scope .fspdp-classics-coffee.dim { opacity: 0.35; }\n  .fspdp-classics-scope .fspdp-classics-img { width: 100%; height: 240px; display: flex; align-items: center; justify-content: center; margin-bottom: 12px; }\n  .fspdp-classics-scope .fspdp-classics-img img { width: auto; height: 100%; }\n  .fspdp-classics-scope .fspdp-classics-img img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; mix-blend-mode: multiply; border: none !important; box-shadow: none !important; }\n  .fspdp-classics-scope h4 { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.25; color: #191919; margin: 0 0 2px; }\n  .fspdp-classics-scope .fspdp-classics-tag { display: block; font-size: 10px; text-transform: uppercase; letter-spacing: 0.8px; font-weight: 800; color: #8a5a2a; margin-bottom: 3px; }\n  .fspdp-classics-scope .fspdp-classics-notes { font-size: 13px; color: #6f6a62; }\n  .fspdp-classics-scope .fspdp-classics-colr { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-top: auto; padding-top: 14px; flex-wrap: wrap; }\n  .fspdp-classics-scope .fspdp-classics-role { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 700; border: 1px solid rgba(25, 25, 25, 0.35); border-radius: 90px; padding: 4px 12px; white-space: nowrap; color: #191919; }\n  .fspdp-classics-scope .fspdp-classics-cnt { display: none; font-size: 12px; font-weight: 800; background: #191919; color: #FFFEFA; border-radius: 90px; padding: 3px 12px; white-space: nowrap; }\n  .fspdp-classics-scope .fspdp-classics-coffee.on .fspdp-classics-cnt { display: inline-block; }\n  .fspdp-classics-scope .fspdp-classics-note { font-size: 12px; color: #6f6a62; margin: 12px 0 0; }\n  .fspdp-classics-scope .fspdp-classics-how { margin-top: 4px; }\n  .fspdp-classics-scope .fspdp-classics-step { display: flex; gap: 14px; align-items: flex-start; padding: 10px 4px; border-bottom: 1px solid rgba(25, 25, 25, 0.08); }\n  .fspdp-classics-scope .fspdp-classics-step:last-child { border-bottom: none; }\n  .fspdp-classics-scope .fspdp-classics-dot { display: inline-block !important; width: 10px; height: 10px; border-radius: 50%; background: #191919; margin-top: 7px; flex-shrink: 0; }\n  .fspdp-classics-scope .fspdp-classics-dot.g { background: #2FAC66; }\n  .fspdp-classics-scope .fspdp-classics-step b { display: block; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; color: #6f6a62; font-weight: 700; }\n  .fspdp-classics-scope .fspdp-classics-step span:not(.fspdp-classics-dot) { font-size: 15px; font-weight: 600; color: #191919; }\n  .fspdp-classics-scope .fspdp-classics-pills { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }\n  .fspdp-classics-scope .fspdp-classics-pill { font-size: 14px; font-weight: 400; color: #191919; padding: 9px 18px; border-radius: 90px; border: 1px solid #191919; background: transparent; white-space: nowrap; }\n\u003c\/style\u003e\n\u003cscript\u003e\n(function () {\n  var TIER = \"classics\";\n  var HANDLE = 'fabrica-coffee-subscription-' + TIER;\n  if (window.location.pathname.indexOf(HANDLE) === -1) return; \/\/ not this product's page - do nothing\n  var panel = document.getElementById(\"fspdp-classics\");\n  if (!panel) return;\n  var WASH = \"#FAF4EC\", ACCENT = \"#7B5B3F\", TILE = \"#F1E7DC\", STROKE = \"#D8C4AC\";\n  \/\/ The JS-written strings can't go through Shopify's translate app (it only\n  \/\/ sees static content), so they localize off the storefront locale directly.\n  var IS_PT = (((window.Shopify \u0026\u0026 window.Shopify.locale) || document.documentElement.lang || '') + '').toLowerCase().indexOf('pt') === 0;\n  var L = IS_PT\n    ? { youPay: 'Total', perDelivery: 'por entrega', perBag: '\/ bag', bag: 'bag', bags: 'bags',\n        youSave: 'Poupe ', shipStrong: 'Portes grátis', shipRest: ' em Portugal e Espanha, em cada entrega' }\n    : { youPay: 'You pay', perDelivery: 'per delivery', perBag: '\/ bag', bag: 'bag', bags: 'bags',\n        youSave: 'You save ', shipStrong: 'Free shipping', shipRest: ' across Portugal and Spain, on every delivery' };\n  var BAGS = { '1 bag': 1, '2 bags': 2, '4 bags': 4, '6 bags': 6 };\n  var BREW = { 'Espresso': 'esp', 'Filter': 'fil', 'Half \u0026 half': 'mix' };\n  function sel() {\n    var q = 1, b = 'esp';\n    Array.prototype.forEach.call(document.querySelectorAll('input[type=radio]'), function (r) {\n      if (r.checked) {\n        if (BAGS[r.value]) q = BAGS[r.value];\n        if (BREW[r.value]) b = BREW[r.value];\n      }\n    });\n    return { q: q, b: b };\n  }\n  function half(m) { return [Math.ceil(m \/ 2), Math.floor(m \/ 2)]; }\n  function allocation(q, b) {\n    var c = {}, s, e, f;\n    if (TIER === 'decaf') { c.decaf = q; return c; }\n    if (TIER === 'classics') {\n      if (b === 'esp') c.cEsp = q;\n      else if (b === 'fil') c.cFil = q;\n      else { s = half(q); c.cEsp = s[0]; if (s[1]) c.cFil = s[1]; }\n      return c;\n    }\n    if (b === 'esp') { s = half(q); c.xEsp1 = s[0]; if (s[1]) c.xEsp2 = s[1]; }\n    else if (b === 'fil') { s = half(q); c.xFil1 = s[0]; if (s[1]) c.xFil2 = s[1]; }\n    else {\n      s = half(q); e = half(s[0]); f = half(s[1]);\n      c.xEsp1 = e[0]; if (e[1]) c.xEsp2 = e[1];\n      if (f[0]) c.xFil1 = f[0]; if (f[1]) c.xFil2 = f[1];\n    }\n    return c;\n  }\n  function update() {\n    var s = sel();\n    var alloc = allocation(s.q, s.b);\n    Array.prototype.forEach.call(panel.querySelectorAll('[data-c]'), function (el) {\n      var n = alloc[el.getAttribute('data-c')];\n      var cnt = el.querySelector('.' + \"fspdp-classics\" + '-cnt');\n      if (n) { el.classList.add('on'); el.classList.remove('dim'); cnt.textContent = '\\u00d7 ' + n + ' ' + (n === 1 ? L.bag : L.bags); }\n      else { el.classList.remove('on'); el.classList.add('dim'); }\n    });\n  }\n  \/\/ \"You pay\" summary above the subscribe button. Reads the widget's server-\n  \/\/ rendered variant-\u003eplan price map, so it never depends on the widget JS's\n  \/\/ own rebase timing. Prices are flat across frequencies, but we still honour\n  \/\/ the selected plan id in case that ever changes.\n  function money(c) {\n    try { if (window.Shopify \u0026\u0026 Shopify.formatMoney \u0026\u0026 Shopify.money_format) return Shopify.formatMoney(Math.round(c), Shopify.money_format); } catch (e) { \/* fall through *\/ }\n    return (Math.round(c) \/ 100).toFixed(2).replace('.', ',') + '\\u20ac';\n  }\n  \/\/ The info container holds several cart forms (sample add-on etc.) - anchor\n  \/\/ on the buy buttons and walk up to THEIR form, which carries the variant id.\n  function buyButtons() {\n    return document.querySelector('.product__info-container .product-form__buttons');\n  }\n  function payData() {\n    var mapEl = document.querySelector('.js-variant-plans-data');\n    var btns = buyButtons();\n    var form = btns \u0026\u0026 btns.closest('form');\n    if (!mapEl || !form) return null;\n    var data; try { data = JSON.parse(mapEl.textContent); } catch (e) { return null; }\n    var vid = form.querySelector('input[name=\"id\"]');\n    var v = vid \u0026\u0026 data[String(vid.value)];\n    if (!v) return null;\n    var planInp = document.querySelector('input[name=\"selling_plan_id\"]:checked');\n    var hidden = form.querySelector('input[name=\"selling_plan\"]');\n    var planId = planInp ? String(planInp.value) : String((hidden \u0026\u0026 hidden.value) || '');\n    function priceOf(x) { return planId \u0026\u0026 x.plans \u0026\u0026 x.plans[planId] != null ? x.plans[planId] : x.base; }\n    var total = priceOf(v);\n    var bags = parseInt(v.bags, 10) || 1;\n    \/\/ The struck price is what these same bags cost bought one at a time on the\n    \/\/ same plan. Every 1-bag variant of a tier shares one price, so the first\n    \/\/ one found is the tier's unit price.\n    var unit = null;\n    for (var k in data) { if (parseInt(data[k].bags, 10) === 1) { unit = priceOf(data[k]); break; } }\n    var was = unit != null ? unit * bags : 0;\n    return { total: total, bags: bags, was: was \u003e total ? was : 0 };\n  }\n  function updatePay() {\n    var el = document.getElementById(\"fspdp-classics\" + '-pay');\n    if (!el) return;\n    var d = payData();\n    if (!d || !d.total) { el.style.display = 'none'; return; }\n    el.style.display = '';\n    el.querySelector('.js-pay-total').textContent = money(d.total);\n    el.querySelector('.js-pay-per').textContent = money(Math.round(d.total \/ d.bags)) + ' ' + L.perBag + (d.bags \u003e 1 ? ' \\u00b7 ' + d.bags + ' ' + L.bags : '');\n    \/\/ No saving at 1 bag - both elements empty themselves rather than show a\n    \/\/ struck price identical to the one beside it.\n    el.querySelector('.js-pay-was').textContent = d.was ? money(d.was) : '';\n    el.querySelector('.js-pay-save').textContent = d.was ? L.youSave + money(d.was - d.total) : '';\n  }\n  function mountPay() {\n    if (document.getElementById(\"fspdp-classics\" + '-pay')) { updatePay(); return; }\n    var btns = buyButtons();\n    if (!btns) return;\n    var el = document.createElement('div');\n    el.id = \"fspdp-classics\" + '-pay';\n    el.className = \"fspdp-classics\" + '-pay';\n    el.innerHTML = '\u003cdiv\u003e\u003cspan class=\"' + \"fspdp-classics\" + '-pay-label\"\u003e' + L.youPay + '\u003c\/span\u003e'\n      + '\u003cs class=\"' + \"fspdp-classics\" + '-pay-was js-pay-was\"\u003e\u003c\/s\u003e'\n      + '\u003cspan class=\"' + \"fspdp-classics\" + '-pay-total js-pay-total\"\u003e\u003c\/span\u003e'\n      + '\u003cspan class=\"' + \"fspdp-classics\" + '-pay-freq\"\u003e' + L.perDelivery + '\u003c\/span\u003e\u003c\/div\u003e'\n      + '\u003cdiv\u003e\u003cdiv class=\"' + \"fspdp-classics\" + '-pay-save js-pay-save\"\u003e\u003c\/div\u003e'\n      + '\u003cdiv class=\"' + \"fspdp-classics\" + '-pay-per js-pay-per\"\u003e\u003c\/div\u003e\u003c\/div\u003e';\n    btns.parentNode.insertBefore(el, btns);\n    updatePay();\n  }\n  \/\/ Free shipping sits UNDER the subscribe button, where the reassurance is\n  \/\/ wanted. The zone qualifier is not decoration - an unqualified \"free\n  \/\/ shipping\" is a claim we cannot keep everywhere (docs\/shipping-rules.md).\n  function mountShip() {\n    if (document.getElementById(\"fspdp-classics\" + '-ship')) return;\n    var btns = buyButtons();\n    if (!btns) return;\n    var el = document.createElement('div');\n    el.id = \"fspdp-classics\" + '-ship';\n    el.className = \"fspdp-classics\" + '-ship';\n    el.innerHTML = '\u003csvg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"' + \"#00C853\" + '\" stroke-width=\"2.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"\u003e'\n      + '\u003cpath d=\"M2.6 6.5h10.9v9H2.6z\"\/\u003e\u003cpath d=\"M13.5 9.6h3.7l3.8 3.7v2.2h-7.5z\"\/\u003e'\n      + '\u003ccircle cx=\"6.6\" cy=\"17.6\" r=\"2\"\/\u003e\u003ccircle cx=\"17.1\" cy=\"17.6\" r=\"2\"\/\u003e\u003c\/svg\u003e'\n      + '\u003cspan\u003e\u003cb\u003e' + L.shipStrong + '\u003c\/b\u003e' + L.shipRest + '\u003c\/span\u003e';\n    btns.parentNode.insertBefore(el, btns.nextSibling);\n  }\n  function mount() {\n    document.body.style.setProperty('background', WASH, 'important');\n    var title = document.querySelector('.product__title h1') || document.querySelector('h1.product__title') || document.querySelector('.product__title');\n    if (title) title.style.setProperty('color', ACCENT);\n    var css = document.createElement('style');\n    css.textContent = [\n      '.product__info-container .text-brown1 { color: ' + ACCENT + ' !important; }',\n      '.product__price .text-brown1, .custom-subscription-block .text-brown1, .sticky-bar .text-brown1 { color: ' + ACCENT + ' !important; }',\n      '.' + \"fspdp-classics\" + '-pay { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px 16px; flex-wrap: wrap; margin: 26px 0 20px; }',\n      '.product__info-container .product-form__buttons .product-form__submit, .sticky-bar .product-form__submit { background-color: ' + ACCENT + ' !important; border-color: ' + ACCENT + ' !important; color: #FFFEFA !important; }',\n      '.' + \"fspdp-classics\" + '-pay-label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.8px; font-weight: 800; color: #6f6a62; margin-bottom: 4px; }',\n      '.' + \"fspdp-classics\" + '-pay-total { font-size: 32px; line-height: 1; font-weight: 700; letter-spacing: -0.02em; color: ' + \"#2FAC66\" + '; }',\n      '.' + \"fspdp-classics\" + '-pay-was { font-size: 19px; font-weight: 600; color: #9a948b; text-decoration: line-through; margin-right: 9px; }',\n      '.' + \"fspdp-classics\" + '-pay-was:empty { display: none; }',\n      '.' + \"fspdp-classics\" + '-pay-save { font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px; color: ' + \"#2FAC66\" + '; text-align: right; margin-bottom: 4px; }',\n      '.' + \"fspdp-classics\" + '-pay-save:empty { display: none; }',\n      '.' + \"fspdp-classics\" + '-ship { display: flex; align-items: center; gap: 10px; margin: 14px 0 4px; padding: 10px 14px; border-radius: 12px; background: rgba(0, 200, 83, 0.09); font-size: 14px; line-height: 1.35; color: #191919; }',\n      '.' + \"fspdp-classics\" + '-ship svg { flex-shrink: 0; }',\n      '.' + \"fspdp-classics\" + '-ship b { font-weight: 700; color: ' + \"#2FAC66\" + '; }',\n      '.' + \"fspdp-classics\" + '-pay-freq { font-size: 14px; font-weight: 500; color: #6f6a62; margin-left: 6px; }',\n      '.' + \"fspdp-classics\" + '-pay-per { font-size: 15px; font-weight: 600; color: #6f6a62; padding-bottom: 3px; }',\n      '.product__info-container [class*=\"bg-brown\"], .product__info-container [class*=\"bg-tan\"] { background-color: ' + TILE + ' !important; }',\n      '.product__info-container [class*=\"border-brown\"] { border-color: ' + ACCENT + ' !important; }',\n      '.product__info-container label:has(input:checked) { background-color: ' + TILE + ' !important; border-color: ' + ACCENT + ' !important; }',\n      '.product__info-container input:checked + label { background-color: ' + TILE + ' !important; border-color: ' + ACCENT + ' !important; }',\n      '.grind-note-wrapper input:checked + label { background-color: ' + TILE + ' !important; border-color: ' + ACCENT + ' !important; }',\n      '.product__info-container input:not(:checked) + label { border-color: ' + STROKE + ' !important; }',\n      '.grind-note-wrapper input:not(:checked) + label { border-color: ' + STROKE + ' !important; }',\n      'label.subscription-plan-row:not(:has(input:checked)) { border-color: ' + STROKE + ' !important; }',\n      'quantity-input.quantity { border-color: ' + STROKE + ' !important; }',\n      '.grind-note .custom-select-trigger { border-color: ' + STROKE + ' !important; }',\n      '.grind-note.has-selection .custom-select-trigger, .grind-note.is-open .custom-select-trigger { background-color: ' + TILE + ' !important; border-color: ' + ACCENT + ' !important; }',\n      '.grind-note .custom-select-option::before { border-color: ' + STROKE + ' !important; }',\n      '.grind-note .custom-select-option.selected::before { background-color: ' + ACCENT + ' !important; border-color: ' + ACCENT + ' !important; }',\n      '.quantity__input { background: transparent !important; -webkit-appearance: none !important; appearance: textfield !important; -moz-appearance: textfield !important; }',\n      '.quantity__input::-webkit-inner-spin-button, .quantity__input::-webkit-outer-spin-button { -webkit-appearance: none !important; display: none !important; margin: 0 !important; }',\n      'label.subscription-plan-row:has(input:checked) { background-color: ' + TILE + ' !important; border-color: ' + ACCENT + ' !important; }'\n    ].join('\\n');\n    document.head.appendChild(css);\n    var host = document.querySelector('.product__media-wrapper') || document.querySelector('media-gallery') || document.querySelector('.product__media-gallery') || document.querySelector('.product__media-list');\n    if (host) {\n      Array.prototype.forEach.call(host.children, function (ch) { ch.style.display = 'none'; });\n      host.appendChild(panel);\n    }\n    panel.style.display = 'block';\n    var extra = document.getElementById(\"fspdp-classics\" + '-extra');\n    var mq = window.matchMedia('(max-width: 749px)');\n    function placeExtra() {\n      if (!extra) return;\n      if (mq.matches) {\n        var info = document.querySelector('.product__info-wrapper') || document.querySelector('.product__info-container');\n        if (info) info.appendChild(extra);\n      } else if (host) {\n        host.appendChild(extra);\n      }\n      extra.style.display = 'block';\n    }\n    placeExtra();\n    if (mq.addEventListener) mq.addEventListener('change', placeExtra);\n    update();\n    mountPay();\n    mountShip();\n    \/\/ Widget builds its rows async - retry so the panel lands even on slow loads.\n    setTimeout(function () { mountPay(); mountShip(); }, 600);\n    setTimeout(function () { mountPay(); mountShip(); }, 2000);\n  }\n  document.addEventListener('change', function (e) {\n    if (e.target \u0026\u0026 e.target.type === 'radio') update();\n    \/\/ After the widget's own 80ms rebase, refresh the pay panel too.\n    setTimeout(updatePay, 160);\n  });\n  \/\/ Live lineup from the roastery app: replace this tier's fallback cards when\n  \/\/ the feed has coffees for it. Same slot-\u003ecard mapping as the landing page.\n  var SLOT_KEY = { exoticEsp1: 'xEsp1', exoticEsp2: 'xEsp2', exoticFil1: 'xFil1', exoticFil2: 'xFil2', classicEsp: 'cEsp', classicFil: 'cFil', decaf: 'decaf' };\n  function escHtml(s) { return String(s).replace(\/\u0026\/g, '\u0026amp;').replace(\/\u003c\/g, '\u0026lt;').replace(\/\u003e\/g, '\u0026gt;').replace(\/\"\/g, '\u0026quot;'); }\n  function liveCards() {\n    fetch('https:\/\/fabrica-roastery.vercel.app\/api\/public\/lineup').then(function (r) { return r.json(); }).then(function (d) {\n      if (!d || !d.slots) return;\n      var mine = d.slots.filter(function (s) { return s.tier === TIER \u0026\u0026 SLOT_KEY[s.slot]; });\n      if (!mine.length) return;\n      var html = mine.map(function (s) {\n        var img = s.imageUrl ? s.imageUrl + (s.imageUrl.indexOf('?') === -1 ? '?' : '\u0026') + 'width=420' : null;\n        var blend = img \u0026\u0026 img.toLowerCase().split('?')[0].slice(-4) === '.png' ? ' style=\"mix-blend-mode:normal\"' : '';\n        var name = s.name + (s.origin ? ' - ' + s.origin : '');\n        return '\u003cdiv class=\"' + \"fspdp-classics\" + '-coffee\" data-c=\"' + SLOT_KEY[s.slot] + '\"\u003e'\n          + '\u003cdiv class=\"' + \"fspdp-classics\" + '-img\"\u003e' + (img ? '\u003cimg src=\"' + escHtml(img) + '\" alt=\"' + escHtml(name) + '\"' + blend + '\u003e' : '') + '\u003c\/div\u003e'\n          + '\u003cspan class=\"' + \"fspdp-classics\" + '-tag\"\u003e' + \"Classics\" + '\u003c\/span\u003e'\n          + '\u003ch4\u003e' + escHtml(name) + '\u003c\/h4\u003e'\n          + '\u003cdiv class=\"' + \"fspdp-classics\" + '-notes\"\u003e' + escHtml(s.notes || '') + '\u003c\/div\u003e'\n          + '\u003cdiv class=\"' + \"fspdp-classics\" + '-colr\"\u003e\u003cspan class=\"' + \"fspdp-classics\" + '-role\"\u003e' + escHtml(s.role) + '\u003c\/span\u003e\u003cspan class=\"' + \"fspdp-classics\" + '-cnt\"\u003e\u003c\/span\u003e\u003c\/div\u003e\u003c\/div\u003e';\n      }).join('');\n      var grid = panel.querySelector('[class$=\"-grid\"]');\n      if (grid \u0026\u0026 html) { grid.innerHTML = html; update(); }\n    }).catch(function () {});\n  }\n  liveCards();\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);\n  else mount();\n})();\n\u003c\/script\u003e","brand":"Fábrica Coffee Roasters","offers":[{"title":"1 saco \/ Espresso","offer_id":53834157326683,"sku":"SUB-CLA-1-ESP","price":15.9,"currency_code":"EUR","in_stock":true},{"title":"1 saco \/ Filtro","offer_id":53834157523291,"sku":"SUB-CLA-1-FIL","price":15.9,"currency_code":"EUR","in_stock":true},{"title":"2 sacos \/ Espresso","offer_id":53834157719899,"sku":"SUB-CLA-2-ESP","price":21.9,"currency_code":"EUR","in_stock":true},{"title":"2 sacos \/ Filtro","offer_id":53834157916507,"sku":"SUB-CLA-2-FIL","price":21.9,"currency_code":"EUR","in_stock":true},{"title":"2 sacos \/ Meio a meio","offer_id":53834158113115,"sku":"SUB-CLA-2-MIX","price":21.9,"currency_code":"EUR","in_stock":true},{"title":"4 sacos \/ Espresso","offer_id":53834158309723,"sku":"SUB-CLA-4-ESP","price":35.9,"currency_code":"EUR","in_stock":true},{"title":"4 sacos \/ Filtro","offer_id":53834158506331,"sku":"SUB-CLA-4-FIL","price":35.9,"currency_code":"EUR","in_stock":true},{"title":"4 sacos \/ Meio a meio","offer_id":53834158702939,"sku":"SUB-CLA-4-MIX","price":35.9,"currency_code":"EUR","in_stock":true},{"title":"6 sacos \/ Espresso","offer_id":53834158899547,"sku":"SUB-CLA-6-ESP","price":49.9,"currency_code":"EUR","in_stock":true},{"title":"6 sacos \/ Filtro","offer_id":53834159096155,"sku":"SUB-CLA-6-FIL","price":49.9,"currency_code":"EUR","in_stock":true},{"title":"6 sacos \/ Meio a meio","offer_id":53834159292763,"sku":"SUB-CLA-6-MIX","price":49.9,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0953\/5553\/5707\/files\/fabrica-coffee-roasters-new-subscription-model-front-images-specialty-coffee-03-03.webp?v=1787593719","url":"https:\/\/fabricacoffeeroasters.com\/pt\/products\/fabrica-coffee-subscription-classics","provider":"Fábrica Coffee Roasters","version":"1.0","type":"link"}