{"title":"French Hair Pins","description":"\u003cp\u003eClassic French hair pins for an easy twist or low bun, no hair tie needed. Choose a single pin or the Duo, in 4 colors.\u003c\/p\u003e\u003cp\u003eNew to French pins? Start with our \u003ca href=\"\/blogs\/hair-guides\/how-to-use-a-french-hair-pin\"\u003estep-by-step guide\u003c\/a\u003e, or see how they compare in \u003ca href=\"\/blogs\/hair-guides\/french-pin-vs-claw-clip\"\u003eFrench Pin vs Claw Clip\u003c\/a\u003e.\u003c\/p\u003e","products":[{"product_id":"tortoiseshell-french-hair-pin","title":"French Hair Pin – U-Shaped Pin for French Twists \u0026 Buns","description":"\u003ch2\u003eOne pin, no hair tie: an easy French twist\u003c\/h2\u003e\n\u003cp\u003eIf a hair tie leaves a dent by lunch, or a claw clip feels like too much for the office, try a French pin. Twist your hair up, slide in one slim U-shaped pin, and you have a French twist or low bun that looks finished, not thrown together.\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cstrong\u003eNothing squeezing your hair.\u003c\/strong\u003e No elastic band, so no elastic dent when you take your hair down.\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eLooks like an accessory, not a hair tool.\u003c\/strong\u003e A softly waved U shape in Tortoiseshell pattern, Black, Olive Green or Burgundy.\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eSmall enough to carry.\u003c\/strong\u003e About 13 cm (5.1 in) long, so it fits in a pocket, desk drawer or makeup bag.\u003c\/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eExtra hold when you need it.\u003c\/strong\u003e Long or thick hair? Add a second pin from the opposite side, or get two in one color with our \u003ca href=\"\/products\/duo-french-twist-set\"\u003eFrench hair pin set\u003c\/a\u003e.\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eNew to French pins? Open \u003cem\u003eHow to use\u003c\/em\u003e below for the 4-step twist, or read our \u003ca href=\"\/blogs\/hair-guides\/how-to-use-a-french-hair-pin\"\u003estep-by-step guide\u003c\/a\u003e. It can take a few tries the first time.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhat you get:\u003c\/strong\u003e 1 French Hair Pin in the color you choose.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eSize:\u003c\/strong\u003e about 13 cm (5.1 in) long, as listed by our supplier.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eShipping \u0026amp; returns:\u003c\/strong\u003e free US shipping on orders $60+ ($8 under $60), and 30-day returns on unworn items in original packaging.\u003c\/p\u003e\n\u003cp\u003eShips from our fulfillment partner overseas: orders are processed in 1–3 business days, then typically arrive 5–11 days after dispatch.\u003c\/p\u003e\n\u003c!-- hv-upsell v1 (Havendoa, adapted from hh-galixo v3): add-ons with quick view, delivery, trust. Remove from the hv-upsell v1 start marker to the \/hv-upsell v1 end marker to undo. --\u003e\n\u003cdiv class=\"hb\" data-handle=\"tortoiseshell-french-hair-pin\" hidden\u003e\u003cdiv class=\"hb-pay\"\u003e\u003c\/div\u003e\u003c\/div\u003e\n\u003cstyle\u003e\n.hb{--g:#7a5534;--line:#e3e3e3;font-size:15px;color:#2b2b2b}\n.hb-pay{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin:12px 0 0}\n.hb-pay svg{width:38px;height:24px}\n.hb-add{margin:16px 0 0;border-radius:12px;background:#f5efe8;padding:12px}\n.hb-add-h{font-weight:700;font-size:17px;color:#1d1d1d;margin:0}\n.hb-add-p{font-size:14px;color:#555;margin:3px 0 6px;line-height:1.45}\n.hb-item{display:flex;align-items:center;gap:10px;background:#fff;border-radius:8px;padding:9px 10px;margin-top:7px;cursor:pointer}\n.hb-item input{width:20px;height:20px;accent-color:var(--g);flex:0 0 20px;margin:0}\n.hb-item img{width:54px;height:54px;object-fit:cover;border-radius:6px;flex:0 0 54px}\n.hb-item-t{flex:1;min-width:0;font-size:14px;line-height:1.35}\n.hb-item-t small{display:block;color:#6b6b6b;font-size:12.5px}\n.hb-item-p{text-align:right;white-space:nowrap;font-weight:700}\n.hb-item-p span{display:block;font-weight:400;font-size:12.5px;color:#c0392b}\n.hb-item-p s{color:#8a8a8a;margin-left:4px}\n.hb-ship{text-align:center;margin:16px 0 0;font-size:15px;line-height:1.5}\n.hb-ship b{color:var(--g)}\n.hb-trust{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:14px 0 18px;text-align:center;font-weight:700;font-size:13.5px;color:var(--g)}\n.hb-trust svg{display:block;margin:0 auto 5px;width:32px;height:32px}\n.hb-err{color:#c0392b;text-align:center;font-size:14px;margin-top:6px}\n.hb-item-a{color:inherit;text-decoration:underline;text-underline-offset:2px}\n.hb-item-img{display:block;flex:0 0 54px}\n.hb-ck{text-align:center;margin:0 0 16px;font-size:14px}\n.hb-ck a{color:var(--g);font-weight:600}\n.hq{position:fixed;inset:0;z-index:2147483000;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;padding:16px}\n.hq[hidden]{display:none}\n.hq-box{background:#fff;border-radius:14px;max-width:780px;width:100%;max-height:90vh;overflow:auto;position:relative;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:28px;padding:30px 26px 24px;color:#1d1d1d;font-size:15px}\n.hq-box\u003ediv{min-width:0}\n.hq-x{position:absolute;top:8px;right:10px;background:none;border:0;font-size:28px;line-height:1;cursor:pointer;color:#444;padding:4px 8px}\n.hq-main{position:relative;aspect-ratio:1\/1;display:flex;align-items:center;justify-content:center}\n.hq-main img{max-width:100%;max-height:100%;object-fit:contain}\n.hq-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.9);border:0;border-radius:50%;width:36px;height:36px;font-size:24px;line-height:1;cursor:pointer;color:#333}\n.hq-prev{left:0}.hq-next{right:0}\n.hq-th{display:flex;gap:10px;overflow-x:auto;margin-top:14px;padding-bottom:4px}\n.hq-th button{flex:0 0 58px;padding:0 0 6px;border:0;border-bottom:2px solid transparent;background:none;cursor:pointer}\n.hq-th button.on{border-bottom-color:#1d1d1d}\n.hq-th img{width:58px;height:52px;object-fit:cover;border-radius:4px;display:block}\n.hq-t{font-size:21px;line-height:1.35;margin:0 30px 8px 0;font-weight:500}\n.hq-v{font-size:13.5px;color:#6b6b6b;margin:0 0 8px}\n.hq-p{font-size:20px;font-weight:700;margin:0 0 4px}\n.hq-p s{font-size:15px;color:#8a8a8a;font-weight:400;margin-left:6px}\n.hq-save{font-size:13.5px;color:#7a5534;margin:0 0 14px}\n.hq-ul{list-style:none;padding:0;margin:0 0 18px;font-size:14.5px;line-height:1.5}\n.hq-ul li{position:relative;padding-left:27px;margin:0 0 6px}\n.hq-ul li:before{content:\"\\2713\";position:absolute;left:0;top:2px;width:19px;height:19px;border-radius:4px;background:#a57549;color:#fff;font-size:13px;font-weight:700;line-height:19px;text-align:center}\n.hq-add{display:block;width:100%;border:0;border-radius:40px;background:#7a5534;color:#fff;font:inherit;font-weight:700;padding:12px;cursor:pointer;margin:0 0 14px}\n.hq-more{display:inline-block;color:#1d1d1d;font-weight:600;text-decoration:none;font-size:15.5px}\nbody.hq-lock{overflow:hidden}\n@media (max-width:640px){.hq-box{grid-template-columns:minmax(0,1fr);gap:14px;padding:42px 16px 18px}.hq-t{font-size:18px;margin-right:0}}\n\u003c\/style\u003e\n\u003cscript\u003e\n(() =\u003e {\n  const cs = document.currentScript;\n  const start = () =\u003e {\n  const root = (cs \u0026\u0026 cs.parentElement.querySelector('.hb')) || document.querySelector('.hb');\n  if (!root || root.dataset.ready) return;\n  root.dataset.ready = 1;\n  \/\/ what to offer under each product (handle -\u003e handles)\n  const PAIR = {\n    'duo-french-twist-set': ['elegant-claw-clip', 'pearl-crystal-flower-hair-pin-set', 'geometric-hair-clips-set-of-2'],\n    'tortoiseshell-french-hair-pin': ['elegant-claw-clip', 'pearl-crystal-flower-hair-pin-set'],\n    'elegant-claw-clip': ['tortoiseshell-french-hair-pin', 'geometric-hair-clips-set-of-2'],\n    'geometric-hair-clips-set-of-2': ['elegant-claw-clip', 'tortoiseshell-french-hair-pin'],\n    'pearl-crystal-flower-hair-pin-set': ['vintage-crystal-hair-pin-set-of-3', 'tortoiseshell-french-hair-pin'],\n    'vintage-crystal-hair-pin-set-of-3': ['pearl-crystal-flower-hair-pin-set', 'tortoiseshell-french-hair-pin'],\n    'heart-pearl-hair-clip': ['pearl-crystal-flower-hair-pin-set', 'vintage-crystal-hair-pin-set-of-3']\n  };\n  const HEAD = {};\n  \/\/ must match the store's automatic discounts: Buy 2 items 10%, Buy 3+ items 15%\n  const pctFor = n =\u003e n \u003e= 3 ? 15 : n \u003e= 2 ? 10 : 0;\n  const money = c =\u003e '$' + (c \/ 100).toFixed(2);\n  const info = document.querySelector('.product__info-container');\n  const form = info \u0026\u0026 info.querySelector('form[action*=\"\/cart\/add\"][data-type=\"add-to-cart-form\"]');\n  if (!info || !form) return;\n  const blockOf = el =\u003e { while (el \u0026\u0026 el.parentElement !== info) el = el.parentElement; return el; };\n  const buyBlock = blockOf(form);\n  const qtyInput = () =\u003e info.querySelector('.product-form__quantity input[name=\"quantity\"]') || document.querySelector('input[name=\"quantity\"]');\n  const mainQty = () =\u003e Math.max(1, parseInt((qtyInput() || {}).value, 10) || 1);\n  buyBlock.after(root);\n  root.hidden = false;\n  const pay = root.querySelector('.hb-pay'), footerPay = document.querySelector('.list-payment');\n  if (!pay.querySelector('svg') \u0026\u0026 footerPay) pay.innerHTML = [...footerPay.querySelectorAll('svg')].map(s =\u003e s.outerHTML).join('');\n  if (!pay.querySelector('svg')) pay.remove();\n  const icon = d =\u003e '\u003csvg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e' + d + '\u003c\/svg\u003e';\n  root.insertAdjacentHTML('beforeend',\n    '\u003cdiv class=\"hb-add\" hidden\u003e\u003c\/div\u003e' +\n    '\u003cdiv class=\"hb-ship\"\u003e\u003cb\u003eFree US shipping\u003c\/b\u003e on orders $60+.\u003cbr\u003eTypical delivery: 5–11 days after dispatch.\u003c\/div\u003e' +\n    '\u003cdiv class=\"hb-trust\"\u003e' +\n    '\u003cdiv\u003e' + icon('\u003cpath d=\"M3 7h11v8H3zM14 10h4l3 3v2h-7\"\/\u003e\u003ccircle cx=\"7\" cy=\"17\" r=\"1.6\"\/\u003e\u003ccircle cx=\"17\" cy=\"17\" r=\"1.6\"\/\u003e') + 'Free Shipping $60+\u003c\/div\u003e' +\n    '\u003cdiv\u003e' + icon('\u003cpath d=\"M4 12a8 8 0 1 0 2.4-5.7\"\/\u003e\u003cpath d=\"M4 4v4h4\"\/\u003e') + '30-Day Returns\u003c\/div\u003e' +\n    '\u003cdiv\u003e' + icon('\u003crect x=\"5\" y=\"11\" width=\"14\" height=\"9\" rx=\"2\"\/\u003e\u003cpath d=\"M8 11V8a4 4 0 0 1 8 0v3\"\/\u003e') + 'Secure Checkout\u003c\/div\u003e' +\n    '\u003c\/div\u003e' +\n    '\u003cp class=\"hb-ck\"\u003e\u003ca href=\"\/blogs\/hair-guides\/how-to-use-a-french-hair-pin\"\u003eNew to French pins? Read our step-by-step guide \u0026rarr;\u003c\/a\u003e\u003c\/p\u003e');\n  const handles = PAIR[root.dataset.handle] || [];\n  let extras = [];\n  const add = root.querySelector('.hb-add');\n  const picks = () =\u003e [...add.querySelectorAll('input:checked')].map(c =\u003e extras[+c.dataset.x]);\n  const paint = () =\u003e {\n    const n = mainQty() + picks().length;\n    add.querySelectorAll('.hb-item').forEach(el =\u003e {\n      const e = extras[+el.dataset.x], on = el.querySelector('input').checked;\n      const pct = pctFor(on ? n : n + 1);\n      el.querySelector('.hb-item-p').innerHTML = (pct ? '\u003cspan\u003e-' + pct + '%\u003cs\u003e' + money(e.v.price) + '\u003c\/s\u003e\u003c\/span\u003e' : '') + money(e.v.price - Math.floor(e.v.price * pct \/ 100));\n    });\n  };\n  Promise.all(handles.map(h =\u003e fetch('\/products\/' + h + '.js').then(r =\u003e r.ok ? r.json() : null).catch(() =\u003e null))).then(list =\u003e {\n    extras = list.filter(Boolean).map(a =\u003e { const v = a.variants.find(x =\u003e x.available); return v \u0026\u0026 { a, v }; }).filter(Boolean);\n    if (!extras.length) return;\n    const img = s =\u003e s ? s + (s.includes('?') ? '\u0026' : '?') + 'width=120' : '';\n    const link = e =\u003e '\/products\/' + e.a.handle + '?variant=' + e.v.id;\n    add.innerHTML = '\u003cp class=\"hb-add-h\"\u003e' + (HEAD[root.dataset.handle] || 'Complete the look') + '\u003c\/p\u003e' +\n      '\u003cp class=\"hb-add-p\"\u003eCheck the box to add it to this order. 2 items save 10%, 3 or more save 15%, applied automatically at checkout.\u003c\/p\u003e' +\n      extras.map((e, i) =\u003e '\u003clabel class=\"hb-item\" data-x=\"' + i + '\"\u003e\u003cinput type=\"checkbox\" data-x=\"' + i + '\"\u003e' +\n        (e.a.featured_image ? '\u003ca class=\"hb-item-img\" href=\"' + link(e) + '\" tabindex=\"-1\" aria-hidden=\"true\"\u003e\u003cimg src=\"' + img(e.a.featured_image) + '\" alt=\"\" loading=\"lazy\"\u003e\u003c\/a\u003e' : '') +\n        '\u003cspan class=\"hb-item-t\"\u003e\u003ca class=\"hb-item-a\" href=\"' + link(e) + '\"\u003e' + e.a.title.split(' – ')[0] + '\u003c\/a\u003e' + (e.a.variants.length \u003e 1 ? '\u003csmall\u003e' + e.v.title.split(' \/ ')[0] + '\u003c\/small\u003e' : '') + '\u003c\/span\u003e' +\n        '\u003cspan class=\"hb-item-p\"\u003e\u003c\/span\u003e\u003c\/label\u003e').join('');\n    add.hidden = false;\n    add.addEventListener('click', ev =\u003e {\n      const a = ev.target.closest('.hb-item-a, .hb-item-img');\n      if (!a || !window.hhQV) return;\n      ev.preventDefault();\n      const i = +a.closest('.hb-item').dataset.x, e = extras[i], cb = add.querySelector('input[data-x=\"' + i + '\"]');\n      window.hhQV({ a: e.a, v: e.v, from: a, note: 'Add it to this order: 2 items save 10%, 3 or more save 15% at checkout.', addLabel: cb.checked ? 'Remove from this order' : 'Add to this order', onAdd: () =\u003e { cb.checked = !cb.checked; cb.dispatchEvent(new Event('change', { bubbles: true })); } });\n    });\n    add.addEventListener('change', paint);\n    document.addEventListener('change', e =\u003e { if (e.target.name === 'quantity') setTimeout(paint, 50); });\n    paint();\n  });\n  document.addEventListener('submit', async e =\u003e {\n    if (e.target !== form || !picks().length) return;\n    e.preventDefault(); e.stopImmediatePropagation();\n    const btn = form.querySelector('[type=\"submit\"]'); if (btn) btn.setAttribute('aria-disabled', 'true');\n    const items = [{ id: +form.querySelector('input[name=\"id\"]').value, quantity: mainQty() }, ...picks().map(x =\u003e ({ id: x.v.id, quantity: 1 }))];\n    let r = null;\n    try { r = await fetch('\/cart\/add.js', { method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify({ items }) }); } catch (x) {}\n    if (r \u0026\u0026 r.ok) { location.href = '\/cart'; return; }\n    if (btn) btn.removeAttribute('aria-disabled');\n    let err = root.querySelector('.hb-err'); if (!err) { err = document.createElement('p'); err.className = 'hb-err'; add.after(err); }\n    err.textContent = (r \u0026\u0026 (await r.json().catch(() =\u003e ({}))).description) || 'Could not add to cart. Please try again.';\n  }, true);\n  window.addEventListener('pageshow', e =\u003e { if (e.persisted) { const b = form.querySelector('[type=\"submit\"]'); if (b) b.removeAttribute('aria-disabled'); } });\n  };\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n})();\n\u003c\/script\u003e\n\u003c!-- \/hv-upsell v1 --\u003e\n\u003c!-- hv-extra v1 (Havendoa, adapted from hh-downsell v2 + hh-nopantry v2): shared quick view, single\/duo switch, French Twist Kit. Remove from the hv-extra v1 start marker to the \/hv-extra v1 end marker to undo. --\u003e\n\u003cdiv class=\"hd\" data-handle=\"tortoiseshell-french-hair-pin\" hidden\u003e\u003c\/div\u003e\u003cdiv class=\"hn\" data-handle=\"tortoiseshell-french-hair-pin\" hidden\u003e\u003c\/div\u003e\n\u003cstyle\u003e\n.hd{--g:#7a5534;margin:6px 0 18px;border:1px solid #e5d6c6;border-radius:12px;padding:12px;background:#fff;font-size:15px;color:#2b2b2b}\n.hd-h{font-weight:700;font-size:17px;margin:0}\n.hd-p{font-size:14px;color:#555;margin:3px 0 8px;line-height:1.45}\n.hd-card{display:flex;gap:10px;align-items:flex-start;border-top:1px solid #eee;padding:10px 0 4px}\n.hd-card img{width:62px;height:62px;object-fit:cover;border-radius:8px;flex:0 0 62px}\n.hd-b{flex:1;min-width:0;font-size:13.5px;line-height:1.4}\n.hd-b a.hd-t{font-weight:700;color:#1d1d1d;text-decoration:underline;text-underline-offset:2px;font-size:14.5px}\n.hd-img{display:block;flex:0 0 62px}\n.hd-b .hd-in{color:#2f6b52}\n.hd-b .hd-out{color:#8a5a2b}\n.hd-btn{margin-top:6px;border:1.5px solid var(--g);background:#fff;color:var(--g);border-radius:30px;padding:7px 14px;font:inherit;font-weight:700;font-size:13.5px;cursor:pointer}\n.hd-btn:disabled{opacity:.6}\n.hn{--g:#7a5534;margin:6px 0 18px;border:2px solid var(--g);border-radius:14px;padding:14px;background:#f8f2ec;font-size:15px;color:#2b2b2b}\n.hn-k{font-size:12px;letter-spacing:.08em;text-transform:uppercase;font-weight:700;color:#c75b2a;margin:0}\n.hn-h{font-weight:700;font-size:18px;margin:2px 0 4px;color:var(--g)}\n.hn-p{font-size:14px;color:#555;margin:0 0 10px;line-height:1.45}\n.hn-list{display:grid;gap:8px;margin:0 0 10px}\n.hn-row{display:flex;gap:10px;align-items:center;background:#fff;border-radius:10px;padding:8px}\n.hn-row img{width:48px;height:48px;object-fit:cover;border-radius:6px;flex:0 0 48px}\n.hn-row span{flex:1;min-width:0;font-size:13.5px;line-height:1.35}\n.hn-row span small{display:block;color:#6b6b6b}\n.hn-row b{white-space:nowrap;font-size:14px}\n.hn-sum{display:flex;justify-content:space-between;font-size:14px;margin:2px 2px 0}\n.hn-sum.hn-you{font-weight:700;font-size:16px;color:var(--g)}\n.hn-btn{display:block;width:100%;margin-top:10px;border:0;border-radius:40px;background:var(--g);color:#fff;font:inherit;font-weight:700;font-size:16px;padding:13px;cursor:pointer}\n.hn-btn:disabled{opacity:.6}\n.hn-a{color:inherit;text-decoration:underline;text-underline-offset:2px}\n.hn-img{display:block;flex:0 0 48px}\n.hn-note{font-size:12.5px;color:#6b6b6b;margin:8px 0 0;text-align:center}\n\u003c\/style\u003e\n\u003cscript\u003e\n(() =\u003e {\n  const cs = document.currentScript;\n  if (!window.hhQV) window.hhQV = (() =\u003e {\n    const qv = document.createElement('div');\n    qv.className = 'hq'; qv.hidden = true;\n    document.body.appendChild(qv);\n    const px = (s, w) =\u003e s ? s + (s.includes('?') ? '\u0026' : '?') + 'width=' + w : '';\n    const esc = s =\u003e String(s).replace(\/[\u0026\u003c\u003e\"]\/g, c =\u003e ({ '\u0026': '\u0026amp;', '\u003c': '\u0026lt;', '\u003e': '\u0026gt;', '\"': '\u0026quot;' }[c]));\n    const cash = c =\u003e '$' + (c \/ 100).toFixed(2);\n    const fileOf = s =\u003e String(s || '').split('?')[0].split('\/').pop();\n    const bullets = html =\u003e {\n      const d = new DOMParser().parseFromString(html || '', 'text\/html');\n      d.querySelectorAll('script, style, .hb, .hd, .hn').forEach(x =\u003e x.remove());\n      let li = [...d.querySelectorAll('li')].map(x =\u003e x.textContent.replace(\/\\s+\/g, ' ').trim()).filter(Boolean);\n      if (!li.length) li = [...d.querySelectorAll('p')].map(x =\u003e x.textContent.trim()).filter(Boolean).slice(0, 1);\n      return li.slice(0, 5).map(t =\u003e t.length \u003e 95 ? t.slice(0, 92).replace(\/[\\s,.;:]+\\S*$\/, '') + '…' : t);\n    };\n    let back = null;\n    const close = () =\u003e { qv.hidden = true; document.body.classList.remove('hq-lock'); if (back) back.focus(); };\n    qv.addEventListener('click', ev =\u003e { if (ev.target === qv) close(); });\n    document.addEventListener('keydown', ev =\u003e {\n      if (qv.hidden) return;\n      if (ev.key === 'Escape') close();\n      if (ev.key === 'ArrowLeft') { const b = qv.querySelector('.hq-prev'); if (b) b.click(); }\n      if (ev.key === 'ArrowRight') { const b = qv.querySelector('.hq-next'); if (b) b.click(); }\n    });\n    return o =\u003e {\n      const a = o.a, v = o.v;\n      back = o.from || null;\n      const imgs = (a.images \u0026\u0026 a.images.length) ? a.images : [a.featured_image];\n      let k = v.featured_image ? Math.max(0, imgs.findIndex(s =\u003e fileOf(s) === fileOf(v.featured_image.src))) : 0;\n      qv.innerHTML = '\u003cdiv class=\"hq-box\" role=\"dialog\" aria-modal=\"true\" aria-label=\"' + esc(a.title) + '\"\u003e' +\n        '\u003cbutton type=\"button\" class=\"hq-x\" aria-label=\"Close\"\u003e\u0026times;\u003c\/button\u003e' +\n        '\u003cdiv\u003e\u003cdiv class=\"hq-main\"\u003e\u003cimg alt=\"\"\u003e' +\n        (imgs.length \u003e 1 ? '\u003cbutton type=\"button\" class=\"hq-nav hq-prev\" aria-label=\"Previous image\"\u003e\u0026lsaquo;\u003c\/button\u003e\u003cbutton type=\"button\" class=\"hq-nav hq-next\" aria-label=\"Next image\"\u003e\u0026rsaquo;\u003c\/button\u003e' : '') +\n        '\u003c\/div\u003e' + (imgs.length \u003e 1 ? '\u003cdiv class=\"hq-th\"\u003e' + imgs.map((s, j) =\u003e '\u003cbutton type=\"button\" data-j=\"' + j + '\" aria-label=\"Image ' + (j + 1) + '\"\u003e\u003cimg src=\"' + px(s, 120) + '\" alt=\"\" loading=\"lazy\"\u003e\u003c\/button\u003e').join('') + '\u003c\/div\u003e' : '') + '\u003c\/div\u003e' +\n        '\u003cdiv\u003e\u003ch2 class=\"hq-t\"\u003e' + esc(a.title) + '\u003c\/h2\u003e' +\n        (a.variants.length \u003e 1 ? '\u003cp class=\"hq-v\"\u003e' + esc(v.title.split(' \/ ')[0]) + '\u003c\/p\u003e' : '') +\n        '\u003cp class=\"hq-p\"\u003e' + cash(v.price) + (v.compare_at_price \u003e v.price ? '\u003cs\u003e' + cash(v.compare_at_price) + '\u003c\/s\u003e' : '') + '\u003c\/p\u003e' +\n        (o.note ? '\u003cp class=\"hq-save\"\u003e' + esc(o.note) + '\u003c\/p\u003e' : '') +\n        '\u003cul class=\"hq-ul\"\u003e' + bullets(a.description).map(t =\u003e '\u003cli\u003e' + esc(t) + '\u003c\/li\u003e').join('') + '\u003c\/ul\u003e' +\n        (o.onAdd ? '\u003cbutton type=\"button\" class=\"hq-add\"\u003e' + esc(o.addLabel || 'Add to cart') + '\u003c\/button\u003e' : '') +\n        '\u003ca class=\"hq-more\" href=\"\/products\/' + a.handle + '?variant=' + v.id + '\"\u003eView more details \u003cspan aria-hidden=\"true\"\u003e\u0026rarr;\u003c\/span\u003e\u003c\/a\u003e\u003c\/div\u003e\u003c\/div\u003e';\n      const main = qv.querySelector('.hq-main img'), th = [...qv.querySelectorAll('.hq-th button')];\n      const show = j =\u003e { k = (j + imgs.length) % imgs.length; main.src = px(imgs[k], 800); th.forEach((b, x) =\u003e b.classList.toggle('on', x === k)); };\n      th.forEach(b =\u003e b.addEventListener('click', () =\u003e show(+b.dataset.j)));\n      const prev = qv.querySelector('.hq-prev'), next = qv.querySelector('.hq-next');\n      if (prev) prev.addEventListener('click', () =\u003e show(k - 1));\n      if (next) next.addEventListener('click', () =\u003e show(k + 1));\n      qv.querySelector('.hq-x').addEventListener('click', close);\n      const addBtn = qv.querySelector('.hq-add');\n      if (addBtn) addBtn.addEventListener('click', () =\u003e { close(); o.onAdd(); });\n      show(k);\n      qv.hidden = false;\n      document.body.classList.add('hq-lock');\n      qv.querySelector('.hq-x').focus();\n    };\n  })();\n  \/\/ single \u003c-\u003e duo switch, per product page\n  const SWITCH = {\n    'duo-french-twist-set': { h: 'Just need one pin?', p: 'Start with a single pin and add a second one later.', items: [{ handle: 'tortoiseshell-french-hair-pin', name: 'French Hair Pin', inc: '1 French Hair Pin in the color you choose', out: 'the second pin (the Duo is $65, less than two single pins)' }] },\n    'tortoiseshell-french-hair-pin': { h: 'Want a spare? Get the Duo', p: 'One pin for a quick twist, two for a fuller, more secure bun. $65 for two: less than two single pins, even with the 2-item discount.', items: [{ handle: 'duo-french-twist-set', name: 'Duo French Twist Set', inc: '2 French Hair Pins in the color you choose', out: '' }] }\n  };\n  \/\/ French Twist Kit, shown on these pages\n  const KIT_ON = ['duo-french-twist-set', 'tortoiseshell-french-hair-pin', 'elegant-claw-clip'];\n  const SET = [\n    { h: 'duo-french-twist-set', name: 'Duo French Twist Set', note: 'two French pins for the twist' },\n    { h: 'elegant-claw-clip', name: 'Elegant Claw Clip', note: 'for quick half-up days' },\n    { h: 'pearl-crystal-flower-hair-pin-set', name: 'Simulated Pearl \u0026 Crystal Flower Pins', note: 'for weddings and special days' }\n  ];\n  const PCT = 15; \/\/ store's automatic discount for 3+ items\n  const money = c =\u003e '$' + (c \/ 100).toFixed(2);\n  const img = (s, w) =\u003e s ? s + (s.includes('?') ? '\u0026' : '?') + 'width=' + w : '';\n  const go = async items =\u003e { const r = await fetch('\/cart\/add.js', { method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify({ items }) }); return r.ok; };\n  const start = () =\u003e {\n    const d = (cs \u0026\u0026 cs.parentElement.querySelector('.hd')) || document.querySelector('.hd');\n    const n = (cs \u0026\u0026 cs.parentElement.querySelector('.hn')) || document.querySelector('.hn');\n    if (!d || d.dataset.ready) return;\n    d.dataset.ready = 1;\n    const handle = d.dataset.handle;\n    const anchor = () =\u003e document.querySelector('.hb:not([hidden])') || (document.querySelector('.product__info-container product-form') || {}).parentElement;\n    const sw = SWITCH[handle];\n    if (sw) Promise.all(sw.items.map(o =\u003e fetch('\/products\/' + o.handle + '.js').then(r =\u003e r.ok ? r.json() : null).catch(() =\u003e null))).then(list =\u003e {\n      const rows = sw.items.map((o, i) =\u003e ({ o, p: list[i] })).filter(x =\u003e x.p \u0026\u0026 x.p.available);\n      if (!rows.length) return;\n      d.innerHTML = '\u003cp class=\"hd-h\"\u003e' + sw.h + '\u003c\/p\u003e\u003cp class=\"hd-p\"\u003e' + sw.p + '\u003c\/p\u003e' + rows.map((x, i) =\u003e {\n        const v = x.p.variants.find(v =\u003e v.available), lk = '\/products\/' + x.o.handle + '?variant=' + v.id;\n        return '\u003cdiv class=\"hd-card\" data-i=\"' + i + '\"\u003e' + (x.p.featured_image ? '\u003ca class=\"hd-img\" href=\"' + lk + '\"\u003e\u003cimg src=\"' + img(x.p.featured_image, 140) + '\" alt=\"\"\u003e\u003c\/a\u003e' : '') +\n          '\u003cdiv class=\"hd-b\"\u003e\u003ca class=\"hd-t\" href=\"' + lk + '\"\u003e' + x.o.name + ' · ' + money(v.price) + (v.compare_at_price \u003e v.price ? ' \u003cs\u003e' + money(v.compare_at_price) + '\u003c\/s\u003e' : '') + '\u003c\/a\u003e\u003cbr\u003e' +\n          '\u003cspan class=\"hd-in\"\u003eIncludes: ' + x.o.inc + '.\u003c\/span\u003e' + (x.o.out ? '\u003cbr\u003e\u003cspan class=\"hd-out\"\u003eNot included: ' + x.o.out + '.\u003c\/span\u003e' : '') + '\u003cbr\u003e' +\n          '\u003cbutton type=\"button\" class=\"hd-btn\" data-v=\"' + v.id + '\"\u003eAdd to cart — ' + money(v.price) + '\u003c\/button\u003e' + (x.p.variants.length \u003e 1 ? ' \u003csmall\u003e(' + v.title.split(' \/ ')[0] + '; pick another color on its page)\u003c\/small\u003e' : '') + '\u003c\/div\u003e\u003c\/div\u003e';\n      }).join('');\n      const a = anchor(); if (a) a.after(d);\n      d.hidden = false;\n      d.addEventListener('click', ev =\u003e {\n        const l = ev.target.closest('a.hd-t, a.hd-img'); if (!l) return; ev.preventDefault();\n        const card = l.closest('.hd-card'), x = rows[+card.dataset.i], b = card.querySelector('.hd-btn');\n        window.hhQV({ a: x.p, v: x.p.variants.find(v =\u003e v.available), from: l, note: 'Includes: ' + x.o.inc + '.', addLabel: b.textContent, onAdd: () =\u003e b.click() });\n      });\n      d.querySelectorAll('.hd-btn').forEach(b =\u003e b.addEventListener('click', async () =\u003e { b.disabled = true; b.textContent = 'Adding…'; if (await go([{ id: +b.dataset.v, quantity: 1 }])) location.href = '\/cart'; else { b.disabled = false; b.textContent = 'Could not add. Try again'; } }));\n    });\n    if (n \u0026\u0026 KIT_ON.includes(handle)) Promise.all(SET.map(s =\u003e fetch('\/products\/' + s.h + '.js').then(r =\u003e r.ok ? r.json() : null).catch(() =\u003e null))).then(list =\u003e {\n      const rows = SET.map((s, i) =\u003e { const p = list[i]; if (!p) return null; const v = p.variants.find(v =\u003e v.available); return v ? { s, p, v } : null; });\n      if (rows.some(r =\u003e !r)) return;\n      const sum = rows.reduce((a, r) =\u003e a + r.v.price, 0), you = sum - Math.floor(sum * PCT \/ 100);\n      const lk = r =\u003e '\/products\/' + r.s.h + '?variant=' + r.v.id;\n      n.innerHTML = '\u003cp class=\"hn-k\"\u003eThe French Twist Kit\u003c\/p\u003e\u003cp class=\"hn-h\"\u003eEverything for an effortless updo.\u003c\/p\u003e' +\n        '\u003cp class=\"hn-p\"\u003eTwo French pins for the twist, a claw clip for quick half-up days, and simulated pearl flower pins for special occasions.\u003c\/p\u003e' +\n        '\u003cdiv class=\"hn-list\"\u003e' + rows.map((r, i) =\u003e '\u003cdiv class=\"hn-row\" data-i=\"' + i + '\"\u003e' + (r.p.featured_image ? '\u003ca class=\"hn-img\" href=\"' + lk(r) + '\"\u003e\u003cimg src=\"' + img(r.p.featured_image, 100) + '\" alt=\"\"\u003e\u003c\/a\u003e' : '') +\n          '\u003cspan\u003e\u003ca class=\"hn-a\" href=\"' + lk(r) + '\"\u003e' + r.s.name + '\u003c\/a\u003e\u003csmall\u003e' + r.s.note + (r.p.variants.length \u003e 1 ? ' · ' + r.v.title.split(' \/ ')[0] : '') + '\u003c\/small\u003e\u003c\/span\u003e\u003cb\u003e' + money(r.v.price) + '\u003c\/b\u003e\u003c\/div\u003e').join('') + '\u003c\/div\u003e' +\n        '\u003cdiv class=\"hn-sum\"\u003e\u003cspan\u003eBought separately\u003c\/span\u003e\u003cspan\u003e' + money(sum) + '\u003c\/span\u003e\u003c\/div\u003e' +\n        '\u003cdiv class=\"hn-sum hn-you\"\u003e\u003cspan\u003eWith ' + PCT + '% off 3+ items\u003c\/span\u003e\u003cspan\u003e' + money(you) + '\u003c\/span\u003e\u003c\/div\u003e' +\n        '\u003cbutton type=\"button\" class=\"hn-btn\"\u003eAdd all ' + rows.length + ' to cart — ' + money(you) + ' at checkout\u003c\/button\u003e' +\n        '\u003cp class=\"hn-note\"\u003eThe ' + PCT + '% applies automatically at checkout and can’t be combined with other discount codes. Items may arrive in separate parcels, each with its own tracking number.\u003c\/p\u003e';\n      const a = (d.hidden ? null : d) || anchor(); if (a) a.after(n);\n      n.hidden = false;\n      const btn = n.querySelector('.hn-btn');\n      const pickColor = () =\u003e { const i = document.querySelector('variant-picker input:checked, variant-selects input:checked, variant-radios input:checked'); return i ? i.value : null; };\n      const pickDuo = r =\u003e { const c = pickColor(); return (c \u0026\u0026 r.p.variants.find(v =\u003e v.available \u0026\u0026 v.title.split(' \/ ')[0] === c)) || r.v; };\n      const relabel = () =\u003e { rows.forEach((r, i) =\u003e { if (r.s.h !== 'duo-french-twist-set' || r.p.variants.length \u003c 2) return; const el = n.querySelector('.hn-row[data-i=\"' + i + '\"] small'); if (el) el.textContent = r.s.note + ' · ' + pickDuo(r).title.split(' \/ ')[0]; }); };\n      document.addEventListener('change', () =\u003e setTimeout(relabel, 200)); relabel();\n      btn.addEventListener('click', async () =\u003e { btn.disabled = true; btn.textContent = 'Adding…'; if (await go(rows.map(x =\u003e ({ id: (x.s.h === 'duo-french-twist-set' ? pickDuo(x) : x.v).id, quantity: 1 })))) location.href = '\/cart'; else { btn.disabled = false; btn.textContent = 'Could not add. Try again'; } });\n      n.addEventListener('click', ev =\u003e {\n        const l = ev.target.closest('a.hn-a, a.hn-img'); if (!l) return; ev.preventDefault();\n        const r = rows[+l.closest('.hn-row').dataset.i];\n        window.hhQV({ a: r.p, v: r.v, from: l, note: 'Part of the French Twist Kit: ' + r.s.note + '.', addLabel: btn.textContent, onAdd: () =\u003e btn.click() });\n      });\n    });\n  };\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () =\u003e setTimeout(start, 50)); else setTimeout(start, 50);\n})();\n\u003c\/script\u003e\n\u003c!-- \/hv-extra v1 --\u003e\n\u003c!-- hv-tiers v1 (Havendoa): \"Buy more, save more\" 1\/2\/3 items, replaces the quantity box. Must match automatic discounts (2 items 10%, 3+ items 15%). Remove from the hv-tiers v1 start marker to the \/hv-tiers v1 end marker to undo. --\u003e\n\u003cdiv class=\"hv-tiers\" data-handle=\"tortoiseshell-french-hair-pin\" hidden\u003e\u003c\/div\u003e\n\u003cstyle\u003e\n.hv-tiers{margin:4px 0 16px;background:#f5efe8;border:1px solid #e5d6c6;border-radius:14px;padding:12px 12px 6px;font-size:15px;color:#2b2b2b}\n.hv-tiers__h{font-weight:700;font-size:16px;margin:0 0 8px;color:#7a5534}\n.hv-tier{display:flex;align-items:center;gap:10px;background:#fff;border:1px solid #eadfd3;border-radius:10px;padding:10px 12px;margin:0 0 8px;cursor:pointer}\n.hv-tier.is-on{box-shadow:0 0 0 2px #7a5534;border-color:#7a5534}\n.hv-tier input{width:20px;height:20px;accent-color:#7a5534;margin:0;flex:0 0 20px}\n.hv-tier__t{flex:1;min-width:0;line-height:1.3}\n.hv-tier__t small{display:block;color:#6b6b6b;font-size:12.5px}\n.hv-tier__b{display:inline-block;margin-left:6px;background:#7a5534;color:#fff;border-radius:20px;font-size:11.5px;font-weight:700;padding:2px 8px;vertical-align:middle}\n.hv-tier__p{text-align:right;font-weight:700;white-space:nowrap}\n.hv-tier__p s{display:block;font-weight:400;font-size:12.5px;color:#8a8a8a}\n.hv-tiers__note{font-size:12.5px;color:#6b6b6b;margin:0 2px 6px}\n\u003c\/style\u003e\n\u003cscript\u003e\n(() =\u003e {\n  const cs = document.currentScript;\n  const TIERS = [[1, 0, ''], [2, 10, 'Save 10%'], [3, 15, 'Save 15%']];\n  const money = c =\u003e '$' + (c \/ 100).toFixed(2);\n  const start = () =\u003e {\n    const box = (cs \u0026\u0026 cs.parentElement.querySelector('.hv-tiers')) || document.querySelector('.hv-tiers');\n    if (!box || box.dataset.ready) return;\n    box.dataset.ready = 1;\n    const info = document.querySelector('.product__info-container');\n    const form = info \u0026\u0026 info.querySelector('form[action*=\"\/cart\/add\"][data-type=\"add-to-cart-form\"]');\n    if (!info || !form) return;\n    const blockOf = el =\u003e { while (el \u0026\u0026 el.parentElement !== info) el = el.parentElement; return el; };\n    const qtyBox = info.querySelector('.product-form__quantity');\n    const anchor = qtyBox ? blockOf(qtyBox) : blockOf(form);\n    const qty = () =\u003e info.querySelector('input[name=\"quantity\"]') || document.querySelector('input[name=\"quantity\"]');\n    fetch('\/products\/' + box.dataset.handle + '.js').then(r =\u003e r.ok ? r.json() : null).then(p =\u003e {\n      if (!p) return;\n      const cur = () =\u003e { const id = +(form.querySelector('input[name=\"id\"]') || {}).value; return p.variants.find(v =\u003e v.id === id) || p.variants.find(v =\u003e v.available) || p.variants[0]; };\n      const render = () =\u003e {\n        const v = cur(), on = box.querySelector('input:checked'), pick = on ? +on.value : Math.max(1, Math.min(3, parseInt((qty() || {}).value, 10) || 1));\n        box.innerHTML = '\u003cp class=\"hv-tiers__h\"\u003eBuy more, save more\u003c\/p\u003e' + TIERS.map(([n, pct, badge]) =\u003e {\n          const full = v.price * n, you = full - Math.floor(full * pct \/ 100);\n          return '\u003clabel class=\"hv-tier' + (n === pick ? ' is-on' : '') + '\"\u003e\u003cinput type=\"radio\" name=\"hv-tier\" value=\"' + n + '\"' + (n === pick ? ' checked' : '') + '\u003e' +\n            '\u003cspan class=\"hv-tier__t\"\u003e\u003cb\u003e' + n + (n === 1 ? ' item' : ' items') + '\u003c\/b\u003e' + (badge ? '\u003cspan class=\"hv-tier__b\"\u003e' + badge + '\u003c\/span\u003e' : '') + (p.variants.length \u003e 1 ? '\u003csmall\u003e' + v.title.split(' \/ ')[0] + (n \u003e 1 ? ' x ' + n : '') + '\u003c\/small\u003e' : '') + '\u003c\/span\u003e' +\n            '\u003cspan class=\"hv-tier__p\"\u003e' + money(you) + (pct ? '\u003cs\u003e' + money(full) + '\u003c\/s\u003e' : '') + '\u003c\/span\u003e\u003c\/label\u003e';\n        }).join('') + '\u003cp class=\"hv-tiers__note\"\u003eWant different colors? Add each one to your cart: the discount counts every item in your order.\u003c\/p\u003e';\n      };\n      const setQty = n =\u003e { const q = qty(); if (q \u0026\u0026 q.value !== String(n)) { q.value = n; q.dispatchEvent(new Event('change', { bubbles: true })); } };\n      anchor.before(box);\n      if (qtyBox) anchor.style.display = 'none';\n      render(); box.hidden = false;\n      box.addEventListener('change', e =\u003e { if (e.target.name !== 'hv-tier') return; setQty(+e.target.value); render(); });\n      \/\/ the theme resets quantity to 1 on every variant change; put the tier back and refresh prices\n      document.addEventListener('change', e =\u003e { if (box.contains(e.target)) return; if (e.target \u0026\u0026 e.target.name === 'id') setTimeout(() =\u003e { const t = box.querySelector('input:checked'); if (t) setQty(+t.value); render(); }, 60); });\n      window.addEventListener('pageshow', () =\u003e setTimeout(() =\u003e { const t = box.querySelector('input:checked'); if (t) setQty(+t.value); }, 0));\n    });\n  };\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n})();\n\u003c\/script\u003e\n\u003c!-- \/hv-tiers v1 --\u003e","brand":"Havendoa","offers":[{"title":"Tortoiseshell","offer_id":49283867443383,"sku":"HV-FP-TOR","price":38.0,"currency_code":"USD","in_stock":true},{"title":"Black","offer_id":49284260069559,"sku":"HV-FP-BLK","price":38.0,"currency_code":"USD","in_stock":true},{"title":"Olive Green","offer_id":49284260102327,"sku":"HV-FP-OLV","price":38.0,"currency_code":"USD","in_stock":true},{"title":"Burgundy","offer_id":49284260135095,"sku":"HV-FP-BUR","price":38.0,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0761\/4998\/1367\/files\/Sec553b7afeb044b2be4f15f42cbf2177A.webp?v=1790938300"},{"product_id":"duo-french-twist-set","title":"Duo French Twist Set – 2 Matching French Hair Pins","description":"\u003ch2\u003eTwo pins, two ways to wear them\u003c\/h2\u003e\n\u003cp\u003eOne French pin is designed for a simple twist. On long or thick hair, cross in the second pin from the other side for extra hold. On other days, keep the spare in your work bag, so you’re never hunting for a hair tie.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eTwo pins, one price.\u003c\/strong\u003e The Duo is $65. Two single French Hair Pins are $76 if bought separately, or $68.40 with our automatic 2-item discount. Want two different colors? Choose two \u003ca href=\"\/products\/tortoiseshell-french-hair-pin\"\u003esingle French Hair Pins\u003c\/a\u003e instead.\u003c\/p\u003e\n\u003c!-- hv-upsell v1 (Havendoa, adapted from hh-galixo v3): add-ons with quick view, delivery, trust. Remove from the hv-upsell v1 start marker to the \/hv-upsell v1 end marker to undo. --\u003e\n\u003cdiv class=\"hb\" data-handle=\"duo-french-twist-set\" hidden\u003e\u003cdiv class=\"hb-pay\"\u003e\u003c\/div\u003e\u003c\/div\u003e\n\u003cstyle\u003e\n.hb{--g:#7a5534;--line:#e3e3e3;font-size:15px;color:#2b2b2b}\n.hb-pay{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin:12px 0 0}\n.hb-pay svg{width:38px;height:24px}\n.hb-add{margin:16px 0 0;border-radius:12px;background:#f5efe8;padding:12px}\n.hb-add-h{font-weight:700;font-size:17px;color:#1d1d1d;margin:0}\n.hb-add-p{font-size:14px;color:#555;margin:3px 0 6px;line-height:1.45}\n.hb-item{display:flex;align-items:center;gap:10px;background:#fff;border-radius:8px;padding:9px 10px;margin-top:7px;cursor:pointer}\n.hb-item input{width:20px;height:20px;accent-color:var(--g);flex:0 0 20px;margin:0}\n.hb-item img{width:54px;height:54px;object-fit:cover;border-radius:6px;flex:0 0 54px}\n.hb-item-t{flex:1;min-width:0;font-size:14px;line-height:1.35}\n.hb-item-t small{display:block;color:#6b6b6b;font-size:12.5px}\n.hb-item-p{text-align:right;white-space:nowrap;font-weight:700}\n.hb-item-p span{display:block;font-weight:400;font-size:12.5px;color:#c0392b}\n.hb-item-p s{color:#8a8a8a;margin-left:4px}\n.hb-ship{text-align:center;margin:16px 0 0;font-size:15px;line-height:1.5}\n.hb-ship b{color:var(--g)}\n.hb-trust{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:14px 0 18px;text-align:center;font-weight:700;font-size:13.5px;color:var(--g)}\n.hb-trust svg{display:block;margin:0 auto 5px;width:32px;height:32px}\n.hb-err{color:#c0392b;text-align:center;font-size:14px;margin-top:6px}\n.hb-item-a{color:inherit;text-decoration:underline;text-underline-offset:2px}\n.hb-item-img{display:block;flex:0 0 54px}\n.hb-ck{text-align:center;margin:0 0 16px;font-size:14px}\n.hb-ck a{color:var(--g);font-weight:600}\n.hq{position:fixed;inset:0;z-index:2147483000;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;padding:16px}\n.hq[hidden]{display:none}\n.hq-box{background:#fff;border-radius:14px;max-width:780px;width:100%;max-height:90vh;overflow:auto;position:relative;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:28px;padding:30px 26px 24px;color:#1d1d1d;font-size:15px}\n.hq-box\u003ediv{min-width:0}\n.hq-x{position:absolute;top:8px;right:10px;background:none;border:0;font-size:28px;line-height:1;cursor:pointer;color:#444;padding:4px 8px}\n.hq-main{position:relative;aspect-ratio:1\/1;display:flex;align-items:center;justify-content:center}\n.hq-main img{max-width:100%;max-height:100%;object-fit:contain}\n.hq-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.9);border:0;border-radius:50%;width:36px;height:36px;font-size:24px;line-height:1;cursor:pointer;color:#333}\n.hq-prev{left:0}.hq-next{right:0}\n.hq-th{display:flex;gap:10px;overflow-x:auto;margin-top:14px;padding-bottom:4px}\n.hq-th button{flex:0 0 58px;padding:0 0 6px;border:0;border-bottom:2px solid transparent;background:none;cursor:pointer}\n.hq-th button.on{border-bottom-color:#1d1d1d}\n.hq-th img{width:58px;height:52px;object-fit:cover;border-radius:4px;display:block}\n.hq-t{font-size:21px;line-height:1.35;margin:0 30px 8px 0;font-weight:500}\n.hq-v{font-size:13.5px;color:#6b6b6b;margin:0 0 8px}\n.hq-p{font-size:20px;font-weight:700;margin:0 0 4px}\n.hq-p s{font-size:15px;color:#8a8a8a;font-weight:400;margin-left:6px}\n.hq-save{font-size:13.5px;color:#7a5534;margin:0 0 14px}\n.hq-ul{list-style:none;padding:0;margin:0 0 18px;font-size:14.5px;line-height:1.5}\n.hq-ul li{position:relative;padding-left:27px;margin:0 0 6px}\n.hq-ul li:before{content:\"\\2713\";position:absolute;left:0;top:2px;width:19px;height:19px;border-radius:4px;background:#a57549;color:#fff;font-size:13px;font-weight:700;line-height:19px;text-align:center}\n.hq-add{display:block;width:100%;border:0;border-radius:40px;background:#7a5534;color:#fff;font:inherit;font-weight:700;padding:12px;cursor:pointer;margin:0 0 14px}\n.hq-more{display:inline-block;color:#1d1d1d;font-weight:600;text-decoration:none;font-size:15.5px}\nbody.hq-lock{overflow:hidden}\n@media (max-width:640px){.hq-box{grid-template-columns:minmax(0,1fr);gap:14px;padding:42px 16px 18px}.hq-t{font-size:18px;margin-right:0}}\n\u003c\/style\u003e\n\u003cscript\u003e\n(() =\u003e {\n  const cs = document.currentScript;\n  const start = () =\u003e {\n  const root = (cs \u0026\u0026 cs.parentElement.querySelector('.hb')) || document.querySelector('.hb');\n  if (!root || root.dataset.ready) return;\n  root.dataset.ready = 1;\n  \/\/ what to offer under each product (handle -\u003e handles)\n  const PAIR = {\n    'duo-french-twist-set': ['elegant-claw-clip', 'pearl-crystal-flower-hair-pin-set', 'geometric-hair-clips-set-of-2'],\n    'tortoiseshell-french-hair-pin': ['elegant-claw-clip', 'pearl-crystal-flower-hair-pin-set'],\n    'elegant-claw-clip': ['tortoiseshell-french-hair-pin', 'geometric-hair-clips-set-of-2'],\n    'geometric-hair-clips-set-of-2': ['elegant-claw-clip', 'tortoiseshell-french-hair-pin'],\n    'pearl-crystal-flower-hair-pin-set': ['vintage-crystal-hair-pin-set-of-3', 'tortoiseshell-french-hair-pin'],\n    'vintage-crystal-hair-pin-set-of-3': ['pearl-crystal-flower-hair-pin-set', 'tortoiseshell-french-hair-pin'],\n    'heart-pearl-hair-clip': ['pearl-crystal-flower-hair-pin-set', 'vintage-crystal-hair-pin-set-of-3']\n  };\n  const HEAD = {};\n  \/\/ must match the store's automatic discounts: Buy 2 items 10%, Buy 3+ items 15%\n  const pctFor = n =\u003e n \u003e= 3 ? 15 : n \u003e= 2 ? 10 : 0;\n  const money = c =\u003e '$' + (c \/ 100).toFixed(2);\n  const info = document.querySelector('.product__info-container');\n  const form = info \u0026\u0026 info.querySelector('form[action*=\"\/cart\/add\"][data-type=\"add-to-cart-form\"]');\n  if (!info || !form) return;\n  const blockOf = el =\u003e { while (el \u0026\u0026 el.parentElement !== info) el = el.parentElement; return el; };\n  const buyBlock = blockOf(form);\n  const qtyInput = () =\u003e info.querySelector('.product-form__quantity input[name=\"quantity\"]') || document.querySelector('input[name=\"quantity\"]');\n  const mainQty = () =\u003e Math.max(1, parseInt((qtyInput() || {}).value, 10) || 1);\n  buyBlock.after(root);\n  root.hidden = false;\n  const pay = root.querySelector('.hb-pay'), footerPay = document.querySelector('.list-payment');\n  if (!pay.querySelector('svg') \u0026\u0026 footerPay) pay.innerHTML = [...footerPay.querySelectorAll('svg')].map(s =\u003e s.outerHTML).join('');\n  if (!pay.querySelector('svg')) pay.remove();\n  const icon = d =\u003e '\u003csvg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u003e' + d + '\u003c\/svg\u003e';\n  root.insertAdjacentHTML('beforeend',\n    '\u003cdiv class=\"hb-add\" hidden\u003e\u003c\/div\u003e' +\n    '\u003cdiv class=\"hb-ship\"\u003e\u003cb\u003eFree US shipping\u003c\/b\u003e on orders $60+.\u003cbr\u003eTypical delivery: 5–11 days after dispatch.\u003c\/div\u003e' +\n    '\u003cdiv class=\"hb-trust\"\u003e' +\n    '\u003cdiv\u003e' + icon('\u003cpath d=\"M3 7h11v8H3zM14 10h4l3 3v2h-7\"\/\u003e\u003ccircle cx=\"7\" cy=\"17\" r=\"1.6\"\/\u003e\u003ccircle cx=\"17\" cy=\"17\" r=\"1.6\"\/\u003e') + 'Free Shipping $60+\u003c\/div\u003e' +\n    '\u003cdiv\u003e' + icon('\u003cpath d=\"M4 12a8 8 0 1 0 2.4-5.7\"\/\u003e\u003cpath d=\"M4 4v4h4\"\/\u003e') + '30-Day Returns\u003c\/div\u003e' +\n    '\u003cdiv\u003e' + icon('\u003crect x=\"5\" y=\"11\" width=\"14\" height=\"9\" rx=\"2\"\/\u003e\u003cpath d=\"M8 11V8a4 4 0 0 1 8 0v3\"\/\u003e') + 'Secure Checkout\u003c\/div\u003e' +\n    '\u003c\/div\u003e' +\n    '\u003cp class=\"hb-ck\"\u003e\u003ca href=\"\/blogs\/hair-guides\/how-to-use-a-french-hair-pin\"\u003eNew to French pins? Read our step-by-step guide \u0026rarr;\u003c\/a\u003e\u003c\/p\u003e');\n  const handles = PAIR[root.dataset.handle] || [];\n  let extras = [];\n  const add = root.querySelector('.hb-add');\n  const picks = () =\u003e [...add.querySelectorAll('input:checked')].map(c =\u003e extras[+c.dataset.x]);\n  const paint = () =\u003e {\n    const n = mainQty() + picks().length;\n    add.querySelectorAll('.hb-item').forEach(el =\u003e {\n      const e = extras[+el.dataset.x], on = el.querySelector('input').checked;\n      const pct = pctFor(on ? n : n + 1);\n      el.querySelector('.hb-item-p').innerHTML = (pct ? '\u003cspan\u003e-' + pct + '%\u003cs\u003e' + money(e.v.price) + '\u003c\/s\u003e\u003c\/span\u003e' : '') + money(e.v.price - Math.floor(e.v.price * pct \/ 100));\n    });\n  };\n  Promise.all(handles.map(h =\u003e fetch('\/products\/' + h + '.js').then(r =\u003e r.ok ? r.json() : null).catch(() =\u003e null))).then(list =\u003e {\n    extras = list.filter(Boolean).map(a =\u003e { const v = a.variants.find(x =\u003e x.available); return v \u0026\u0026 { a, v }; }).filter(Boolean);\n    if (!extras.length) return;\n    const img = s =\u003e s ? s + (s.includes('?') ? '\u0026' : '?') + 'width=120' : '';\n    const link = e =\u003e '\/products\/' + e.a.handle + '?variant=' + e.v.id;\n    add.innerHTML = '\u003cp class=\"hb-add-h\"\u003e' + (HEAD[root.dataset.handle] || 'Complete the look') + '\u003c\/p\u003e' +\n      '\u003cp class=\"hb-add-p\"\u003eCheck the box to add it to this order. 2 items save 10%, 3 or more save 15%, applied automatically at checkout.\u003c\/p\u003e' +\n      extras.map((e, i) =\u003e '\u003clabel class=\"hb-item\" data-x=\"' + i + '\"\u003e\u003cinput type=\"checkbox\" data-x=\"' + i + '\"\u003e' +\n        (e.a.featured_image ? '\u003ca class=\"hb-item-img\" href=\"' + link(e) + '\" tabindex=\"-1\" aria-hidden=\"true\"\u003e\u003cimg src=\"' + img(e.a.featured_image) + '\" alt=\"\" loading=\"lazy\"\u003e\u003c\/a\u003e' : '') +\n        '\u003cspan class=\"hb-item-t\"\u003e\u003ca class=\"hb-item-a\" href=\"' + link(e) + '\"\u003e' + e.a.title.split(' – ')[0] + '\u003c\/a\u003e' + (e.a.variants.length \u003e 1 ? '\u003csmall\u003e' + e.v.title.split(' \/ ')[0] + '\u003c\/small\u003e' : '') + '\u003c\/span\u003e' +\n        '\u003cspan class=\"hb-item-p\"\u003e\u003c\/span\u003e\u003c\/label\u003e').join('');\n    add.hidden = false;\n    add.addEventListener('click', ev =\u003e {\n      const a = ev.target.closest('.hb-item-a, .hb-item-img');\n      if (!a || !window.hhQV) return;\n      ev.preventDefault();\n      const i = +a.closest('.hb-item').dataset.x, e = extras[i], cb = add.querySelector('input[data-x=\"' + i + '\"]');\n      window.hhQV({ a: e.a, v: e.v, from: a, note: 'Add it to this order: 2 items save 10%, 3 or more save 15% at checkout.', addLabel: cb.checked ? 'Remove from this order' : 'Add to this order', onAdd: () =\u003e { cb.checked = !cb.checked; cb.dispatchEvent(new Event('change', { bubbles: true })); } });\n    });\n    add.addEventListener('change', paint);\n    document.addEventListener('change', e =\u003e { if (e.target.name === 'quantity') setTimeout(paint, 50); });\n    paint();\n  });\n  document.addEventListener('submit', async e =\u003e {\n    if (e.target !== form || !picks().length) return;\n    e.preventDefault(); e.stopImmediatePropagation();\n    const btn = form.querySelector('[type=\"submit\"]'); if (btn) btn.setAttribute('aria-disabled', 'true');\n    const items = [{ id: +form.querySelector('input[name=\"id\"]').value, quantity: mainQty() }, ...picks().map(x =\u003e ({ id: x.v.id, quantity: 1 }))];\n    let r = null;\n    try { r = await fetch('\/cart\/add.js', { method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify({ items }) }); } catch (x) {}\n    if (r \u0026\u0026 r.ok) { location.href = '\/cart'; return; }\n    if (btn) btn.removeAttribute('aria-disabled');\n    let err = root.querySelector('.hb-err'); if (!err) { err = document.createElement('p'); err.className = 'hb-err'; add.after(err); }\n    err.textContent = (r \u0026\u0026 (await r.json().catch(() =\u003e ({}))).description) || 'Could not add to cart. Please try again.';\n  }, true);\n  window.addEventListener('pageshow', e =\u003e { if (e.persisted) { const b = form.querySelector('[type=\"submit\"]'); if (b) b.removeAttribute('aria-disabled'); } });\n  };\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n})();\n\u003c\/script\u003e\n\u003c!-- \/hv-upsell v1 --\u003e\n\u003c!-- hv-extra v1 (Havendoa, adapted from hh-downsell v2 + hh-nopantry v2): shared quick view, single\/duo switch, French Twist Kit. Remove from the hv-extra v1 start marker to the \/hv-extra v1 end marker to undo. --\u003e\n\u003cdiv class=\"hd\" data-handle=\"duo-french-twist-set\" hidden\u003e\u003c\/div\u003e\u003cdiv class=\"hn\" data-handle=\"duo-french-twist-set\" hidden\u003e\u003c\/div\u003e\n\u003cstyle\u003e\n.hd{--g:#7a5534;margin:6px 0 18px;border:1px solid #e5d6c6;border-radius:12px;padding:12px;background:#fff;font-size:15px;color:#2b2b2b}\n.hd-h{font-weight:700;font-size:17px;margin:0}\n.hd-p{font-size:14px;color:#555;margin:3px 0 8px;line-height:1.45}\n.hd-card{display:flex;gap:10px;align-items:flex-start;border-top:1px solid #eee;padding:10px 0 4px}\n.hd-card img{width:62px;height:62px;object-fit:cover;border-radius:8px;flex:0 0 62px}\n.hd-b{flex:1;min-width:0;font-size:13.5px;line-height:1.4}\n.hd-b a.hd-t{font-weight:700;color:#1d1d1d;text-decoration:underline;text-underline-offset:2px;font-size:14.5px}\n.hd-img{display:block;flex:0 0 62px}\n.hd-b .hd-in{color:#2f6b52}\n.hd-b .hd-out{color:#8a5a2b}\n.hd-btn{margin-top:6px;border:1.5px solid var(--g);background:#fff;color:var(--g);border-radius:30px;padding:7px 14px;font:inherit;font-weight:700;font-size:13.5px;cursor:pointer}\n.hd-btn:disabled{opacity:.6}\n.hn{--g:#7a5534;margin:6px 0 18px;border:2px solid var(--g);border-radius:14px;padding:14px;background:#f8f2ec;font-size:15px;color:#2b2b2b}\n.hn-k{font-size:12px;letter-spacing:.08em;text-transform:uppercase;font-weight:700;color:#c75b2a;margin:0}\n.hn-h{font-weight:700;font-size:18px;margin:2px 0 4px;color:var(--g)}\n.hn-p{font-size:14px;color:#555;margin:0 0 10px;line-height:1.45}\n.hn-list{display:grid;gap:8px;margin:0 0 10px}\n.hn-row{display:flex;gap:10px;align-items:center;background:#fff;border-radius:10px;padding:8px}\n.hn-row img{width:48px;height:48px;object-fit:cover;border-radius:6px;flex:0 0 48px}\n.hn-row span{flex:1;min-width:0;font-size:13.5px;line-height:1.35}\n.hn-row span small{display:block;color:#6b6b6b}\n.hn-row b{white-space:nowrap;font-size:14px}\n.hn-sum{display:flex;justify-content:space-between;font-size:14px;margin:2px 2px 0}\n.hn-sum.hn-you{font-weight:700;font-size:16px;color:var(--g)}\n.hn-btn{display:block;width:100%;margin-top:10px;border:0;border-radius:40px;background:var(--g);color:#fff;font:inherit;font-weight:700;font-size:16px;padding:13px;cursor:pointer}\n.hn-btn:disabled{opacity:.6}\n.hn-a{color:inherit;text-decoration:underline;text-underline-offset:2px}\n.hn-img{display:block;flex:0 0 48px}\n.hn-note{font-size:12.5px;color:#6b6b6b;margin:8px 0 0;text-align:center}\n\u003c\/style\u003e\n\u003cscript\u003e\n(() =\u003e {\n  const cs = document.currentScript;\n  if (!window.hhQV) window.hhQV = (() =\u003e {\n    const qv = document.createElement('div');\n    qv.className = 'hq'; qv.hidden = true;\n    document.body.appendChild(qv);\n    const px = (s, w) =\u003e s ? s + (s.includes('?') ? '\u0026' : '?') + 'width=' + w : '';\n    const esc = s =\u003e String(s).replace(\/[\u0026\u003c\u003e\"]\/g, c =\u003e ({ '\u0026': '\u0026amp;', '\u003c': '\u0026lt;', '\u003e': '\u0026gt;', '\"': '\u0026quot;' }[c]));\n    const cash = c =\u003e '$' + (c \/ 100).toFixed(2);\n    const fileOf = s =\u003e String(s || '').split('?')[0].split('\/').pop();\n    const bullets = html =\u003e {\n      const d = new DOMParser().parseFromString(html || '', 'text\/html');\n      d.querySelectorAll('script, style, .hb, .hd, .hn').forEach(x =\u003e x.remove());\n      let li = [...d.querySelectorAll('li')].map(x =\u003e x.textContent.replace(\/\\s+\/g, ' ').trim()).filter(Boolean);\n      if (!li.length) li = [...d.querySelectorAll('p')].map(x =\u003e x.textContent.trim()).filter(Boolean).slice(0, 1);\n      return li.slice(0, 5).map(t =\u003e t.length \u003e 95 ? t.slice(0, 92).replace(\/[\\s,.;:]+\\S*$\/, '') + '…' : t);\n    };\n    let back = null;\n    const close = () =\u003e { qv.hidden = true; document.body.classList.remove('hq-lock'); if (back) back.focus(); };\n    qv.addEventListener('click', ev =\u003e { if (ev.target === qv) close(); });\n    document.addEventListener('keydown', ev =\u003e {\n      if (qv.hidden) return;\n      if (ev.key === 'Escape') close();\n      if (ev.key === 'ArrowLeft') { const b = qv.querySelector('.hq-prev'); if (b) b.click(); }\n      if (ev.key === 'ArrowRight') { const b = qv.querySelector('.hq-next'); if (b) b.click(); }\n    });\n    return o =\u003e {\n      const a = o.a, v = o.v;\n      back = o.from || null;\n      const imgs = (a.images \u0026\u0026 a.images.length) ? a.images : [a.featured_image];\n      let k = v.featured_image ? Math.max(0, imgs.findIndex(s =\u003e fileOf(s) === fileOf(v.featured_image.src))) : 0;\n      qv.innerHTML = '\u003cdiv class=\"hq-box\" role=\"dialog\" aria-modal=\"true\" aria-label=\"' + esc(a.title) + '\"\u003e' +\n        '\u003cbutton type=\"button\" class=\"hq-x\" aria-label=\"Close\"\u003e\u0026times;\u003c\/button\u003e' +\n        '\u003cdiv\u003e\u003cdiv class=\"hq-main\"\u003e\u003cimg alt=\"\"\u003e' +\n        (imgs.length \u003e 1 ? '\u003cbutton type=\"button\" class=\"hq-nav hq-prev\" aria-label=\"Previous image\"\u003e\u0026lsaquo;\u003c\/button\u003e\u003cbutton type=\"button\" class=\"hq-nav hq-next\" aria-label=\"Next image\"\u003e\u0026rsaquo;\u003c\/button\u003e' : '') +\n        '\u003c\/div\u003e' + (imgs.length \u003e 1 ? '\u003cdiv class=\"hq-th\"\u003e' + imgs.map((s, j) =\u003e '\u003cbutton type=\"button\" data-j=\"' + j + '\" aria-label=\"Image ' + (j + 1) + '\"\u003e\u003cimg src=\"' + px(s, 120) + '\" alt=\"\" loading=\"lazy\"\u003e\u003c\/button\u003e').join('') + '\u003c\/div\u003e' : '') + '\u003c\/div\u003e' +\n        '\u003cdiv\u003e\u003ch2 class=\"hq-t\"\u003e' + esc(a.title) + '\u003c\/h2\u003e' +\n        (a.variants.length \u003e 1 ? '\u003cp class=\"hq-v\"\u003e' + esc(v.title.split(' \/ ')[0]) + '\u003c\/p\u003e' : '') +\n        '\u003cp class=\"hq-p\"\u003e' + cash(v.price) + (v.compare_at_price \u003e v.price ? '\u003cs\u003e' + cash(v.compare_at_price) + '\u003c\/s\u003e' : '') + '\u003c\/p\u003e' +\n        (o.note ? '\u003cp class=\"hq-save\"\u003e' + esc(o.note) + '\u003c\/p\u003e' : '') +\n        '\u003cul class=\"hq-ul\"\u003e' + bullets(a.description).map(t =\u003e '\u003cli\u003e' + esc(t) + '\u003c\/li\u003e').join('') + '\u003c\/ul\u003e' +\n        (o.onAdd ? '\u003cbutton type=\"button\" class=\"hq-add\"\u003e' + esc(o.addLabel || 'Add to cart') + '\u003c\/button\u003e' : '') +\n        '\u003ca class=\"hq-more\" href=\"\/products\/' + a.handle + '?variant=' + v.id + '\"\u003eView more details \u003cspan aria-hidden=\"true\"\u003e\u0026rarr;\u003c\/span\u003e\u003c\/a\u003e\u003c\/div\u003e\u003c\/div\u003e';\n      const main = qv.querySelector('.hq-main img'), th = [...qv.querySelectorAll('.hq-th button')];\n      const show = j =\u003e { k = (j + imgs.length) % imgs.length; main.src = px(imgs[k], 800); th.forEach((b, x) =\u003e b.classList.toggle('on', x === k)); };\n      th.forEach(b =\u003e b.addEventListener('click', () =\u003e show(+b.dataset.j)));\n      const prev = qv.querySelector('.hq-prev'), next = qv.querySelector('.hq-next');\n      if (prev) prev.addEventListener('click', () =\u003e show(k - 1));\n      if (next) next.addEventListener('click', () =\u003e show(k + 1));\n      qv.querySelector('.hq-x').addEventListener('click', close);\n      const addBtn = qv.querySelector('.hq-add');\n      if (addBtn) addBtn.addEventListener('click', () =\u003e { close(); o.onAdd(); });\n      show(k);\n      qv.hidden = false;\n      document.body.classList.add('hq-lock');\n      qv.querySelector('.hq-x').focus();\n    };\n  })();\n  \/\/ single \u003c-\u003e duo switch, per product page\n  const SWITCH = {\n    'duo-french-twist-set': { h: 'Just need one pin?', p: 'Start with a single pin and add a second one later.', items: [{ handle: 'tortoiseshell-french-hair-pin', name: 'French Hair Pin', inc: '1 French Hair Pin in the color you choose', out: 'the second pin (the Duo is $65, less than two single pins)' }] },\n    'tortoiseshell-french-hair-pin': { h: 'Want a spare? Get the Duo', p: 'One pin for a quick twist, two for a fuller, more secure bun. $65 for two: less than two single pins, even with the 2-item discount.', items: [{ handle: 'duo-french-twist-set', name: 'Duo French Twist Set', inc: '2 French Hair Pins in the color you choose', out: '' }] }\n  };\n  \/\/ French Twist Kit, shown on these pages\n  const KIT_ON = ['duo-french-twist-set', 'tortoiseshell-french-hair-pin', 'elegant-claw-clip'];\n  const SET = [\n    { h: 'duo-french-twist-set', name: 'Duo French Twist Set', note: 'two French pins for the twist' },\n    { h: 'elegant-claw-clip', name: 'Elegant Claw Clip', note: 'for quick half-up days' },\n    { h: 'pearl-crystal-flower-hair-pin-set', name: 'Simulated Pearl \u0026 Crystal Flower Pins', note: 'for weddings and special days' }\n  ];\n  const PCT = 15; \/\/ store's automatic discount for 3+ items\n  const money = c =\u003e '$' + (c \/ 100).toFixed(2);\n  const img = (s, w) =\u003e s ? s + (s.includes('?') ? '\u0026' : '?') + 'width=' + w : '';\n  const go = async items =\u003e { const r = await fetch('\/cart\/add.js', { method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify({ items }) }); return r.ok; };\n  const start = () =\u003e {\n    const d = (cs \u0026\u0026 cs.parentElement.querySelector('.hd')) || document.querySelector('.hd');\n    const n = (cs \u0026\u0026 cs.parentElement.querySelector('.hn')) || document.querySelector('.hn');\n    if (!d || d.dataset.ready) return;\n    d.dataset.ready = 1;\n    const handle = d.dataset.handle;\n    const anchor = () =\u003e document.querySelector('.hb:not([hidden])') || (document.querySelector('.product__info-container product-form') || {}).parentElement;\n    const sw = SWITCH[handle];\n    if (sw) Promise.all(sw.items.map(o =\u003e fetch('\/products\/' + o.handle + '.js').then(r =\u003e r.ok ? r.json() : null).catch(() =\u003e null))).then(list =\u003e {\n      const rows = sw.items.map((o, i) =\u003e ({ o, p: list[i] })).filter(x =\u003e x.p \u0026\u0026 x.p.available);\n      if (!rows.length) return;\n      d.innerHTML = '\u003cp class=\"hd-h\"\u003e' + sw.h + '\u003c\/p\u003e\u003cp class=\"hd-p\"\u003e' + sw.p + '\u003c\/p\u003e' + rows.map((x, i) =\u003e {\n        const v = x.p.variants.find(v =\u003e v.available), lk = '\/products\/' + x.o.handle + '?variant=' + v.id;\n        return '\u003cdiv class=\"hd-card\" data-i=\"' + i + '\"\u003e' + (x.p.featured_image ? '\u003ca class=\"hd-img\" href=\"' + lk + '\"\u003e\u003cimg src=\"' + img(x.p.featured_image, 140) + '\" alt=\"\"\u003e\u003c\/a\u003e' : '') +\n          '\u003cdiv class=\"hd-b\"\u003e\u003ca class=\"hd-t\" href=\"' + lk + '\"\u003e' + x.o.name + ' · ' + money(v.price) + (v.compare_at_price \u003e v.price ? ' \u003cs\u003e' + money(v.compare_at_price) + '\u003c\/s\u003e' : '') + '\u003c\/a\u003e\u003cbr\u003e' +\n          '\u003cspan class=\"hd-in\"\u003eIncludes: ' + x.o.inc + '.\u003c\/span\u003e' + (x.o.out ? '\u003cbr\u003e\u003cspan class=\"hd-out\"\u003eNot included: ' + x.o.out + '.\u003c\/span\u003e' : '') + '\u003cbr\u003e' +\n          '\u003cbutton type=\"button\" class=\"hd-btn\" data-v=\"' + v.id + '\"\u003eAdd to cart — ' + money(v.price) + '\u003c\/button\u003e' + (x.p.variants.length \u003e 1 ? ' \u003csmall\u003e(' + v.title.split(' \/ ')[0] + '; pick another color on its page)\u003c\/small\u003e' : '') + '\u003c\/div\u003e\u003c\/div\u003e';\n      }).join('');\n      const a = anchor(); if (a) a.after(d);\n      d.hidden = false;\n      d.addEventListener('click', ev =\u003e {\n        const l = ev.target.closest('a.hd-t, a.hd-img'); if (!l) return; ev.preventDefault();\n        const card = l.closest('.hd-card'), x = rows[+card.dataset.i], b = card.querySelector('.hd-btn');\n        window.hhQV({ a: x.p, v: x.p.variants.find(v =\u003e v.available), from: l, note: 'Includes: ' + x.o.inc + '.', addLabel: b.textContent, onAdd: () =\u003e b.click() });\n      });\n      d.querySelectorAll('.hd-btn').forEach(b =\u003e b.addEventListener('click', async () =\u003e { b.disabled = true; b.textContent = 'Adding…'; if (await go([{ id: +b.dataset.v, quantity: 1 }])) location.href = '\/cart'; else { b.disabled = false; b.textContent = 'Could not add. Try again'; } }));\n    });\n    if (n \u0026\u0026 KIT_ON.includes(handle)) Promise.all(SET.map(s =\u003e fetch('\/products\/' + s.h + '.js').then(r =\u003e r.ok ? r.json() : null).catch(() =\u003e null))).then(list =\u003e {\n      const rows = SET.map((s, i) =\u003e { const p = list[i]; if (!p) return null; const v = p.variants.find(v =\u003e v.available); return v ? { s, p, v } : null; });\n      if (rows.some(r =\u003e !r)) return;\n      const sum = rows.reduce((a, r) =\u003e a + r.v.price, 0), you = sum - Math.floor(sum * PCT \/ 100);\n      const lk = r =\u003e '\/products\/' + r.s.h + '?variant=' + r.v.id;\n      n.innerHTML = '\u003cp class=\"hn-k\"\u003eThe French Twist Kit\u003c\/p\u003e\u003cp class=\"hn-h\"\u003eEverything for an effortless updo.\u003c\/p\u003e' +\n        '\u003cp class=\"hn-p\"\u003eTwo French pins for the twist, a claw clip for quick half-up days, and simulated pearl flower pins for special occasions.\u003c\/p\u003e' +\n        '\u003cdiv class=\"hn-list\"\u003e' + rows.map((r, i) =\u003e '\u003cdiv class=\"hn-row\" data-i=\"' + i + '\"\u003e' + (r.p.featured_image ? '\u003ca class=\"hn-img\" href=\"' + lk(r) + '\"\u003e\u003cimg src=\"' + img(r.p.featured_image, 100) + '\" alt=\"\"\u003e\u003c\/a\u003e' : '') +\n          '\u003cspan\u003e\u003ca class=\"hn-a\" href=\"' + lk(r) + '\"\u003e' + r.s.name + '\u003c\/a\u003e\u003csmall\u003e' + r.s.note + (r.p.variants.length \u003e 1 ? ' · ' + r.v.title.split(' \/ ')[0] : '') + '\u003c\/small\u003e\u003c\/span\u003e\u003cb\u003e' + money(r.v.price) + '\u003c\/b\u003e\u003c\/div\u003e').join('') + '\u003c\/div\u003e' +\n        '\u003cdiv class=\"hn-sum\"\u003e\u003cspan\u003eBought separately\u003c\/span\u003e\u003cspan\u003e' + money(sum) + '\u003c\/span\u003e\u003c\/div\u003e' +\n        '\u003cdiv class=\"hn-sum hn-you\"\u003e\u003cspan\u003eWith ' + PCT + '% off 3+ items\u003c\/span\u003e\u003cspan\u003e' + money(you) + '\u003c\/span\u003e\u003c\/div\u003e' +\n        '\u003cbutton type=\"button\" class=\"hn-btn\"\u003eAdd all ' + rows.length + ' to cart — ' + money(you) + ' at checkout\u003c\/button\u003e' +\n        '\u003cp class=\"hn-note\"\u003eThe ' + PCT + '% applies automatically at checkout and can’t be combined with other discount codes. Items may arrive in separate parcels, each with its own tracking number.\u003c\/p\u003e';\n      const a = (d.hidden ? null : d) || anchor(); if (a) a.after(n);\n      n.hidden = false;\n      const btn = n.querySelector('.hn-btn');\n      const pickColor = () =\u003e { const i = document.querySelector('variant-picker input:checked, variant-selects input:checked, variant-radios input:checked'); return i ? i.value : null; };\n      const pickDuo = r =\u003e { const c = pickColor(); return (c \u0026\u0026 r.p.variants.find(v =\u003e v.available \u0026\u0026 v.title.split(' \/ ')[0] === c)) || r.v; };\n      const relabel = () =\u003e { rows.forEach((r, i) =\u003e { if (r.s.h !== 'duo-french-twist-set' || r.p.variants.length \u003c 2) return; const el = n.querySelector('.hn-row[data-i=\"' + i + '\"] small'); if (el) el.textContent = r.s.note + ' · ' + pickDuo(r).title.split(' \/ ')[0]; }); };\n      document.addEventListener('change', () =\u003e setTimeout(relabel, 200)); relabel();\n      btn.addEventListener('click', async () =\u003e { btn.disabled = true; btn.textContent = 'Adding…'; if (await go(rows.map(x =\u003e ({ id: (x.s.h === 'duo-french-twist-set' ? pickDuo(x) : x.v).id, quantity: 1 })))) location.href = '\/cart'; else { btn.disabled = false; btn.textContent = 'Could not add. Try again'; } });\n      n.addEventListener('click', ev =\u003e {\n        const l = ev.target.closest('a.hn-a, a.hn-img'); if (!l) return; ev.preventDefault();\n        const r = rows[+l.closest('.hn-row').dataset.i];\n        window.hhQV({ a: r.p, v: r.v, from: l, note: 'Part of the French Twist Kit: ' + r.s.note + '.', addLabel: btn.textContent, onAdd: () =\u003e btn.click() });\n      });\n    });\n  };\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () =\u003e setTimeout(start, 50)); else setTimeout(start, 50);\n})();\n\u003c\/script\u003e\n\u003c!-- \/hv-extra v1 --\u003e\n\u003c!-- hv-tiers v1 (Havendoa): \"Buy more, save more\" 1\/2\/3 items, replaces the quantity box. Must match automatic discounts (2 items 10%, 3+ items 15%). Remove from the hv-tiers v1 start marker to the \/hv-tiers v1 end marker to undo. --\u003e\n\u003cdiv class=\"hv-tiers\" data-handle=\"duo-french-twist-set\" hidden\u003e\u003c\/div\u003e\n\u003cstyle\u003e\n.hv-tiers{margin:4px 0 16px;background:#f5efe8;border:1px solid #e5d6c6;border-radius:14px;padding:12px 12px 6px;font-size:15px;color:#2b2b2b}\n.hv-tiers__h{font-weight:700;font-size:16px;margin:0 0 8px;color:#7a5534}\n.hv-tier{display:flex;align-items:center;gap:10px;background:#fff;border:1px solid #eadfd3;border-radius:10px;padding:10px 12px;margin:0 0 8px;cursor:pointer}\n.hv-tier.is-on{box-shadow:0 0 0 2px #7a5534;border-color:#7a5534}\n.hv-tier input{width:20px;height:20px;accent-color:#7a5534;margin:0;flex:0 0 20px}\n.hv-tier__t{flex:1;min-width:0;line-height:1.3}\n.hv-tier__t small{display:block;color:#6b6b6b;font-size:12.5px}\n.hv-tier__b{display:inline-block;margin-left:6px;background:#7a5534;color:#fff;border-radius:20px;font-size:11.5px;font-weight:700;padding:2px 8px;vertical-align:middle}\n.hv-tier__p{text-align:right;font-weight:700;white-space:nowrap}\n.hv-tier__p s{display:block;font-weight:400;font-size:12.5px;color:#8a8a8a}\n.hv-tiers__note{font-size:12.5px;color:#6b6b6b;margin:0 2px 6px}\n\u003c\/style\u003e\n\u003cscript\u003e\n(() =\u003e {\n  const cs = document.currentScript;\n  const TIERS = [[1, 0, ''], [2, 10, 'Save 10%'], [3, 15, 'Save 15%']];\n  const money = c =\u003e '$' + (c \/ 100).toFixed(2);\n  const start = () =\u003e {\n    const box = (cs \u0026\u0026 cs.parentElement.querySelector('.hv-tiers')) || document.querySelector('.hv-tiers');\n    if (!box || box.dataset.ready) return;\n    box.dataset.ready = 1;\n    const info = document.querySelector('.product__info-container');\n    const form = info \u0026\u0026 info.querySelector('form[action*=\"\/cart\/add\"][data-type=\"add-to-cart-form\"]');\n    if (!info || !form) return;\n    const blockOf = el =\u003e { while (el \u0026\u0026 el.parentElement !== info) el = el.parentElement; return el; };\n    const qtyBox = info.querySelector('.product-form__quantity');\n    const anchor = qtyBox ? blockOf(qtyBox) : blockOf(form);\n    const qty = () =\u003e info.querySelector('input[name=\"quantity\"]') || document.querySelector('input[name=\"quantity\"]');\n    fetch('\/products\/' + box.dataset.handle + '.js').then(r =\u003e r.ok ? r.json() : null).then(p =\u003e {\n      if (!p) return;\n      const cur = () =\u003e { const id = +(form.querySelector('input[name=\"id\"]') || {}).value; return p.variants.find(v =\u003e v.id === id) || p.variants.find(v =\u003e v.available) || p.variants[0]; };\n      const render = () =\u003e {\n        const v = cur(), on = box.querySelector('input:checked'), pick = on ? +on.value : Math.max(1, Math.min(3, parseInt((qty() || {}).value, 10) || 1));\n        box.innerHTML = '\u003cp class=\"hv-tiers__h\"\u003eBuy more, save more\u003c\/p\u003e' + TIERS.map(([n, pct, badge]) =\u003e {\n          const full = v.price * n, you = full - Math.floor(full * pct \/ 100);\n          return '\u003clabel class=\"hv-tier' + (n === pick ? ' is-on' : '') + '\"\u003e\u003cinput type=\"radio\" name=\"hv-tier\" value=\"' + n + '\"' + (n === pick ? ' checked' : '') + '\u003e' +\n            '\u003cspan class=\"hv-tier__t\"\u003e\u003cb\u003e' + n + (n === 1 ? ' item' : ' items') + '\u003c\/b\u003e' + (badge ? '\u003cspan class=\"hv-tier__b\"\u003e' + badge + '\u003c\/span\u003e' : '') + (p.variants.length \u003e 1 ? '\u003csmall\u003e' + v.title.split(' \/ ')[0] + (n \u003e 1 ? ' x ' + n : '') + '\u003c\/small\u003e' : '') + '\u003c\/span\u003e' +\n            '\u003cspan class=\"hv-tier__p\"\u003e' + money(you) + (pct ? '\u003cs\u003e' + money(full) + '\u003c\/s\u003e' : '') + '\u003c\/span\u003e\u003c\/label\u003e';\n        }).join('') + '\u003cp class=\"hv-tiers__note\"\u003eWant different colors? Add each one to your cart: the discount counts every item in your order.\u003c\/p\u003e';\n      };\n      const setQty = n =\u003e { const q = qty(); if (q \u0026\u0026 q.value !== String(n)) { q.value = n; q.dispatchEvent(new Event('change', { bubbles: true })); } };\n      anchor.before(box);\n      if (qtyBox) anchor.style.display = 'none';\n      render(); box.hidden = false;\n      box.addEventListener('change', e =\u003e { if (e.target.name !== 'hv-tier') return; setQty(+e.target.value); render(); });\n      \/\/ the theme resets quantity to 1 on every variant change; put the tier back and refresh prices\n      document.addEventListener('change', e =\u003e { if (box.contains(e.target)) return; if (e.target \u0026\u0026 e.target.name === 'id') setTimeout(() =\u003e { const t = box.querySelector('input:checked'); if (t) setQty(+t.value); render(); }, 60); });\n      window.addEventListener('pageshow', () =\u003e setTimeout(() =\u003e { const t = box.querySelector('input:checked'); if (t) setQty(+t.value); }, 0));\n    });\n  };\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();\n})();\n\u003c\/script\u003e\n\u003c!-- \/hv-tiers v1 --\u003e","brand":"Havendoa","offers":[{"title":"Tortoiseshell","offer_id":49284150001847,"sku":"HV-DUO-TOR","price":65.0,"currency_code":"USD","in_stock":true},{"title":"Black","offer_id":49284260167863,"sku":"HV-DUO-BLK","price":65.0,"currency_code":"USD","in_stock":true},{"title":"Olive Green","offer_id":49284260200631,"sku":"HV-DUO-OLV","price":65.0,"currency_code":"USD","in_stock":true},{"title":"Burgundy","offer_id":49284260233399,"sku":"HV-DUO-BUR","price":65.0,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0761\/4998\/1367\/files\/Se72d2fbac55c4b74b7e0bbebb54efd2cp_46cabe3a-39b1-4d7a-9f99-aaf9ef3238e7.webp?v=1790943910"}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0761\/4998\/1367\/collections\/Sec553b7afeb044b2be4f15f42cbf2177A_6b0aa6a8-f8d6-4f47-b38a-b59277d1b24d.webp?v=1790955784","url":"https:\/\/havendoa.com\/collections\/french-pins.oembed","provider":"Havendoa","version":"1.0","type":"link"}