1<!doctype html> 2<html lang="en"> 3<head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 <title>Shop · Nic's Fix</title> 7 <link rel="stylesheet" href="/static/style.css"> 8 <link rel="icon" href="/settings/favicon?v=55028">
9<script> 10// Apply saved (or system) theme before paint to avoid a flash, and expose the toggle. 11(function () { 12 try { 13 var t = localStorage.getItem('theme'); 14 if (t !== 'dark' && t !== 'light') { 15 t = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; 16 } 17 document.documentElement.setAttribute('data-theme', t); 18 } catch (e) {} 19})(); 20function toggleTheme() { 21 var d = document.documentElement; 22 var next = d.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; 23 try { localStorage.setItem('theme', next); } catch (e) {} 24 d.setAttribute('data-theme', next); 25} 26</script>
26 27</head> 28<body> 29 30 31<div class="sitebanner" role="status" style="background:#fdecee; color:#a3222f;"> 32 <div class="sitebanner-inner"><p><strong>Heads up!</strong> There's an unusually large volume of requests and repairs right now. There will be delays! It's not quite the quick and personal repair experience I aim to provide, so discounts are being applied where possible. Every request <strong>will</strong> be responded to, repaired, and returned as fast as a dad can do it! Thank you~</p></div> 33</div> 34 35<button class="theme-fab" type="button" onclick="toggleTheme()" aria-label="Toggle dark mode" title="Toggle light / dark"> 36 <svg class="i-light" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg> 37 <svg class="i-dark" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg> 38</button> 39<div class="portal"> 40 <div class="portal-head"> 41 <div class="mark"><img src="/settings/logo" alt="Nic's Fix" style="width:100%; height:100%; object-fit:cover; border-radius:12px;"></div> 42 <h1>Nic's Fix</h1> 43 <p>Pods, docks & genuine parts</p> 44 <nav class="portal-nav" style="display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:6px;font-size:14px;"> 45 <a href="/request">Request a repair</a> 46 <a href="/track">Track</a> 47 <a href="/shop">Shop</a> 48 </nav> 49 </div> 50 51 52 53 54 55<style> 56 /* The shared portal shell is 560px - fine for a status page, far too narrow for a 25-item 57 catalogue (one column, and the 860px cart sidebar below could never trigger). Widen it 58 here only; every other page that uses base_public.html is untouched. */ 59 .portal{max-width:1120px;} 60 @media(max-width:900px){.portal{padding-left:16px;padding-right:16px;}} 61 .shop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:14px;align-items:start;} 62 .shop-card{border:1px solid var(--border,#e3e3e8);border-radius:14px;overflow:hidden;background:var(--surface,#fff);display:flex;flex-direction:column;} 63 .shop-card .ph{position:relative;aspect-ratio:1/1;background:#f5f5f7;display:flex;align-items:center;justify-content:center;overflow:hidden;} 64 .shop-card .ph img{width:100%;height:100%;object-fit:contain;cursor:zoom-in;}
65 /* Tap-to-enlarge lightbox */ 66 #shopLightbox{position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,.82);padding:24px;cursor:zoom-out;} 67 #shopLightbox.on{display:flex;} 68 #shopLightbox .lbframe{position:relative;display:inline-block;line-height:0;max-width:96vw;max-height:92vh;} 69 #shopLightbox img{display:block;max-width:96vw;max-height:92vh;object-fit:contain;border-radius:8px;box-shadow:0 10px 50px rgba(0,0,0,.5);cursor:pointer;} 70 /* Close button anchored to the image's own top-right corner. */ 71 #shopLightbox .lbx{position:absolute;top:-14px;right:-14px;width:38px;height:38px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:#111;border:2px solid #fff;color:#fff;font-size:24px;line-height:1;cursor:pointer;z-index:2;box-shadow:0 2px 10px rgba(0,0,0,.5);} 72 #shopLightbox .lbx:hover{background:#333;} 73 #shopLightbox .lbcount{position:absolute;bottom:10px;left:50%;transform:translateX(-50%);color:#fff;font-size:13px;line-height:1;background:rgba(0,0,0,.6);padding:4px 12px;border-radius:999px;} 74 .csl{position:absolute;inset:0;opacity:0;transition:opacity .5s ease;display:flex;align-items:center;justify-content:center;} 75 .csl.on{opacity:1;} 76 .cnav{position:absolute;top:50%;transform:translateY(-50%);border:none;background:rgba(0,0,0,.34);color:#fff;width:26px;height:32px;border-radius:7px;cursor:pointer;font-size:18px;line-height:1;z-index:2;opacity:.85;} 77 .cnav:hover{opacity:1;} 78 .cprev{left:6px;} .cnext{right:6px;} 79 .cdots{position:absolute;bottom:7px;left:0;right:0;display:flex;gap:5px;justify-content:center;z-index:2;} 80 .cdot{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,.55);box-shadow:0 0 0 1px rgba(0,0,0,.25);cursor:pointer;} 81 .cdot.on{background:#fff;} 82 .shop-card .pad{padding:12px 14px;display:flex;flex-direction:column;gap:8px;flex:1;} 83 .shop-card h4{margin:0;font-size:15px;} 84 .shop-card .price{font-weight:700;font-size:16px;} 85 .shop-card .desc{font-size:12px;color:var(--muted,#777);line-height:1.4;} 86 .shop-card .desc.clamped{max-height:4.2em;overflow:hidden;} 87 .shop-card .desc a{color:var(--accent,#2f6fed);} 88 .shop-card .readmore{background:none;border:none;padding:2px 0;margin-top:2px;color:var(--accent,#2f6fed);font-size:12px;font-weight:600;cursor:pointer;} 89 .shop-card .readmore:hover{text-decoration:underline;} 90 .itemnote{margin-top:6px;font-size:12px;line-height:1.4;background:rgba(224,168,0,.14);border:1px solid rgba(224,168,0,.5);border-radius:8px;padding:7px 9px;} 91 .itemnote.clamped{max-height:4.4em;overflow:hidden;} 92 .notemore{background:none;border:none;padding:2px 0;color:var(--accent,#2f6fed);font-size:11.5px;font-weight:600;cursor:pointer;} 93 .itemnote a{color:var(--accent,#2f6fed);} 94 .oos{color:var(--danger,#c0392b);font-weight:600;font-size:13px;} 95 .stocknote{font-size:12px;font-weight:600;color:#b26b00;min-height:0;} 96 .stocknote.out{color:var(--danger,#c0392b);} 97 .cart-line .short{color:var(--danger,#c0392b);font-size:11px;font-weight:600;} 98 .opt-text{width:100%;margin-top:6px;font-size:13px;} 99 .shop-cart{position:sticky;top:14px;} 100 .cart-line{display:flex;justify-content:space-between;gap:8px;font-size:13px;padding:6px 0;border-bottom:1px solid var(--border,#eee);} 101 .cart-line .x{border:none;background:none;cursor:pointer;color:var(--muted,#999);font-size:15px;} 102 .qtybox{display:inline-flex;align-items:center;gap:4px;} 103 .qtybox button{width:22px;height:22px;border:1px solid var(--border,#ddd);background:var(--surface-2,#f3f3f6);border-radius:6px;cursor:pointer;} 104 /* ---- Toolbar: search + category filters + density toggle ---- */ 105 .shop-tools{position:sticky;top:0;z-index:30;background:var(--paper,#f5f5f7);padding:10px 0 8px;margin-bottom:12px;border-bottom:1px solid var(--border,#e3e3e8);} 106 .shop-search{position:relative;margin-bottom:8px;} 107 .shop-search input{width:100%;height:40px;padding:0 34px 0 34px;font-size:15px;border-radius:10px;} 108 .shop-search .si{position:absolute;left:11px;top:50%;transform:translateY(-50%);opacity:.5;font-size:15px;pointer-events:none;} 109 .shop-search .sx{position:absolute;right:6px;top:50%;transform:translateY(-50%);border:none;background:none;cursor:pointer;font-size:19px;color:var(--muted,#888);padding:4px 8px;}
110 .chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;} 111 .chip{border:1px solid var(--border,#e3e3e8);background:var(--surface,#fff);color:inherit;border-radius:999px;padding:5px 11px;font-size:13px;cursor:pointer;white-space:nowrap;} 112 .chip:hover{background:var(--surface-2,#f5f5f7);} 113 .chip.on{background:var(--ink,#1d1d1f);color:#fff;border-color:var(--ink,#1d1d1f);} 114 .chip .n{opacity:.55;font-size:11px;margin-left:3px;} 115 .chip.on .n{opacity:.75;} 116 .toolrow{display:flex;gap:8px;align-items:center;margin-top:8px;flex-wrap:wrap;} 117 .toolrow .count{font-size:12.5px;color:var(--muted,#777);margin-right:auto;} 118 .viewtog{display:inline-flex;border:1px solid var(--border,#e3e3e8);border-radius:8px;overflow:hidden;} 119 .viewtog button{border:none;background:var(--surface,#fff);color:inherit;padding:5px 10px;font-size:12.5px;cursor:pointer;} 120 .viewtog button.on{background:var(--ink,#1d1d1f);color:#fff;} 121 .shopintro.clamped{max-height:5.6em;overflow:hidden;} 122 .shop-empty{padding:26px 14px;text-align:center;color:var(--muted,#777);} 123 .pop-badge{display:inline-block;background:#fdf3e3;color:#8a5a10;border:1px solid #f3e0bd;border-radius:999px;padding:1px 7px;font-size:10.5px;font-weight:700;vertical-align:middle;} 124 .shop-card.is-hidden,.shop-sec.is-hidden{display:none;} 125 /* Compact by default: two lines of description, options tucked behind a toggle. */ 126 .shop-card .desc.clamped{max-height:2.9em;overflow:hidden;} 127 .optwrap[hidden]{display:none;} 128 .optoggle{background:none;border:1px solid var(--border,#e3e3e8);border-radius:8px;padding:4px 9px;font-size:12px;cursor:pointer;color:inherit;} 129 .optoggle:hover{background:var(--surface-2,#f5f5f7);} 130 131 /* ---- List (dense) view ---- */ 132 .shop-grid.list{display:block;} 133 .shop-grid.list .shop-card{flex-direction:row;align-items:stretch;border-radius:10px;margin-bottom:8px;} 134 .shop-grid.list .shop-card .ph{width:74px;min-width:74px;aspect-ratio:auto;} 135 .shop-grid.list .shop-card .pad{padding:9px 12px;gap:5px;} 136 .shop-grid.list .shop-card .desc,.shop-grid.list .cdots,.shop-grid.list .cnav{display:none;} 137 .shop-grid.list .shop-card h4{font-size:14px;} 138 .shop-grid.list .shop-card .price{font-size:14px;} 139 @media(min-width:640px){ 140 .shop-grid.list .shop-card .pad{flex-direction:row;align-items:center;gap:12px;} 141 .shop-grid.list .shop-card h4{flex:1;} 142 .shop-grid.list .shop-card .btn{margin-top:0!important;} 143 .shop-grid.list .optwrap{flex-basis:100%;} 144 } 145 @media(min-width:860px){.shop-wrap{display:grid;grid-template-columns:1fr 300px;gap:18px;align-items:start;}} 146 /* Mobile: a confirmation toast on add, and a sticky cart bar so the cart isn't stranded at the 147 very bottom of the page. Both are hidden on desktop (the sticky sidebar handles it there). */ 148 #cartToast{position:fixed;left:50%;bottom:78px;transform:translateX(-50%) translateY(10px);background:#15703f;color:#fff;padding:9px 16px;border-radius:999px;font-size:14px;font-weight:600;z-index:120;opacity:0;pointer-events:none;transition:opacity .2s ease,transform .2s ease;box-shadow:0 6px 20px rgba(0,0,0,.22);} 149 #cartToast.show{opacity:1;transform:translateX(-50%) translateY(0);} 150 #cartBar{position:fixed;left:0;right:0;bottom:0;z-index:110;display:none;padding:10px 14px;background:var(--surface,#fff);border-top:1px solid var(--border,#e3e3e8);box-shadow:0 -6px 20px rgba(0,0,0,.10);} 151 #cartBar button{width:100%;justify-content:center;} 152 @media(max-width:859px){.shop-wrap{padding-bottom:74px;}} 153 @media(min-width:860px){ #cartBar{display:none!important;} #cartToast{bottom:24px;} } 154</style> 155 156 157 158 159 <div class="panel pad" style="margin-bottom:14px;"> 160 <div class="richtext shopintro clamped" id="shopIntro">Refurbished HomePods, docks, tools, and genuine tested / refurbished parts, and sometimes other things. I can only guarantee that my parts are in verified good, working order. I can't guarantee my parts will fix your problem! �<br>�<br>You'll get a PayPal invoice to complete your order. �<br>�<br>While I aim to ship most orders within a few days...busier seasons may result in some orders taking as much as 7-14 days after payment to be ready to ship, and you may
160not get an update until then. Replies to chat may also be particularly slow during busier seasons.�<br>�<br>You'll get a confirmation email with a link to track your order status, and another email with tracking info when your order is about to ship.�<br>�<br>Orders not paid for after 14 days will be automatically cancelled and you can always submit a new one.�<br>�<br>Thank you for supporting a free-time, family-run business!</div> 161 <button type="button" class="readmore" id="shopIntroMore" hidden onclick=" 162 var i=document.getElementById('shopIntro'); 163 var open=i.classList.toggle('clamped'); 164 this.textContent = open ? 'Read more' : 'Show less';">Read more</button> 165 </div> 166 167 168 169 170 <div class="shop-tools"> 171 <div class="shop-search"> 172 <span class="si">ð</span> 173 <input type="search" id="shopQ" placeholder="Search 26 items â try âgasketâ, âadapterâ, âboardââ¦" 174 aria-label="Search the shop" autocomplete="off" spellcheck="false"> 175 <button type="button" class="sx" id="shopQX" aria-label="Clear search" hidden>Ã</button> 176 </div> 177 <div class="chips" role="group" aria-label="Filter by category"> 178 <button type="button" class="chip on" data-cat="" onclick="shopFilter(this)">All<span class="n">26</span></button> 179 180 <button type="button" class="chip" data-cat="COMING SOON!" onclick="shopFilter(this)">COMING SOON!<span class="n">1</span></button> 181 182 <button type="button" class="chip" data-cat="HomePods" onclick="shopFilter(this)">HomePods<span class="n">2</span></button> 183 184 <button type="button" class="chip" data-cat="USB Adapter Dongle Dock" onclick="shopFilter(this)">USB Adapter Dongle Dock<span class="n">1</span></button> 185 186 <button type="button" class="chip" data-cat="Merch" onclick="shopFilter(this)">Merch<span class="n">1</span></button> 187 188 <button type="button" class="chip" data-cat="Boards and Sensors" onclick="shopFilter(this)">Boards and Sensors<span class="n">7</span></button> 189 190 <button type="button" class="chip" data-cat="Tools" onclick="shopFilter(this)">Tools<span class="n">1</span></button> 191 192 <button type="button" class="chip" data-cat="Sonos" onclick="shopFilter(this)">Sonos<span class="n">1</span></button> 193 194 <button type="button" class="chip" data-cat="Parts and Spares" onclick="shopFilter(this)">Parts and Spares<span class="n">4</span></button> 195 196 <button type="button" class="chip" data-cat="Ribbons, Cables, and Connectors" onclick="shopFilter(this)">Ribbons, Cables, and Connectors<span class="n">5</span></button> 197 198 <button type="button" class="chip" data-cat="Cosmetics" onclick="shopFilter(this)">Cosmetics<span class="n">3</span></button> 199 200 <a class="chip" href="#support">ð Support</a> 201 </div> 202 <div class="toolrow"> 203 <span class="count" id="shopCount"></span> 204 <label class="check" style="margin:0;font-size:12.5px;"><input type="checkbox" id="popFirst" onchange="applyShopFilter()"> Popular first</label> 205 <span class="viewtog"> 206 <button type="button" id="viewGrid" class="on" onclick="setShopView('grid')">⦠Grid</button> 207 <button type="button" id="viewList" onclick="setShopView('list')">â° List</button> 208 </span> 209 </div> 210 </div> 211 212 <div class="shop-wrap"> 213 <div> 214 215 <section class="shop-sec" data-cat="COMING SOON!"> 216 <h2 id="sec-1" style="margin:10px 0; scroll-margin-top:60px;">COMING SOON!</h2> 217 <div class="shop-grid" style="margin-bottom:20px;"> 218 219 <div class="shop-card" data-id="26" data-cat="COMING SOON!" 220 data-search="custom replacement mesh for apple homepods coming soon! new colors and special editions added weekly, check back often! fits the 1st-generation apple homepod (a1639). 2nd-gen versions are coming soon! is your apple homepod's mesh looking bad? maybe you just want to add some flair? no special skill or tools required! pick three things: 1) fit - how it goes on - slip-over: goes on over your original mesh. nothing to remove. - replacement: replaces your original mesh. you remove the old mesh first. 2) style - how it's made - one piece: a single piece that slides on. - two halves (+$10): two halves you join together on the homepod. 3) color choose from the colors i have on hand - some change color with heat, and one glows in the dark. want something else? any color that plastic comes in can be done, from multi-color to color-changing with light or temperature. pick "custom color" (+$20), tell me what you want, and i'll special order the filament for you. custom colors take longer, since the filament has to arrive first. fit slip-over - goes over your original mesh replacement - swap out your original mesh style one piece - slides on two halves - joined together on the homepod color white black grey red orange blue purple to pink (changes with heat) burgundy to pink (changes with heat) rainbow ombré (glows in the dark) custom color - i'll order it for you" data-units="0" 221 data-name="custom replacement mesh for apple homepods"> 222 <div class="ph"> 223 224 <div class="csl on"><img loading="lazy" src="/shop/item-media/70" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/71" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/72" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/73" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/74" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/75" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/76" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/77" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/78" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/79" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/80" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/81" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/82" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/83" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/84" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/85" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/86" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/87" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/88" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/89" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/90" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/91" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/92" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/93" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/94" alt="Custom Replacement Mesh for Apple HomePods"></div><div class="csl"><img loading="lazy" src="/shop/item-media/95" alt="Custom Replacement Mesh for Apple HomePods"></div> 225 226 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 227 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 228 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span></div> 229 230 231 </div> 232 <div class="pad"> 233 <h4>Custom Replacement Mesh for Apple HomePods</h4> 234 235 236 <div class="price" id="price_26">
236from $30</div> 237 <div class="stocknote" id="stock_26"></div> 238 239 <div class="desc" data-clampable>Is your Apple HomePod's mesh looking bad? Maybe you just want to add some flair? No special skill or tools required!�<br>�<br>Pick three things:�<br>�<br>1) FIT - how it goes on�<br>- Slip-over: goes on over your original mesh. Nothing to remove.�<br>- Replacement: replaces your original mesh. You remove the old mesh first.�<br>�<br>2) STYLE - how it's made�<br>- One piece: a single piece that slides on.�<br>- Two halves (+$10): two halves you join together on the HomePod.�<br>�<br>3) COLOR�<br>Choose from the colors I have on hand - some change color with heat, and one glows in the dark. Want something else? Any color that plastic comes in can be done, from multi-color to color-changing with light or temperature. Pick "Custom color" (+$20), tell me what you want, and I'll special order the filament for you. Custom colors take longer, since the filament has to arrive first.</div><button type="button" class="readmore" hidden>Read more</button> 240 <div class="itemnote" data-clampable-note>New colors and special editions added weekly, check back often! Fits the 1st-generation Apple HomePod (A1639). 2nd-gen versions are coming soon!</div><button type="button" class="notemore" hidden>Read more</button> 241 242 243 244 245 <div class="optwrap" id="opts_26" > 246 247 <label class="field" style="margin:0;"> 248 <span class="lab" style="font-size:12px;">Fit *</span> 249 250 <select class="opt-select" data-item="26" data-option="19" data-required="1" onchange="syncCard(26)"> 251 252 253 <option value="36" data-delta="0.0">Slip-over - goes over your original mesh</option><option value="37" data-delta="0.0" disabled>Replacement - swap out your original mesh - sold out</option> 254 </select> 255 256 257 258 </label> 259 260 <label class="field" style="margin:0;"> 261 <span class="lab" style="font-size:12px;">Style *</span> 262 263 <select class="opt-select" data-item="26" data-option="20" data-required="1" onchange="syncCard(26)"> 264 265 266 <option value="38" data-delta="0.0">One piece - slides on</option><option value="39" data-delta="10.0" disabled>Two halves - joined together on the HomePod (+$10) - sold out</option> 267 </select> 268 269 270 271 </label> 272 273 <label class="field" style="margin:0;"> 274 <span class="lab" style="font-size:12px;">Color *</span> 275 276 <select class="opt-select" data-item="26" data-option="21" data-required="1" onchange="syncCard(26)"> 277 278 279 <option value="40" data-delta="0.0">White</option><option value="41" data-delta="0.0">Black</option><option value="42" data-delta="0.0">Grey</option><option value="43" data-delta="0.0">Red</option><option value="44" data-delta="0.0">Orange</option><option value="45" data-delta="0.0">Blue</option><option value="46" data-delta="0.0">Purple to Pink (changes with heat)</option><option value="47" data-delta="0.0">Burgundy to Pink (changes with heat)</option><option value="48" data-delta="0.0">Rainbow Ombré (glows in the dark) - only 2 left</option><option value="49" data-delta="20.0">Custom color - I'll order it for you (+$20)</option> 280 </select> 281 282 <input type="text" class="opt-text" data-item="26" data-choice="49" maxlength="200" placeholder="Which color? A filament brand/name, a link, or just describe it" aria-label="Which color? A filament brand/name, a link, or just describe it" hidden> 283 284 </label> 285 286 </div> 287 288 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(26)">Add to cart</button> 289 290 </div> 291 </div> 292 293 </div> 294 </section> 295 296 <section class="shop-sec" data-cat="HomePods"> 297 <h2 id="sec-2" style="margin:10px 0; scroll-margin-top:60px;">HomePods</h2> 298 <div class="shop-grid" style="margin-bottom:20px;"> 299 300 <div class="shop-card" data-id="1" data-cat="HomePods" 301 data-search="apple homepod (first-generation, a1639) homepods refurbished with
301new dc filters, no-power diode, and a new nitrile subwoofer gasket. one-year warranty and usa shipping included. currently out of stock - you can also send in your own homepod for repair. color(s) white black / space grey" data-units="0" 302 data-name="apple homepod (first-generation, a1639)"> 303 <div class="ph"> 304 305 <div class="csl on"><img loading="lazy" src="/shop/item-media/1" alt="Apple HomePod (First-Generation, A1639)"></div><div class="csl"><img loading="lazy" src="/shop/item-media/20" alt="Apple HomePod (First-Generation, A1639)"></div> 306 307 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 308 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 309 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span></div> 310 311 312 </div> 313 <div class="pad"> 314 <h4>Apple HomePod (First-Generation, A1639)</h4> 315 316 317 <div class="price" id="price_1">from $160</div> 318 <div class="stocknote" id="stock_1"></div> 319 320 <div class="desc" data-clampable>Refurbished with new DC filters, no-power diode, and a new nitrile subwoofer gasket. One-year warranty and USA shipping included. Currently out of stock - you can also send in your own HomePod for repair.</div><button type="button" class="readmore" hidden>Read more</button> 321 322 323 324 325 326 <div class="optwrap" id="opts_1" > 327 328 <label class="field" style="margin:0;"> 329 <span class="lab" style="font-size:12px;">Color(s) *</span> 330 331 332 <label class="check" style="font-size:13px;"><input type="checkbox" class="opt" data-item="1" data-choice="13" data-delta="0.0" onchange="syncCard(1)"> White</label> 333 334 335 <label class="check" style="font-size:13px;"><input type="checkbox" class="opt" data-item="1" data-choice="14" data-delta="0.0" onchange="syncCard(1)"> Black / Space Grey</label> 336 337 338 339 </label> 340 341 </div> 342 343 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(1)">Add to cart</button> 344 345 </div> 346 </div> 347 348 <div class="shop-card" data-id="2" data-cat="HomePods" 349 data-search="custom a1639 apple homepod homepods choose any color from rit's dyemore lineup - optionally dye the top and cable too, and pick how deep you want the color. one-year warranty, usa shipping included." data-units="0" 350 data-name="custom a1639 apple homepod"> 351 <div class="ph"> 352 353 <div class="csl on"><img loading="lazy" src="/shop/item-media/2" alt="Custom A1639 Apple HomePod"></div> 354 355 356 </div> 357 <div class="pad"> 358 <h4>Custom A1639 Apple HomePod</h4> 359 360 361 <div class="price" id="price_2">$220</div> 362 <div class="stocknote" id="stock_2"></div> 363 364 <div class="desc" data-clampable>Choose any color from Rit's DyeMore lineup - optionally dye the top and cable too, and pick how deep you want the color. One-year warranty, USA shipping included.</div><button type="button" class="readmore" hidden>Read more</button> 365 366 367 368 369 370 <div class="optwrap" id="opts_2" > 371 372 </div> 373 374 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(2)">Add to cart</button> 375 376 </div> 377 </div> 378 379 </div> 380 </section> 381 382 <section class="shop-sec" data-cat="USB Adapter Dongle Dock"> 383 <h2 id="sec-3" style="margin:10px 0; scroll-margin-top:60px;">USB Adapter Dongle Dock</h2> 384 <div class="shop-grid" style="margin-bottom:20px;"> 385 386 <div class="shop-card" data-id="3" data-cat="USB Adapter Dongle Dock" 387 data-search="apple homepod usb adapter (for 1st gen only!) usb adapter dongle dock important: symptoms like blinking volume buttons, boot loops, and failing to set up / configure / update, are impossible to tell whether it's caused by software alone, or rather, an underlying hardware issue, without attempting the restore! but they do have a chance of this fixing it. this will definitely not fix homepods that have no power, sound issues (like no sound, death farts, bad or no bass), and other hardware issues, though sometimes errors when attempting a restore can point to what the hardware cause is. it also won't fit 2nd gen homepods! made-to-order dock for the 1st-gen homepod (a1639) not for second gen or mini! comes with philips screws and a usb-a to c adapter. attempt your own software restores and play with hidden usb functionality! get going with the restore guide before spending money: https://github.com/unbendablestraw/homepod-restore/ the uart option simply adds three exposed wires; tx, rx, and gnd, for you to connect your own (usb to) serial adapter for uart connection to the homepod. only select this option if you are familiar with uart and will use it! this option will not increase your chances of a successful software restore. storage c
387ase / protective cap add storage case (+$5) uart breakout add uart breakout" data-units="89" 388 data-name="apple homepod usb adapter (for 1st gen only!)"> 389 <div class="ph"> 390 391 <div class="csl on"><img loading="lazy" src="/shop/item-media/3" alt="Apple HomePod USB Adapter (for 1st Gen Only!)"></div><div class="csl"><img loading="lazy" src="/shop/item-media/25" alt="Apple HomePod USB Adapter (for 1st Gen Only!)"></div><div class="csl"><img loading="lazy" src="/shop/item-media/26" alt="Apple HomePod USB Adapter (for 1st Gen Only!)"></div><div class="csl"><img loading="lazy" src="/shop/item-media/27" alt="Apple HomePod USB Adapter (for 1st Gen Only!)"></div> 392 393 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 394 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 395 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span></div> 396 397 398 </div> 399 <div class="pad"> 400 <h4>Apple HomePod USB Adapter (for 1st Gen Only!) <span class="pop-badge" title="One of our best sellers">POPULAR</span></h4> 401 402 403 <div class="price" id="price_3">from $20</div> 404 <div class="stocknote" id="stock_3"></div> 405 406 <div class="desc" data-clampable>Made-to-order dock for the 1st-gen HomePod (A1639) NOT FOR SECOND GEN OR MINI! Comes with Philips screws and a USB-A to C adapter. Attempt your own software restores and play with hidden USB functionality! �<br>�<br>GET GOING WITH THE RESTORE GUIDE BEFORE SPENDING MONEY: <a href="https://github.com/UnbendableStraw/homepod-restore/" target="_blank" rel="noopener nofollow">https://github.com/UnbendableStraw/homepod-restore/</a>�<br>�<br>The UART option simply adds three exposed wires; TX, RX, and GND, for you to connect your own (USB to) serial adapter for UART connection to the HomePod. Only select this option if you are familiar with UART and will use it! This option will NOT increase your chances of a successful software restore.</div><button type="button" class="readmore" hidden>Read more</button> 407 <div class="itemnote" data-clampable-note>IMPORTANT: Symptoms like blinking volume buttons, boot loops, and failing to set up / configure / update, are impossible to tell whether it's caused by software alone, or rather, an underlying hardware issue, without attempting the restore! But they do have a chance of this fixing it.�<br>�<br>This will definitely not fix HomePods that have No Power, Sound Issues (like no sound, death farts, bad or no bass), and other hardware issues, though sometimes errors when attempting a restore can point to what the hardware cause is. It also won't fit 2nd Gen HomePods!</div><button type="button" class="notemore" hidden>Read more</button> 408 409 410 411 <button type="button" class="optoggle" onclick="toggleOpts(3, this)" 412 aria-expanded="false" aria-controls="opts_3">Options â¾</button> 413 414 415 <div class="optwrap" id="opts_3" hidden> 416 417 <label class="field" style="margin:0;"> 418 <span class="lab" style="font-size:12px;">Storage case / protective cap</span> 419 420 421 <label class="check" style="font-size:13px;"><input type="checkbox" class="opt" data-item="3" data-choice="1" data-delta="5.0" onchange="syncCard(3)"> Add storage case (+$5)</label> 422 423 424 425 </label> 426 427 <label class="field" style="margin:0;"> 428 <span class="lab" style="font-size:12px;">UART breakout</span> 429 430 431 <label class="check" style="font-size:13px;"><input type="checkbox" class="opt" data-item="3" data-choice="17" data-delta="5.0" onchange="syncCard(3)"> Add UART breakout</label> 432 433 434 435 </label> 436 437 </div> 438 439 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(3)">Add to cart</button> 440 441 </div> 442 </div> 443 444 </div> 445 </section> 446 447 <section class="shop-sec" data-cat="Merch"> 448 <h2 id="sec-4" style="margin:10px 0; scroll-margin-top:60px;">Merch</h2> 449 <div class="shop-grid" style="margin-bottom:20px;"> 450 451 <div class="shop-card" data-id="23" data-cat="Merch" 452 data-search="nic's business card merch free with any order or repair! snag a random color limited run of my custom homepod-shaped business cards. the mini happens to fit snug and perfect in the base of your big homepod! if you're lucky, you'll get a soft, foam mesh version of the card. size biggie mini" data-units="0" 453 data-name="nic's business card"> 454 <div class="ph"> 455 456 <div class="csl on"><img loading="lazy" src="/shop/item-media/36" alt="Nic's Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/37" alt="Nic's Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/38" alt="Nic's Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/39" alt="Nic's Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/40" alt="Nic's Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/41" alt="Nic's Business Card"></div> 457 458 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 459 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 460 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span></div> 461 462 463 </div> 464 <div class="pad"> 465 <h4>Nic's Business Card</h4> 466 467 468 <div class="price" id="price_23">from $0</div> 469 <div class="stocknote" id="stock_23"></div> 470 471 <div class="desc" data-clampable>Free with any order or repair! Snag a random color limited run of my custom HomePod-shaped business cards. The Mini happens to fit snug and perfect in the base of your big HomePod! If you're lucky, you'll get a soft, foam mesh version of the card.</div><button type="button" class="readmore" hidden>Read more</button> 472 473 474 475 476 477 <div class="optwrap" id="opts_23" > 478 479 <label class="field" style="margin:0;">
480 <span class="lab" style="font-size:12px;">Size *</span> 481 482 483 <label class="check" style="font-size:13px;"><input type="checkbox" class="opt" data-item="23" data-choice="23" data-delta="0.0" onchange="syncCard(23)"> Biggie</label> 484 485 486 <label class="check" style="font-size:13px;"><input type="checkbox" class="opt" data-item="23" data-choice="24" data-delta="0.0" onchange="syncCard(23)"> Mini</label> 487 488 489 490 </label> 491 492 </div> 493 <div class="muted" style="font-size:11px;">Limit 6 per order.</div> 494 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(23)">Add to cart</button> 495 496 </div> 497 </div> 498 499 </div> 500 </section> 501 502 <section class="shop-sec" data-cat="Boards and Sensors"> 503 <h2 id="sec-5" style="margin:10px 0; scroll-margin-top:60px;">
503Boards and Sensors</h2> 504 <div class="shop-grid" style="margin-bottom:20px;"> 505 506 <div class="shop-card" data-id="22" data-cat="Boards and Sensors" 507 data-search="replacement touch sensor / panel for 2nd generation apple homepod boards and sensors aftermarket replacement touch panel assembly by technerd for apple homepod 2nd gen (2023, a2825). resolves ghost touch: random playback, accidental siri activation, unresponsive volume keys. key features: ⢠technerd aftermarket touch panel assembly ⢠cross-reference oem part numbers: 821-03845-03, 821-03845-a ⢠our touch sensor marking: yk1750a / tsc3825 ⢠uses oca optical adhesive lamination, the same type of bonding technology utilized in original factory panels ⢠fpc gold-plated contacts; flex cable reinforced with bonding glue. double-sided adhesive provides shock absorption between panel and sensor ⢠eliminates ghost touch, phantom playback and volume control failure ⢠integrated flex cable for direct plug-in installation ⢠brand new, 100% inspected unused parts ⢠continuous bulk supply for electronics repair shops technical parameters: ⢠dimensional tolerance: ±0.2 mm ⢠unit weight: 3.2g (with film), 2.1g (film removed) ⢠operating temperature: 32°â95°f (0°â35°c) ⢠humidity: 5%â90%, noncondensing ⢠max tested altitude: 10,000 ft (3000 m) stable conductive performance for long-term equipment repair. compatibility: fits apple homepod 2nd gen (2023, a2825) note: confirm speaker model before purchase. our sensor marked yk1750a / tsc3825 for identification. what's included: 1 à technerd touch panel assemblies with flex cables condition: new installation notes: ⢠required tools: heat gun, t6 torx screwdriver, spudger, guitar pick ⢠professional disassembly skill required ⢠original panel permanently laminated with oca optical adhesive. moderate heating required for safe removal ⢠fast processing, shockproof packaging ⢠returns accepted only for manufacturing defects â ï¸ damage caused by improper disassembly or installation cannot be returned. disclaimer: aftermarket replacement part, not official apple product. oem part numbers for compatibility reference only. variant plug and play installation (pre-installed onto replacement white plastic top, plug and play, as pictured on right) plug and play installation (pre-installed onto replacement black plastic top, plug and play, as pictured on left) diy installation (no plastic top, just the touch sensor film, as pictured in middle) extended t6 torx bit, 4mm shank, 45mm long add extended t6 bit! i do not need an extended t6 bit. i've already checked and confirmed that i have a t6 screwdriver that will reach the screws in the speaker grille!" data-units="8" 508 data-name="replacement touch sensor / panel for 2nd generation apple homepod"> 509 <div class="ph"> 510 511 <div class="csl on"><img loading="lazy" src="/shop/item-media/33" alt="Replacement Touch Sensor / Panel for 2nd Generation Apple HomePod"></div><div class="csl"><img loading="lazy" src="/shop/item-media/34" alt="Replacement Touch Sensor / Panel for 2nd Generation Apple HomePod"></div><div class="csl"><img loading="lazy" src="/shop/item-media/35" alt="Replacement Touch Sensor / Panel for 2nd Generation Apple HomePod"></div><div class="csl"><img loading="lazy" src="/shop/item-media/44" alt="Replacement Touch Sensor / Panel for 2nd Generation Apple HomePod"></div> 512 513 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 514 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 515 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span></div> 516 517 518 </div> 519 <div class="pad"> 520 <h4>Replacement Touch Sensor / Panel for 2nd Generation Apple HomePod</h4> 521 522 523 <div class="price" id="price_22">from $50</div> 524 <div class="stocknote" id="stock_22"></div> 525 526 <div class="desc" data-clampable>Aftermarket replacement touch panel assembly by TechNerd for Apple HomePod 2nd Gen (2023, A2825). Resolves ghost touch: random playback, accidental Siri activation, unresponsive volume keys.�<br>�<br>Key Features:�<br>⢠TechNerd aftermarket touch panel assembly�<br>⢠Cross-reference OEM part numbers: 821-03845-03, 821-03845-A�<br>⢠Our touch sensor marking: YK1750A / TSC3825�<br>⢠Uses OCA optical adhesive lamination, the same type of bonding technology utilized in original factory panels�<br>
526⢠FPC gold-plated contacts; flex cable reinforced with bonding glue. Double-sided adhesive provides shock absorption between panel and sensor�<br>⢠Eliminates ghost touch, phantom playback and volume control failure�<br>⢠Integrated flex cable for direct plug-in installation�<br>⢠Brand new, 100% inspected unused parts�<br>⢠Continuous bulk supply for electronics repair shops�<br>�<br>Technical Parameters:�<br>⢠Dimensional tolerance: ±0.2 mm�<br>⢠Unit weight: 3.2g (with film), 2.1g (film removed)�<br>⢠Operating temperature: 32°â95°F (0°â35°C)�<br>⢠Humidity: 5%â90%, noncondensing�<br>⢠Max tested altitude: 10,000 ft (3000 m)�<br>Stable conductive performance for long-term equipment repair.�<br>�<br>Compatibility:�<br>Fits Apple HomePod 2nd Gen (2023, A2825)�<br>Note: Confirm speaker model before purchase. Our sensor marked YK1750A / TSC3825 for identification.�<br>�<br>What's Included:�<br>1 à TechNerd touch panel assemblies with flex cables�<br>Condition: New�<br>�<br>Installation Notes:�<br>⢠Required tools: heat gun, T6 Torx screwdriver, spudger, guitar pick�<br>⢠Professional disassembly skill required�<br>⢠Original panel permanently laminated with OCA optical adhesive. Moderate heating required for safe removal�<br>⢠Fast processing, shockproof packaging�<br>⢠Returns accepted only for manufacturing defects�<br>â ï¸ Damage caused by improper disassembly or installation cannot be returned.�<br>�<br>Disclaimer: Aftermarket replacement part, not official Apple product. OEM part numbers for compatibility reference only.</div><button type="button" class="readmore" hidden>Read more</button> 527 528 529 530 531 532 <div class="optwrap" id="opts_22" > 533 534 <label class="field" style="margin:0;"> 535 <span class="lab" style="font-size:12px;">Variant *</span> 536 537 <select class="opt-select" data-item="22" data-option="13" data-required="1" onchange="syncCard(22)"> 538 539 540 <option value="20" data-delta="20.0">Plug and Play Installation (pre-installed onto replacement WHITE plastic top, plug and play, as pictured on right) (+$20)</option><option value="21" data-delta="20.0">Plug and Play Installation (pre-installed onto replacement BLACK plastic top, plug and play, as pictured on left) (+$20)</option><option value="22" data-delta="0.0">DiY Installation (no plastic top, just the touch sensor film, as pictured in middle)</option> 541 </select> 542 543 544 545 </label> 546 547 <label class="field" style="margin:0;"> 548 <span class="lab" style="font-size:12px;">Extended T6 Torx Bit, 4mm Shank, 45mm Long *</span> 549 550 <select class="opt-select" data-item="22" data-option="17" data-required="1" onchange="syncCard(22)"> 551 552 553 <option value="28" data-delta="5.0">Add extended T6 bit! (+$5)</option><option value="29" data-delta="0.0">I do not need an extended t6 bit. I've already checked and confirmed that I have a t6 screwdriver that will reach the screws in the speaker grille!</option> 554 </select> 555 556 557 558 </label> 559 560 </div> 561 562 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(22)">Add to cart</button> 563 564 </div> 565 </div> 566 567 <div class="shop-card" data-id="4" data-cat="Boards and Sensors" 568 data-search="refurbished amplifier board boards and sensors repaired and tested. one-year warranty; us shipping covered for the first 6 months." data-units="13" 569 data-name="refurbished amplifier board"> 570 <div class="ph"> 571 572 <div class="csl on"><img loading="lazy" src="/shop/item-media/4" alt="Refurbished Amplifier Board"></div> 573 574 575 </div> 576 <div class="pad"> 577 <h4>Refurbished Amplifier Board</h4> 578 579 580 <div class="price" id="price_4">$60</div> 581 <div class="stocknote" id="stock_4"></div> 582 583 <div class="desc" data-clampable>Repaired and tested. One-year warranty; US shipping covered for the first 6 months.</div><button type="button" class="readmore" hidden>Read more</button> 584 585 586 587 588 589 <div class="optwrap" id="opts_4" > 590 591 </div> 592 593 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(4)">Add to cart</button> 594 595 </div> 596 </div> 597 598 <div class="shop-card" data-id="5" data-cat="Boards and
598Sensors" 599 data-search="logic board boards and sensors currently out of stock. source a broken homepod that won't power on or has bass issues - these usually have a good logic board." data-units="0" 600 data-name="logic board"> 601 <div class="ph"> 602 603 <div class="csl on"><img loading="lazy" src="/shop/item-media/5" alt="Logic Board"></div> 604 605 606 </div> 607 <div class="pad"> 608 <h4>Logic Board</h4> 609 610 <div class="oos">Sold out</div> 611 612 <div class="desc" data-clampable>Currently out of stock. Source a broken HomePod that won't power on or has bass issues - these usually have a good logic board.</div><button type="button" class="readmore" hidden>Read more</button> 613 614 615 </div> 616 </div> 617 618 <div class="shop-card" data-id="6" data-cat="Boards and Sensors" 619 data-search="touch / display board for 1st generation apple homepod boards and sensors oem 820-00965-03. re-usable adhesive. tested and confirmed working. for 1st generation apple homepod" data-units="0" 620 data-name="touch / display board for 1st generation apple homepod"> 621 <div class="ph"> 622 623 <div class="csl on"><img loading="lazy" src="/shop/item-media/6" alt="Touch / Display Board for 1st Generation Apple HomePod"></div> 624 625 626 </div> 627 <div class="pad"> 628 <h4>Touch / Display Board for 1st Generation Apple HomePod</h4> 629 630 631 <div class="price" id="price_6">$30</div> 632 <div class="stocknote" id="stock_6"></div> 633 634 <div class="desc" data-clampable>OEM 820-00965-03. Re-usable adhesive. Tested and confirmed working. For 1st Generation Apple HomePod</div><button type="button" class="readmore" hidden>Read more</button> 635 636 637 638 639 640 <div class="optwrap" id="opts_6" > 641 642 </div> 643 644 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(6)">Add to cart</button> 645 646 </div> 647 </div> 648 649 <div class="shop-card" data-id="7" data-cat="Boards and Sensors" 650 data-search="power supply boards and sensors model adp-60ef s (2949022004). all psus are 110-220v." data-units="0" 651 data-name="power supply"> 652 <div class="ph"> 653 654 <div class="csl on"><img loading="lazy" src="/shop/item-media/7" alt="Power Supply"></div> 655 656 657 </div> 658 <div class="pad"> 659 <h4>Power Supply</h4> 660 661 662 <div class="price" id="price_7">$30</div> 663 <div class="stocknote" id="stock_7"></div> 664 665 <div class="desc" data-clampable>Model ADP-60EF S (2949022004). All PSUs are 110-220V.</div><button type="button" class="readmore" hidden>Read more</button> 666 667 668 669 670 671 <div class="optwrap" id="opts_7" > 672 673 </div> 674 675 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(7)">Add to cart</button> 676 677 </div> 678 </div> 679 680 <div class="shop-card" data-id="8" data-cat="Boards and Sensors" 681 data-search="microphone suplicity board boards and sensors oem 820-00632-03. re-usable adhesive." data-units="0" 682 data-name="microphone suplicity board"> 683 <div class="ph"> 684 685 <div class="csl on"><img loading="lazy" src="/shop/item-media/8" alt="Microphone Suplicity Board"></div> 686 687 688 </div> 689 <div class="pad"> 690 <h4>Microphone Suplicity Board</h4> 691 692 693 <div class="price" id="price_8">$15</div> 694 <div class="stocknote" id="stock_8"></div> 695 696 <div class="desc" data-clampable>OEM 820-00632-03. Re-usable adhesive.</div><button type="button" class="readmore" hidden>Read more</button> 697 698 699 700 701 702 <div class="optwrap" id="opts_8" > 703 704 </div> 705 706 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(8)">Add to cart</button> 707 708 </div> 709 </div> 710 711 <div class="shop-card" data-id="13" data-cat="Boards and
711Sensors" 712 data-search="microphones boards and sensors oem 821-00834-04. comes with mic screens. same part for either side." data-units="0" 713 data-name="microphones"> 714 <div class="ph"> 715 716 <div class="csl on"><img loading="lazy" src="/shop/item-media/13" alt="Microphones"></div> 717 718 719 </div> 720 <div class="pad"> 721 <h4>Microphones</h4> 722 723 724 <div class="price" id="price_13">$15</div> 725 <div class="stocknote" id="stock_13"></div> 726 727 <div class="desc" data-clampable>OEM 821-00834-04. Comes with mic screens. Same part for either side.</div><button type="button" class="readmore" hidden>Read more</button> 728 729 730 731 732 733 <div class="optwrap" id="opts_13" > 734 735 </div> 736 737 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(13)">Add to cart</button> 738 739 </div> 740 </div> 741 742 </div> 743 </section> 744 745 <section class="shop-sec" data-cat="Tools"> 746 <h2 id="sec-6" style="margin:10px 0; scroll-margin-top:60px;">Tools</h2> 747 <div class="shop-grid" style="margin-bottom:20px;"> 748 749 <div class="shop-card" data-id="19" data-cat="Tools" 750 data-search="extended t6 torx bit, 4mm shank, 45mm long tools having a tough time reaching that one screw between the two tall capacitors on your 1st gen homepod amp? can't find anything that'll reach those recessed screws inside the speaker basket of a 2nd gen pod? this bit makes the job trivial! avoid breaking stuff and stripping screws you can't reach with most tools. this is the same bit i use on a daily basis on all my homepod repairs. fits any 4mm hex shank (like your ifixit screwdriver)" data-units="8" 751 data-name="extended t6 torx bit, 4mm shank, 45mm long"> 752 <div class="ph"> 753 754 <div class="csl on"><img loading="lazy" src="/shop/item-media/19" alt="Extended T6 Torx Bit, 4mm Shank, 45mm Long"></div> 755 756 757 </div> 758 <div class="pad"> 759 <h4>Extended T6 Torx Bit, 4mm Shank, 45mm Long</h4> 760 761 762 <div class="price" id="price_19">$5</div> 763 <div class="stocknote" id="stock_19"></div> 764 765 <div class="desc" data-clampable>Having a tough time reaching that one screw between the two tall capacitors on your 1st gen HomePod amp? Can't find anything that'll reach those recessed screws inside the speaker basket of a 2nd gen pod? This bit makes the job trivial! Avoid breaking stuff and stripping screws you can't reach with most tools. This is the same bit I use on a daily basis on all my HomePod repairs. Fits any 4mm hex shank (like your iFixit screwdriver)</div><button type="button" class="readmore" hidden>Read more</button> 766 767 768 769 770 771 <div class="optwrap" id="opts_19" > 772 773 </div> 774 775 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(19)">Add to cart</button> 776 777 </div> 778 </div> 779 780 </div> 781 </section> 782 783 <section class="shop-sec" data-cat="Sonos"> 784 <h2 id="sec-7" style="margin:10px 0; scroll-margin-top:60px;">Sonos</h2> 785 <div class="shop-grid" style="margin-bottom:20px;"> 786 787 <div class="shop-card" data-id="21" data-cat="Sonos" 788 data-search="sonos s16 amp sonos used, tested, working sonos s16 amplifier. has not been repaired or disassembled. in beautiful shape with minor signs of wear. newest version of this amp ive seen! you will need your own power cord and speaker plugs. 2 year warranty." data-units="0" 789 data-name="sonos s16 amp"> 790 <div class="ph"> 791 792 <div class="csl on"><img loading="lazy" src="/shop/item-media/28" alt="Sonos S16 Amp"></div><div class="csl"><img loading="lazy" src="/shop/item-media/29" alt="Sonos S16 Amp"></div><div class="csl"><img loading="lazy" src="/shop/item-media/30" alt="Sonos S16 Amp"></div><div class="csl"><img loading="lazy" src="/shop/item-media/31" alt="Sonos S16 Amp"></div><div class="csl"><img loading="lazy" src="/shop/item-media/32" alt="Sonos S16 Amp"></div> 793 794 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 795 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 796 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span></div> 797 798 799 </div> 800 <div class="pad"> 801 <h4>Sonos S16 Amp</h4> 802 803 804 <div class="price" id="price_21">$500</div> 805 <div class="stocknote" id="stock_21"></div> 806 807 <div class="desc" data-clampable>Used, tested, working Sonos s16 amplifier. Has not been repaired or disassembled. In beautiful shape with minor signs of wear. NEWEST VERSION OF THIS AMP IVE SEEN! You will need your own power cord and speaker plugs. 2 year warranty.</div><button type="button" class="readmore" hidden>Read more</button> 808 809 810 811 812 813 <div class="optwrap" id="opts_21" > 814 815 </div> 816 817 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(21)">Add to cart</button> 818 819 </div> 820 </div> 821 822 </div> 823 </section> 824 825 <section class="shop-sec" data-cat="Parts and Spares"> 826 <h2 id="sec-8" style="margin:10px 0; scroll-margin-top:60px;">Parts and Spares</h2> 827 <div class="shop-grid" style="margin-bottom:20px;"> 828 829 <div class="shop-card" data-id="20" data-cat="Parts and Spares" 830 data-search="custom subwoofer gasket for 1st gen apple homepod parts and spares replace the old foam subwoofer gasket inside your 1st generation apple homepod with something far better! designed to get rid of the rubber grommets around the subwoofer screws, while proving a superior and longer-lasting seal than the factory foam. this also protects the woofer against being bent as a result of drop damage." data-units="21" 831 data-name="custom subwoofer gasket for 1st gen apple homepod"> 832 <div class="ph"> 833 834 <div class="csl on"><img loading="lazy" src="/shop/item-media/21" alt="Custom Subwoofer Gasket for 1st Gen Apple HomePod"></div><div class="csl"><img loading="lazy" src="/shop/item-media/22" alt="Custom Subwoofer Gasket for 1st Gen Apple HomePod"></div><div class="csl"><img loading="lazy" src="/shop/item-media/23" alt="Custom Subwoofer Gasket for 1st Gen Apple HomePod"></div><div class="csl"><img loading="lazy" src="/shop/item-media/24" alt="Custom Subwoofer Gasket for 1st Gen Apple HomePod"></div> 835 836 <button type="button" class="cnav cprev" aria-label="Previous image">â¹</button> 837 <button type="button" class="cnav cnext" aria-label="Next image">âº</button> 838 <div class="cdots"><span class="cdot on"></span><span class="cdot"></span><span class="cdot"></span><span class="cdot"></span></div> 839 840 841 </div> 842 <div class="pad"> 843 <h4>Custom Subwoofer Gasket for 1st Gen Apple HomePod <span class="pop-badge" title="One of our best sellers">POPULAR</span></h4> 844 845 846 <div class="price" id="price_20">$5</div> 847 <div class="stocknote" id="stock_20"></div> 848 849 <div class="desc" data-clampable>Replace the old foam subwoofer gasket inside your 1st Generation Apple HomePod with something far better! Designed to get rid of the rubber grommets around the subwoofer screws, while proving a superior and longer-lasting seal than the factory foam. This also protects the woofer against being bent as a result of drop damage.</div><button type="button" class="readmore" hidden>Read more</button> 850 851 852 853 854 855 <div class="optwrap" id="opts_20" > 856 857 </div> 858 859 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(20)">Add to cart</button> 860 861 </div> 862 </div> 863 864 <div class="shop-card" data-id="24" data-cat="Parts and Spares" 865 data-search="parts board parts and spares non-working donor amp board for 1st generation apple homepod. donât count on fixing it, but it can be a source for hard to find board components or practice for your real repair. let me know if there is a specific component you need and iâll make sure itâs present." data-units="0" 866 data-name="parts board"> 867 <div class="ph"> 868 869 <div class="csl on"><img loading="lazy" src="/shop/item-media/42" alt="Parts Board"></div> 870 871 872 </div> 873 <div class="pad"> 874 <h4>Parts Board</h4> 875 876 877 <div class="price" id="price_24">$10</div> 878 <div class="stocknote" id="stock_24"></div> 879 880 <div class="desc" data-clampable>NON-WORKING DONOR AMP BOARD for 1st generation Apple HomePod. Donât count on fixing it, but it can be a source for hard to find board components or practice for your real repair. Let me know if there is a specific component you need and Iâll make sure itâs present.</div><button type="button" class="readmore" hidden>Read more</button> 881 882 883 884 885 886 <div class="optwrap" id="opts_24" > 887 888 </div> 889 890 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(24)">Add to cart</button> 891 892 </div> 893 </div> 894 895 <div class="shop-card" data-id="14" data-cat="Parts and Spares" 896 data-search="top basket w/ lower rings parts and spares tested and in good working order." data-units="0" 897 data-name="top basket w/ lower rings"> 898 <div class="ph"> 899 900 <div class="csl on"><img loading="lazy" src="/shop/item-media/14" alt="Top Basket w/ Lower Rings"></div> 901 902 903 </div> 904 <div class="pad"> 905 <h4>Top Basket w/ Lower Rings</h4> 906 907 908 <div class="price" id="price_14">$20</div> 909 <div class="stocknote" id="stock_14"></div> 910 911 <div class="desc" data-clampable>Tested and in good working order.</div><button type="button" class="readmore" hidden>Read more</button> 912 913 914 915 916 917 <div class="optwrap" id="opts_14" > 918 919 </div> 920 921 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(14)">Add to cart</button> 922 923 </div> 924 </div> 925 926 <div class="shop-card" data-id="15" data-cat="Parts and Spares" 927 data-search="top basket only parts and spares tested and in good working order." data-units="0" 928 data-name="top basket only"> 929 <div class="ph"> 930 931 <div class="csl on"><img loading="lazy" src="/shop/item-media/15" alt="Top Basket Only"></div> 932 933 934 </div> 935 <div class="pad"> 936 <h4>Top Basket Only</h4> 937 938 939 <div class="price" id="price_15">$15</div> 940 <div class="stocknote" id="stock_15"></div> 941 942 <div class="desc" data-clampable>Tested and in good working order.</div><button type="button" class="readmore" hidden>Read more</button> 943 944 945 946 947 948 <div class="optwrap" id="opts_15" > 949 950 </div> 951 952 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(15)">Add to cart</button> 953 954 </div> 955 </div> 956 957 </div> 958 </section> 959 960 <section class="shop-sec" data-cat="Ribbons, Cables, and Connectors"> 961 <h2 id="sec-9" style="margin:10px 0; scroll-margin-top:60px;">Ribbons, Cables, and Connectors</h2> 962 <div class="shop-grid" style="margin-bottom:20px;"> 963 964 <div class="shop-card" data-id="12" data-cat="Ribbons, Cables, and Connectors" 965 data-search="main logic cable ribbons, cables, and connectors oem 821-00548-07. limit one per person." data-units="0" 966 data-name="main logic cable"> 967 <div class="ph"> 968 969 <div class="csl on"><img loading="lazy" src="/shop/item-media/12" alt="Main Logic Cable"></div> 970 971 972 </div> 973 <div class="pad"> 974 <h4>Main Logic Cable</h4> 975 976 977 <div class="price" id="price_12">$20</div> 978 <div class="stocknote" id="stock_12"></div> 979 980 <div class="desc" data-clampable>OEM 821-00548-07. Limit one per person.</div><button type="button" class="readmore" hidden>Read more</button> 981 982 983 984 985 986 <div class="optwrap" id="opts_12" > 987 988 </div> 989 <div class="muted" style="font-size:11px;">Limit 1 per order.</div> 990 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(12)">Add to cart</button> 991 992 </div> 993 </div> 994 995 <div class="shop-card" data-id="9" data-cat="Ribbons, Cables, and Connectors" 996 data-search="logic to display cable ribbons, cables, and connectors oem 821-01259-04." data-units="0" 997 data-name="logic to display cable"> 998 <div class="ph"> 999 1000 <div class="csl on"><img loading="lazy" src="/shop/item-media/9" alt="Logic to Display Cable"></div> 1001 1002 1003 </div> 1004 <div class="pad"> 1005 <h4>Logic to Display Cable</h4> 1006 1007 1008 <div class="price" id="price_9">$10</div> 1009 <div class="stocknote" id="stock_9"></div> 1010 1011 <div class="desc" data-clampable>OEM 821-01259-04.</div><button type="button" class="readmore" hidden>Read more</button> 1012 1013 1014 1015 1016 1017 <div class="optwrap" id="opts_9" > 1018 1019 </div> 1020 1021 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(9)">Add to cart</button> 1022 1023 </div> 1024 </div> 1025 1026 <div class="shop-card" data-id="10" data-cat="Ribbons, Cables, and Connectors" 1027 data-search="subwoofer connector ribbons, cables, and connectors oem 43025." data-units="0" 1028 data-name="subwoofer connector"> 1029 <div class="ph"> 1030 1031 <div class="csl on"><img loading="lazy" src="/shop/item-media/10" alt="Subwoofer Connector"></div> 1032 1033 1034 </div> 1035 <div class="pad"> 1036 <h4>Subwoofer Connector</h4> 1037 1038 1039 <div class="price" id="price_10">$10</div> 1040 <div class="stocknote" id="stock_10"></div> 1041 1042 <div class="desc" data-clampable>OEM 43025.</div><button type="button" class="readmore" hidden>Read more</button> 1043 1044 1045 1046 1047 1048 <div class="optwrap" id="opts_10" > 1049 1050 </div> 1051 1052 <button type="button" class="btn sm" style="margin-top
1052:auto;" onclick="addToCart(10)">Add to cart</button> 1053 1054 </div> 1055 </div> 1056 1057 <div class="shop-card" data-id="11" data-cat="Ribbons, Cables, and Connectors" 1058 data-search="internal ac connector ribbons, cables, and connectors oem 43645. power a homepod while disassembled - plug one end into the psu and the other into your original power cord." data-units="0" 1059 data-name="internal ac connector"> 1060 <div class="ph"> 1061 1062 <div class="csl on"><img loading="lazy" src="/shop/item-media/11" alt="Internal AC Connector"></div> 1063 1064 1065 </div> 1066 <div class="pad"> 1067 <h4>Internal AC Connector</h4> 1068 1069 1070 <div class="price" id="price_11">$20</div> 1071 <div class="stocknote" id="stock_11"></div> 1072 1073 <div class="desc" data-clampable>OEM 43645. Power a HomePod while disassembled - plug one end into the PSU and the other into your original power cord.</div><button type="button" class="readmore" hidden>Read more</button> 1074 1075 1076 1077 1078 1079 <div class="optwrap" id="opts_11" > 1080 1081 </div> 1082 1083 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(11)">Add to cart</button> 1084 1085 </div> 1086 </div> 1087 1088 <div class="shop-card" data-id="25" data-cat="Ribbons, Cables, and Connectors" 1089 data-search="amp to main board ribbon connector ribbons, cables, and connectors for first-gen apple homepod. this connector is very fragile and can easily be broken off the board if not careful. the plastic will melt if you do not properly replace itâ¦i suggest heating the board from underneath to reflow the connector back on." data-units="0" 1090 data-name="amp to main board ribbon connector"> 1091 <div class="ph"> 1092 1093 <div class="csl on"><img loading="lazy" src="/shop/item-media/43" alt="Amp to Main board ribbon connector"></div> 1094 1095 1096 </div> 1097 <div class="pad"> 1098 <h4>Amp to Main board ribbon connector</h4> 1099 1100 1101 <div class="price" id="price_25">$4</div> 1102 <div class="stocknote" id="stock_25"></div> 1103 1104 <div class="desc" data-clampable>For first-gen Apple HomePod. This connector is very fragile and can easily be broken off the board if not careful. The plastic will melt if you do not properly replace itâ¦i suggest heating the board from underneath to reflow the connector back on.</div><button type="button" class="readmore" hidden>Read more</button> 1105 1106 1107 1108 1109 1110 <div class="optwrap" id="opts_25" > 1111 1112 </div> 1113 1114 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(25)">Add to cart</button> 1115 1116 </div> 1117 </div> 1118 1119 </div> 1120 </section> 1121 1122 <section class="shop-sec" data-cat="Cosmetics"> 1123 <h2 id="sec-10" style="margin:10px 0; scroll-margin-top:60px;">Cosmetics</h2> 1124 <div class="shop-grid" style="margin-bottom:20px;"> 1125 1126 <div class="shop-card" data-id="16" data-cat="Cosmetics" 1127 data-search="plastic top cosmetics choose a cosmetic grade. us shipping +$5. grade a - little to no signs of use (+$30) b - some scratches, no cracks (+$25) c - scratches & small cracks, one piece (+$20) d - scratches & larger cracks, one piece (+$10) color white black / space grey" data-units="0" 1128 data-name="plastic top"> 1129 <div class="ph"> 1130 1131 <div class="csl on"><img loading="lazy" src="/shop/item-media/16" alt="Plastic Top"></div> 1132 1133 1134 </div> 1135 <div class="pad"> 1136 <h4>Plastic Top</h4> 1137 1138 1139 <div class="price" id="price_16">from $0</div> 1140 <div class="stocknote" id="stock_16"></div> 1141 1142 <div class="desc" data-clampable>Choose a cosmetic grade. US shipping +$5.</div><button type="button" class="readmore" hidden>Read more</button> 1143 1144 1145 1146 1147 1148 <div class="optwrap" id="opts_16" > 1149 1150 <label class="field" style="margin:0;">
1151 <span class="lab" style="font-size:12px;">Grade *</span> 1152 1153 <select class="opt-select" data-item="16" data-option="3" data-required="1" onchange="syncCard(16)"> 1154 1155 1156 <option value="3" data-delta="30.0">A - little to no signs of use (+$30)</option><option value="4" data-delta="25.0">B - some scratches, no cracks (+$25)</option><option value="5" data-delta="20.0">C - scratches & small cracks, one piece (+$20)</option><option value="6" data-delta="10.0">D - scratches & larger cracks, one piece (+$10)</option> 1157 </select> 1158 1159 1160 1161 </label> 1162 1163 <label class="field" style="margin:0;"> 1164 <span class="lab" style="font-size:12px;">Color *</span> 1165 1166 <select class="opt-select" data-item="16" data-option="10" data-required="1" onchange="syncCard(16)"> 1167 1168 1169 <option value="15" data-delta="0.0">White</option><option value="16" data-delta="0.0">Black / Space Grey</option> 1170 </select> 1171 1172 1173 1174 </label> 1175 1176 </div> 1177 1178 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(16)">Add to cart</button> 1179 1180 </div> 1181 </div> 1182 1183 <div class="shop-card" data-id="17" data-cat="Cosmetics" 1184 data-search="fabric mesh cosmetics choose a cosmetic grade. us shipping +$10. grade a - little to no signs of use, no stains (+$40) b - some signs of use / small stains (+$30)" data-units="0" 1185 data-name="fabric mesh"> 1186 <div class="ph"> 1187 1188 <div class="csl on"><img loading="lazy" src="/shop/item-media/17" alt="Fabric Mesh"></div> 1189 1190 1191 </div> 1192 <div class="pad"> 1193 <h4>Fabric Mesh</h4> 1194 1195 1196 <div class="price" id="price_17">from $0</div> 1197 <div class="stocknote" id="stock_17"></div> 1198 1199 <div class="desc" data-clampable>Choose a cosmetic grade. US shipping +$10.</div><button type="button" class="readmore" hidden>Read more</button> 1200 1201 1202 1203 1204 1205 <div class="optwrap" id="opts_17" > 1206 1207 <label class="field" style="margin:0;"> 1208 <span class="lab" style="font-size:12px;">Grade *</span> 1209 1210 <select class="opt-select" data-item="17" data-option="4" data-required="1" onchange="syncCard(17)"> 1211 1212 1213 <option value="7" data-delta="40.0">A - little to no signs of use, no stains (+$40)</option><option value="8" data-delta="30.0">B - some signs of use / small stains (+$30)</option> 1214 </select> 1215 1216 1217 1218 </label> 1219 1220 </div> 1221 1222 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(17)">Add to cart</button> 1223 1224 </div> 1225 </div> 1226 1227 <div class="shop-card" data-id="18" data-cat="Cosmetics" 1228 data-search="rubber base cosmetics us shipping +$7. grade grade a (white) grade a (black)" data-units="0" 1229 data-name="rubber base"> 1230 <div class="ph"> 1231 1232 <div class="csl on"><img loading="lazy" src="/shop/item-media/18" alt="Rubber Base"></div> 1233 1234 1235 </div> 1236 <div class="pad"> 1237 <h4>Rubber Base</h4> 1238 1239 1240 <div class="price" id="price_18">from $0</div> 1241 <div class="stocknote" id="stock_18"></div> 1242 1243 <div class="desc" data-clampable>US shipping +$7.</div><button type="button" class="readmore" hidden>Read more</button> 1244 1245 1246 1247 1248 1249 <div class="optwrap" id="opts_18" > 1250 1251 <label class="field" style="margin:0;">
1252 <span class="lab" style="font-size:12px;">Grade *</span> 1253 1254 <select class="opt-select" data-item="18" data-option="5" data-required="1" onchange="syncCard(18)"> 1255 1256 1257 <option value="11" data-delta="5.0">Grade A (White) (+$5)</option><option value="12" data-delta="5.0">Grade A (Black) (+$5)</option> 1258 </select> 1259 1260 1261 1262 </label> 1263 1264 </div> 1265 1266 <button type="button" class="btn sm" style="margin-top:auto;" onclick="addToCart(18)">Add to cart</button> 1267 1268 </div> 1269 </div> 1270 1271 </div> 1272 </section> 1273 1274 <div class="shop-empty panel pad" id="shopEmpty" hidden> 1275 <p style="margin:0 0 8px;">No items match that search.</p> 1276 <button type="button" class="btn ghost sm" onclick="clearShopFilter()">Show everything</button> 1277 </div> 1278 1279 1280 1281<div class="panel pad" id="support" style="margin-top:12px;"> 1282 1283 <h2 style="margin:0 0 4px;">Support us ð</h2> 1284 <p class="muted" style="margin-top:0;">Love what I do? Chuck a buck or two each month for special perks!</p> 1285 1286 1287 1288 <div class="lab" style="margin:6px 0 4px; font-size:12px;">Send a tip / donation</div> 1289 <div style="display:flex; gap:8px; flex-wrap:wrap;"> 1290 <a class="btn ghost" href="https://venmo.com/u/NicsFix" target="_blank" rel="noopener">Venmo @NicsFix</a> 1291 <a class="btn ghost" href="https://cash.app/$NicsFix" target="_blank" rel="noopener">Cash App $NicsFix</a> 1292 1293 1294 <span class="btn ghost" style="cursor:default;">PayPal · <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="452b2c26052b2c2636232c3d6b262a28">[email protected]</a></span> 1295 </div> 1296 1297 <div class="help" style="margin-top:4px;">For PayPal, send to <strong><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="86e8efe5c6e8efe5f5e0effea8e5e9eb">[email protected]</a></strong> (Goods & Services for buyer protection).</div> 1298 1299 1300 1301 <div class="lab" style="margin:12px 0 4px; font-size:12px;">Become a member</div> 1302 <a class="btn" href="https://patreon.com/nicsfix" target="_blank" rel="noopener" style="background:#ff424d; border-color:#ff424d; color:#fff;">Join us on Patreon</a> 1303 <div class="muted" style="font-size:13px; margin-top:6px;">Join <strong>37</strong> other Patreon members today!</div> 1304 1305 <div style="display:flex; gap:10px; flex-wrap:wrap; margin-top:10px;"> 1306 1307 <div style="flex:1; min-width:150px; border:1px solid var(--border,#e3e3e8); border-radius:10px; padding:10px 12px;"> 1308 <div style="font-weight:700;">Free</div> 1309 <div style="font-size:15px; font-weight:600;">$0<span class="muted" style="font-weight:400; font-size:12px;"> / mo</span></div> 1310 1311 </div> 1312 1313 <div style="flex:1; min-width:150px; border:1px solid var(--border,#e3e3e8); border-radius:10px; padding:10px 12px;"> 1314 <div style="font-weight:700;">Pull-Up Resistor</div> 1315 <div style="font-size:15px; font-weight:600;">$1<span class="muted" style="font-weight:400; font-size:12px;"> / mo</span></div> 1316 1317 1318 <div class="muted pt-desc" style="font-size:12px; margin-top:4px; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;"><p>Pull-Up resistors are a cheap and simple, but effective and critical component in making a known good HIGH signal.</p><p></p><p>This tier, as small as it is, will help keep the dream alive and going strong. </p><p></p><p>You'll gain access to livestreams that ONLY paid members get where I show behind the fixes. Unboxing mail-ins, responding to the general inbox, making orders, let's have fun and hang out while we work!</p><p></p><p>I'll shout you out with your name on the screen at the end of every YouTube live stream or Upload!</p><p></p><p>You also gain access to my private patreon posts where I'll be sharing any cool finds while thrifting around the Pacific Northwest.</p></div> 1319 <button type="button" style="border:none; background:none; padding:0; margin-top:3px; color:var(--accent,#2f6fed); font-size:12px; cursor:pointer;" 1320 onclick="var d=this.previousElementSibling; var clamped=d.style.webkitLineClamp==='3'; d.style.webkitLineClamp=clamped?'unset':'3'; d.style.overflow=clamped?'visible':'hidden'; this.textContent=clamped?'Show less':'Show more';">Show more</button> 1321 1322 1323 </div> 1324 1325 <div style="flex:1; min-width:150px; border:1px solid var(--border,#e3e3e8); border-radius:10px; padding:10px 12px;"> 1326 <div style="font-weight:700;">
1326MLCC Ceramic Capacitor</div> 1327 <div style="font-size:15px; font-weight:600;">$5<span class="muted" style="font-weight:400; font-size:12px;"> / mo</span></div> 1328 1329 1330 <div class="muted pt-desc" style="font-size:12px; margin-top:4px; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;"><p>Commonly used for DC filtering, like in Apple HomePods. Ceramic capacitors are made of a crystalline structure that weakens over time. You can technically bake them back to life!</p><p></p><p>Your name will be placed somewhere on my workbench and shout out at the end of all my streams / uploads for showing the love for a good fix!</p></div> 1331 <button type="button" style="border:none; background:none; padding:0; margin-top:3px; color:var(--accent,#2f6fed); font-size:12px; cursor:pointer;" 1332 onclick="var d=this.previousElementSibling; var clamped=d.style.webkitLineClamp==='3'; d.style.webkitLineClamp=clamped?'unset':'3'; d.style.overflow=clamped?'visible':'hidden'; this.textContent=clamped?'Show less':'Show more';">Show more</button> 1333 1334 1335 </div> 1336 1337 <div style="flex:1; min-width:150px; border:1px solid var(--border,#e3e3e8); border-radius:10px; padding:10px 12px;"> 1338 <div style="font-weight:700;">KGB - Known Good Board</div> 1339 <div style="font-size:15px; font-weight:600;">$10<span class="muted" style="font-weight:400; font-size:12px;"> / mo</span></div> 1340 1341 1342 <div class="muted pt-desc" style="font-size:12px; margin-top:4px; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;"><p>Having a known good board or reference is invaluable to diagnosing an unfamiliar issue. It's like playing spot the difference!</p><p></p><p>In addition to your name on my desk and a shout-out at the end of every stream / upload, you'll get early access to uploads before they're public!</p></div> 1343 <button type="button" style="border:none; background:none; padding:0; margin-top:3px; color:var(--accent,#2f6fed); font-size:12px; cursor:pointer;" 1344 onclick="var d=this.previousElementSibling; var clamped=d.style.webkitLineClamp==='3'; d.style.webkitLineClamp=clamped?'unset':'3'; d.style.overflow=clamped?'visible':'hidden'; this.textContent=clamped?'Show less':'Show more';">Show more</button> 1345 1346 1347 </div> 1348 1349 <div style="flex:1; min-width:150px; border:1px solid var(--border,#e3e3e8); border-radius:10px; padding:10px 12px;"> 1350 <div style="font-weight:700;">Flux</div> 1351 <div style="font-size:15px; font-weight:600;">$20<span class="muted" style="font-weight:400; font-size:12px;"> / mo</span></div> 1352 1353 1354 <div class="muted pt-desc" style="font-size:12px; margin-top:4px; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;"><p>Flux is one of the most important parts of soldering. It helps heat transfer more uniformly, and reacts with heat to remove the oxidation that immeditaley builds up on your joints, to create that beautiful blob!</p><p></p><p>In addition to your name on my desk and a shout-out at the end of streams / uploads, and early access to uploads, I'll send you a personal notification whenever I am about to go live!</p></div> 1355 <button type="button" style="border:none; background:none; padding:0; margin-top:3px; color:var(--accent,#2f6fed); font-size:12px; cursor:pointer;" 1356 onclick="var d=this.previousElementSibling; var clamped=d.style.webkitLineClamp==='3'; d.style.webkitLineClamp=clamped?'unset':'3'; d.style.overflow=clamped?'visible':'hidden'; this.textContent=clamped?'Show less':'Show more';">Show more</button> 1357 1358 1359 </div> 1360 1361 <div style="flex:1; min-width:150px; border:1px solid var(--border,#e3e3e8); border-radius:10px; padding:10px 12px;"> 1362 <div style="font-weight:700;">Your Fix</div> 1363 <div style="font-size:15px; font-weight:600;">$100<span class="muted" style="font-weight:400; font-size:12px;"> / mo</span></div> 1364 1365 1366 <div class="muted pt-desc" style="font-size:12px; margin-top:4px; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;"><p>Show exceptional support for the cause and help take this to the next level. We can schedule up to 60 minutes every month to have a 1 on 1 call, just hanging out or trying to repair something you've got yourself!</p></div> 1367 <button type="button" style="border:none; background:none; padding:0; margin-top:3px; color:var(--accent,#2f6fed); font-size:12px; cursor:pointer;" 1368 onclick="var d=this.previousElementSibling; var clamped=d.style.webkitLineClamp==='3'; d.style.webkitLineClamp=clamped?'unset':'3'; d.style.overflow=clamped?'visible':'hidden'; this.textContent=clamped?'Show less':'Show more';">Show more</button> 1369 1370 1371 </div> 1372 1373 </div> 1374 1375 1376</div> 1377 1378 </div> 1379 1380 <div class="shop-cart" id="cartCol"> 1381 <div class="panel pad"> 1382 <h3 style="margin:0 0 8px;">Your cart</h3> 1383 <div id="cartlines"><p class="muted" style="margin:0;">Your cart is empty.</p></div> 1384 <div id="carttotals" style="margin-top:8px;display:none;"> 1385 <div style="display:flex;justify-content:space-between;font-size:13px;"><span>Subtotal</span><span id="c_sub">$0</span></div> 1386 <div style="display:flex;justify-content:space-between;font-size:13px;"><span>Shipping</span><span id="c_ship">$0</span></div> 1387 <div style="display:flex;justify-content:space-between;font-weight:700;margin-top:4px;"><span>Total</span><span id="c_total">$0</span></div> 1388 <div class="muted" style="font-size:11px;margin-top:4px;">Only the highest-shipping item is charged â everything else ships free in the same box.</div> 1389 </div> 1390 <button type="button" class="btn" style="width:100%;margin-top:10px;display:none;" id="checkoutBtn" onclick="openCheckout()">Checkout</button> 1391 </div> 1392 1393 <div class="panel pad" id="checkoutPanel" style="display:none;margin-top:12px;"> 1394 <h3 style="margin:0 0 8px;">Your details</h3> 1395 <form method="post" action="/shop/checkout" id="checkoutForm" onsubmit="return prepCheckout()"> 1396 <div class="row"> 1397 <label class="field"><span class="lab">First name *</span><input type="text" name="first_name" required></label> 1398 <label class="field"><span class="lab">Last name *</span><input type="text" name="last_name" required></label> 1399 </div> 1400 <label class="field"><span class="lab">Email *</span><input type="email" name="email" required></label> 1401 <label class="field"><span class="lab">Country *</span> 1402 <select name="country" id="country" onchange="renderCart()"> 1403 <option value="United States" selected>United States</option> 1404 <option value="Canada">Canada</option> 1405 <option value="United Kingdom">United Kingdom</option> 1406 <option value="Australia">Australia</option> 1407 <option value="Other">Other (outside the US)</option> 1408 </select> 1409 </label> 1410 <div style="margin:8px 0;"> 1411 <div style="font-size:13px;color:var(--accent,#2f6fed);margin-bottom:4px;">Shipping address *</div> 1412 <label class="field"><span class="lab">Address line 1 *</span><input type="text" name="address1" required></label> 1413 <label class="field"><span class="lab">Address line 2</span><input type="text" name="address2"></label> 1414 <div class="row"> 1415 <label class="field"><span class="lab">City *</span><input type="text" name="city" required></label> 1416 <label class="field" style="max-width:90px;"><span class="lab">State *</span><input type="text" name="state" required></label> 1417 <label class="field" style="max-width:110px;"><span class="lab">ZIP *</span><input type="text" name="postal_code" required></label> 1418 </div> 1419 </div> 1420 <input type="hidden" name="cart" id="cartField"> 1421 1422 1423<div class="cf-turnstile" data-sitekey="0x4AAAAAADn2hnkY8P04I2ik" style="margin:12px 0;"></div>
vendor: 109 bytes, lines 1423-1424
1423 1424<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
1424<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
1424 1425 1426 1427 <button class="btn" type="submit" style="width:100%;justify-content:center;">Place order & get invoice</button> 1428 <div class="help" style="margin-top:6px;">We'll email you a PayPal invoice to complete payment. Nothing is charged here.</div> 1429 </form> 1430 1431 1432 1433<div class="panel pad" style="margin-top:12px;"> 1434 1435 <div class="lab" style="margin:0 0 6px;">Support us ð</div> 1436 1437 1438 1439 <div class="lab" style="margin:6px 0 4px; font-size:12px;">Send a tip / donation</div> 1440 <div style="display:flex; gap:8px; flex-wrap:wrap;"> 1441 <a class="btn ghost" href="https://venmo.com/u/NicsFix" target="_blank" rel="noopener">Venmo @NicsFix</a> 1442 <a class="btn ghost" href="https://cash.app/$NicsFix" target="_blank" rel="noopener">Cash App $NicsFix</a> 1443 1444 1445 <span class="btn ghost" style="cursor:default;">PayPal · <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="82ecebe1c2ecebe1f1e4ebfaace1edef">[email protected]</a></span> 1446 </div> 1447 1448 1449 1450 1451 1452 <div class="lab" style="margin:12px 0 4px; font-size:12px;">Become a member</div> 1453 <a class="btn" href="https://patreon.com/nicsfix" target="_blank" rel="noopener" style="background:#ff424d; border-color:#ff424d; color:#fff;">Join us on Patreon</a> 1454 <div class="muted" style="font-size:13px; margin-top:6px;">Join <strong>37</strong> other Patreon members today!</div> 1455 1456 1457</div> 1458 1459 </div> 1460 </div> 1461 </div> 1462 1463 <div id="cartToast">Added to cart â</div> 1464 <div id="cartBar"><button type="button" class="btn" onclick="jumpCart()">ð <span id="bar_count">0</span> item(s) · <span id="bar_total">$0</span> â View cart</button></div> 1465 1466 <div id="shopLightbox" role="dialog" aria-modal="true" aria-label="Product image"> 1467 <div class="lbframe"> 1468 <button type="button" class="lbx" aria-label="Close">Ã</button> 1469 <img id="shopLightboxImg" src="" alt=""> 1470 <div class="lbcount" id="shopLightboxCount" style="display:none;"></div> 1471 </div> 1472 </div>
vendor: 112 bytes, lines 1472-1474
1472 1473 1474 <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
1474<script> 1475 var CAT = {"1": {"id": 1, "max_per_order": 0, "name": "Apple HomePod (First-Generation, A1639)", "options": [{"choices": [{"delta": 0.0, "id": 13, "label": "White", "prompt": "", "stock": -1}, {"delta": 0.0, "id": 14, "label": "Black / Space Grey", "prompt": "", "stock": -1}], "id": 8, "kind": "checkbox", "name": "Color(s)", "required": 1}], "price": 160.0, "ship_intl": 60.0, "ship_us": 10.0, "stock": 5}, "10": {"id": 10, "max_per_order": 0, "name": "Subwoofer Connector", "options": [], "price": 10.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "11": {"id": 11, "max_per_order": 0, "name": "Internal AC Connector", "options": [], "price": 20.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "12": {"id": 12, "max_per_order": 1, "name": "Main Logic Cable", "options": [], "price": 20.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "13": {"id": 13, "max_per_order": 0, "name": "Microphones", "options": [], "price": 15.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "14": {"id": 14, "max_per_order": 0, "name": "Top Basket w/ Lower Rings", "options": [], "price": 20.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "15": {"id": 15, "max_per_order": 0, "name": "Top Basket Only", "options": [], "price": 15.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "16": {"id": 16, "max_per_order": 0, "name": "Plastic Top", "options": [{"choices": [{"delta": 30.0, "id": 3, "label": "A - little to no signs of use (+$30)", "prompt": "", "stock": -1}, {"delta": 25.0, "id": 4, "label": "B - some scratches, no cracks (+$25)", "prompt": "", "stock": -1}, {"delta": 20.0, "id": 5, "label": "C - scratches \u0026 small cracks, one piece (+$20)", "prompt": "", "stock": -1}, {"delta": 10.0, "id": 6, "label": "D - scratches \u0026 larger cracks, one piece (+$10)", "prompt": "", "stock": -1}], "id": 3, "kind": "select", "name": "Grade", "required": 1}, {"choices": [{"delta": 0.0, "id": 15, "label": "White", "prompt": "", "stock": -1}, {"delta": 0.0, "id": 16, "label": "Black / Space Grey", "prompt": "", "stock": -1}], "id": 10, "kind": "select", "name": "Color", "required": 1}], "price": 0.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "17": {"id": 17, "max_per_order": 0, "name": "Fabric Mesh", "options": [{"choices": [{"delta": 40.0, "id": 7, "label": "A - little to no signs of use, no stains (+$40)", "prompt": "", "stock": -1}, {"delta": 30.0, "id": 8, "label": "B - some signs of use / small stains (+$30)", "prompt": "", "stock": -1}], "id": 4, "kind": "select", "name": "Grade", "required": 1}], "price": 0.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "18": {"id": 18, "max_per_order": 0, "name": "Rubber Base", "options": [{"choices": [{"delta": 5.0, "id": 11, "label": "Grade A (White)", "prompt": "", "stock": -1}, {"delta": 5.0, "id": 12, "label": "Grade A (Black)", "prompt": "", "stock": -1}], "id": 5, "kind": "select", "name": "Grade", "required": 1}], "price": 0.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "19": {"id": 19, "max_per_order": 0, "name": "Extended T6 Torx Bit, 4mm Shank, 45mm Long", "options": [], "price": 5.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": 9}, "2": {"id": 2, "max_per_order": 0, "name": "Custom A1639 Apple HomePod", "options": [], "price": 220.0, "ship_intl": 60.0, "ship_us": 0.0, "stock": -1}, "20": {"id": 20, "max_per_order": 0, "name": "Custom Subwoofer Gasket for 1st Gen Apple HomePod", "options": [], "price": 5.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "21": {"id": 21, "max_per_order": 0, "name": "Sonos S16 Amp", "options": [], "price": 500.0, "ship_intl": 50.0, "ship_us": 25.0, "stock": 1}, "22": {"id": 22, "max_per_order": 0, "name": "Replacement Touch Sensor / Panel for 2nd Generation Apple HomePod", "options": [{"choices": [{"delta": 20.0, "id": 20, "label": "Plug and Play Installation (pre-installed onto replacement WHITE plastic top, plug and play, as pictured on right)", "prompt": "", "stock": -1}, {"delta": 20.0, "id": 21, "label": "Plug and Play Installation (pre-installed onto replacement BLACK plastic top, plug and play, as pictured on left)", "prompt": "", "stock": -1}, {"delta": 0.0, "id": 22, "label": "DiY Installation (no plastic top, just the touch sensor film, as pictured in middle)", "prompt": "", "stock": -1}], "id": 13, "kind": "select", "name": "Variant", "required": 1}, {"choices": [{"delta": 5.0, "id": 28, "label": "Add extended T6 bit!", "prompt": "", "stock": -1}, {"delta": 0.0, "id": 29, "label": "I do not need an extended t6 bit. I\u0027ve already checked and confirmed that I have a t6 screwdriver that will reach the screws in the speaker grille!", "prompt": "", "stock": -1}], "id": 17, "kind": "select", "name": "Extended T6 Torx Bit, 4mm Shank, 45mm Long", "required": 1}], "price": 50.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": 40}, "23": {"id": 23, "max_per_order": 6, "name": "Nic\u0027s Business Card", "options": [{"choices": [{"delta": 0.0, "id": 23, "label": "Biggie", "prompt": "", "stock": -1}, {"delta": 0.0, "id": 24, "label": "Mini", "prompt": "", "stock": -1}], "id": 14, "kind": "checkbox", "name": "Size", "required": 1}], "price": 0.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "24": {"id": 24, "max_per_order": 0, "name": "Parts Board", "options": [], "price": 10.0, "ship_intl": 16.0, "ship_us": 11.0, "stock": -1}, "25": {"id": 25, "max_per_order": 0, "name": "Amp to Main board ribbon connector", "options": [], "price": 4.0, "ship_intl": 16.0, "ship_us": 11.0, "stock": -1}, "26": {"id": 26, "max_per_order": 0, "name": "Custom Replacement Mesh for Apple HomePods", "options": [{"choices": [{"delta": 0.0, "id": 36, "label": "Slip-over - goes over your original mesh", "prompt": "", "stock": -1}, {"delta": 0.0, "id": 37, "label": "Replacement - swap out your original mesh", "prompt": "", "stock": 0}], "id": 19, "kind": "select", "name": "Fit", "required": 1}, {"choices": [{"delta": 0.0, "id": 38, "label": "One piece - slides on", "prompt": "", "stock": -1}, {"delta": 10.0, "id": 39, "label": "Two halves - joined together on the HomePod", "prompt": "", "stock": 0}], "id": 20, "kind": "select", "name": "Style", "required": 1}, {"choices": [{"delta": 0.0, "id": 40, "label": "White", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 41, "label": "Black", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 42, "label": "Grey", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 43, "label": "Red", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 44, "label": "Orange", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 45, "label": "Blue", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 46, "label": "Purple to Pink (changes with heat)", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 47, "label": "Burgundy to Pink (changes with heat)", "prompt": "", "stock": 5}, {"delta": 0.0, "id": 48, "label": "Rainbow Ombr\u00e9 (glows in the dark)", "prompt": "", "stock": 2}, {"delta": 20.0, "id": 49, "label": "Custom color - I\u0027ll order it for you", "prompt": "Which color? A filament brand/name, a link, or just describe it", "stock": -1}], "id": 21, "kind": "select", "name": "Color", "required": 1}], "price": 30.0, "ship_intl": 16.0, "ship_us": 11.0, "stock": -1}, "3": {"id": 3, "max_per_order": 0, "name": "Apple HomePod USB Adapter (for 1st Gen Only!)", "options": [{"choices": [{"delta": 5.0, "id": 1, "label": "Add storage c
1475ase (+$5)", "prompt": "", "stock": -1}], "id": 1, "kind": "checkbox", "name": "Storage case / protective cap", "required": 0}, {"choices": [{"delta": 5.0, "id": 17, "label": "Add UART breakout", "prompt": "", "stock": -1}], "id": 2, "kind": "checkbox", "name": "UART breakout", "required": 0}], "price": 20.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "4": {"id": 4, "max_per_order": 0, "name": "Refurbished Amplifier Board", "options": [], "price": 60.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "5": {"id": 5, "max_per_order": 0, "name": "Logic Board", "options": [], "price": 0.0, "ship_intl": 0.0, "ship_us": 0.0, "stock": 0}, "6": {"id": 6, "max_per_order": 0, "name": "Touch / Display Board for 1st Generation Apple HomePod", "options": [], "price": 30.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "7": {"id": 7, "max_per_order": 0, "name": "Power Supply", "options": [], "price": 30.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "8": {"id": 8, "max_per_order": 0, "name": "Microphone Suplicity Board", "options": [], "price": 15.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}, "9": {"id": 9, "max_per_order": 0, "name": "Logic to Display Cable", "options": [], "price": 10.0, "ship_intl": 16.0, "ship_us": 10.0, "stock": -1}}; 1476 function round2(n){ return Math.round(n*100)/100; } 1477 function money(n){ n=round2(n); return n%1===0 ? '$'+n : '$'+n.toFixed(2); } 1478 function esc(s){ var d=document.createElement('div'); d.textContent=(s==null?'':s); return d.innerHTML; } 1479 function loadCart(){ try{ return JSON.parse(localStorage.getItem('nicsfix_cart')||'[]'); }catch(e){ return []; } } 1480 function saveCart(c){ try{ localStorage.setItem('nicsfix_cart', JSON.stringify(c)); }catch(e){} } 1481 var CART = loadCart(); 1482 1483 function cardChoiceIds(id){ 1484 var ids = []; 1485 document.querySelectorAll('.opt[data-item="'+id+'"]:checked').forEach(function(el){ ids.push(parseInt(el.dataset.choice)); }); 1486 document.querySelectorAll('.opt-select[data-item="'+id+'"]').forEach(function(sel){ if(sel.value) ids.push(parseInt(sel.value)); }); 1487 return ids; 1488 } 1489 function unitFor(item, choiceIds){ 1490 var p = item.price; 1491 item.options.forEach(function(o){ o.choices.forEach(function(c){ if(choiceIds.indexOf(c.id)>=0) p+=c.delta; }); }); 1492 return round2(p); 1493 } 1494 // ---- Stock: item count and per-choice counts (-1 = not counted). What's left for a given 1495 // pick = the smallest tracked count, minus what's already in the cart for the same item or 1496 // the same choices. The server re-checks at checkout; this just stops obvious oversells. ---- 1497 function choiceStock(item, cid){ 1498 for (var i=0;i<item.options.length;i++){ var cs=item.options[i].choices; 1499 for (var j=0;j<cs.length;j++){ if(cs[j].id===cid) return (cs[j].stock==null ? -1 : cs[j].stock); } } 1500 return -1; 1501 } 1502 function remainingFor(itemId, choiceIds, skipIdx){ 1503 var item = CAT[itemId]; if(!item) return null; 1504 var best = null; 1505 function consider(stock, used){ if(stock>=0){ var r=stock-used; best = (best===null) ? r : Math.min(best, r); } } 1506 var usedItem = 0; 1507 CART.forEach(function(e, i){ if(i!==skipIdx && e.item_id===itemId) usedItem += e.qty; }); 1508 consider(item.stock==null ? -1 : item.stock, usedItem); 1509 (choiceIds||[]).forEach(function(cid){ 1510 var used = 0; 1511 CART.forEach(function(e, i){ if(i!==skipIdx && e.item_id===itemId && (e.choices||[]).indexOf(cid)>=0) used += e.qty; }); 1512 consider(choiceStock(item, cid), used); 1513 }); 1514 return best === null ? null : Math.max(0, best); 1515 } 1516 // Choices that ask for text (e.g. "Custom color" -> which color?): show the box only while 1517 // that choice is picked, and send what they typed with the cart line. 1518 function syncTextBoxes(id){ 1519 var picked = cardChoiceIds(id); 1520 document.querySelectorAll('.opt-text[data-item="'+id+'"]').forEach(function(box){ 1521 var on = picked.indexOf(parseInt(box.dataset.choice, 10)) >= 0; 1522 box.hidden = !on; 1523 }); 1524 } 1525 function cardTexts(id){ 1526 var t = {}; 1527 document.querySelectorAll('.opt-text[data-item="'+id+'"]').forEach(function(box){ 1528 if (!box.hidden) t[box.dataset.choice] = box.value.trim(); 1529 }); 1530 return t; 1531 } 1532 function syncCard(id){ 1533 var item = CAT[id]; if(!item) return; 1534 syncTextBoxes(id); 1535 var u = unitFor(item, cardChoiceIds(id)); 1536 var el = document.getElementById('price_'+id); 1537 if(el) el.textContent = money(u); 1538 var note = document.getElementById('stock_'+id); 1539 if(note){ 1540 var rem = remainingFor(id, cardChoiceIds(id), -1); 1541 note.className = 'stocknote' + (rem===0 ? ' out' : ''); 1542 note.textContent = rem===null ? '' : rem===0 ? (inCart(id) ? 'All available are in y
1542our cart' : 'Sold out') : 1543 rem <= 3 ? ('Only ' + rem + ' left') : ''; 1544 } 1545 } 1546 function inCart(id){ return CART.some(function(e){ return e.item_id===id; }); } 1547 function missingRequired(id){ 1548 var item = CAT[id]; 1549 for(var i=0;i<item.options.length;i++){ 1550 var o = item.options[i]; 1551 if(o.required){ 1552 var picked = cardChoiceIds(id).some(function(cid){ return o.choices.some(function(c){return c.id===cid;}); }); 1553 if(!picked) return o.name; 1554 } 1555 } 1556 return null; 1557 } 1558 function addToCart(id){ 1559 var miss = missingRequired(id); 1560 if(miss){ 1561 // Make sure the control they're being asked for is actually on screen. 1562 var w = document.getElementById('opts_' + id); 1563 if (w && w.hasAttribute('hidden')){ 1564 w.removeAttribute('hidden'); 1565 var t = w.parentNode.querySelector('.optoggle'); 1566 if (t){ t.setAttribute('aria-expanded','true'); t.textContent = 'Hide options \u25b4'; } 1567 } 1568 var sel = w && w.querySelector('select, input'); 1569 if (sel) sel.focus(); 1570 alert('Please choose '+miss+' first.'); 1571 return; 1572 } 1573 var item = CAT[id]; 1574 var choices = cardChoiceIds(id); 1575 var texts = cardTexts(id); 1576 var emptyBox = null; 1577 document.querySelectorAll('.opt-text[data-item="'+id+'"]').forEach(function(box){ 1578 if (!box.hidden && !box.value.trim() && !emptyBox) emptyBox = box; 1579 }); 1580 if (emptyBox){ emptyBox.focus(); alert('Please fill in: ' + emptyBox.placeholder); return; } 1581 var max = item.max_per_order || 0; 1582 var rem = remainingFor(id, choices, -1); 1583 if(rem !== null && rem < 1){ 1584 showToast(inCart(id) ? 'You already have all we have in stock' : 'Sorry, that one is sold out'); 1585 syncCard(id); return; 1586 } 1587 // merge identical lines (same item + same option set) 1588 // Lines only merge when the item, choices AND any typed text (e.g. custom color) match. 1589 function lineKey(iid, ch, tx){ return iid+'|'+ch.slice().sort().join(',')+'|'+JSON.stringify(tx||{}); } 1590 var key = lineKey(id, choices, texts); 1591 var found = CART.find(function(e){ return lineKey(e.item_id, e.choices||[], e.texts)===key; }); 1592 if(found){ found.qty += 1; if(max && found.qty>max) found.qty=max; } 1593 else { CART.push({item_id:id, qty:1, choices:choices, texts:texts}); } 1594 saveCart(CART); renderCart(); syncCard(id); showToast('Added to cart â'); 1595 } 1596 var toastTimer; 1597 function showToast(msg){ 1598 var t=document.getElementById('cartToast'); if(!t) return; 1599 t.textContent=msg; t.classList.add('show'); 1600 clearTimeout(toastTimer); toastTimer=setTimeout(function(){ t.classList.remove('show'); }, 1600); 1601 } 1602 function jumpCart(){ 1603 var c=document.getElementById('cartCol'); if(c) c.scrollIntoView({behavior:'smooth', block:'start'}); 1604 } 1605 function setQty(idx, delta){ 1606 var e = CART[idx]; if(!e) return; 1607 e.qty += delta; 1608 var max = (CAT[e.item_id]||{}).max_per_order || 0; 1609 var rem = remainingFor(e.item_id, e.choices||[], idx); 1610 if(e.qty<1){ CART.splice(idx,1); } 1611 else if(max && e.qty>max){ e.qty=max; } 1612 if(delta>0 && rem!==null && e.qty>rem){ e.qty=Math.max(1, rem); showToast(rem ? 'Only '+rem+' available' : 'Sorry, that one is sold out'); } 1613 saveCart(CART); renderCart(); syncCard(e.item_id); 1614 } 1615 function isIntl(){ var s=document.getElementById('country'); return s ? s.value!=='United States' : false; } 1616 function optLabels(item, choiceIds, texts){ 1617 var labels=[]; 1618 item.options.forEach(function(o){ o.choices.forEach(function(c){ 1619 if(choiceIds.indexOf(c.id)>=0){ 1620 var t = texts && texts[String(c.id)]; 1621 labels.push(c.label + (t ? ': \u201c' + t + '\u201d' : '')); 1622 } }); }); 1623 return labels.join(', '); 1624 } 1625 function renderCart(){ 1626 var box=document.getElementById('cartlines'); 1627 if(!CART.length){ box.innerHTML='<p class="muted" style="margin:0;">Your cart is empty.</p>'; 1628 document.getElementById('carttotals').style.display='none'; 1629 document.getElementById('checkoutBtn').style.display='none'; 1630 document.getElementById('checkoutPanel').style.display='none'; 1631 document.getElementById('cartBar').style.display='none'; return; } 1632 var intl=isIntl(), sub=0, best=0, html=''; 1633 CART.forEach(function(e, idx){ 1634 var item=CAT[e.item_id]; if(!item){ return; } 1635 var u=unitFor(item, e.choices||[]); var line=round2(u*e.qty); sub+=line; 1636 var rate = intl ? item.ship_intl : item.ship_us; if(rate>best) best=rate; 1637 var ol=optLabels(item, e.choices||[], e.texts); 1638 // A cart saved earlier can hold more than is left now - say so before checkout does. 1639 var left=remainingFor(e.item_id, e.choices||[], idx); 1640 var short = (left!==null && e.qty>left) ? '<br><span class="short">'+(left ? 'Only '+left+' left - lower the quantity' : 'Sold out - please remove')+'</span>' : ''; 1641 html += '<div class="cart-line"><div><strong>'+esc(item.name)+'</strong>'+(ol?'<br><span class="muted" style="font-size:11px;">'+esc(ol)+'</span>':'')+short+ 1642 '<div class="qtybox" style="margin-top:3px;"><button type="button" onclick="setQty('+idx+',-1)">â</button>'+e.qty+'<button type="button" onclick="setQty('+idx+',1)">+</button></div></div>'+ 1643 '<div style="text-align:right;">'+money(line)+'<br><button class="x" type="button" onclick="setQty('+idx+',-99)">remove</button></div></div>'; 1644 }); 1645 box.innerHTML=html; 1646 document.getElementById('c_sub').textContent=money(sub); 1647 document.getElementById('c_ship').textContent=money(best); 1648 document.getElementById('c_total').textContent=money(sub+best); 1649 document.getElementById('carttotals').style.display='block'; 1650 document.getElementById('checkoutBtn').style.display='block'; 1651 var count=0; CART.forEach(function(e){ count+=e.qty; });
1652 document.getElementById('bar_count').textContent=count; 1653 document.getElementById('bar_total').textContent=money(sub+best); 1654 document.getElementById('cartBar').style.display='block'; 1655 } 1656 function openCheckout(){ document.getElementById('checkoutPanel').style.display='block'; 1657 document.getElementById('checkoutPanel').scrollIntoView({behavior:'smooth',block:'nearest'}); } 1658 function prepCheckout(){ 1659 if(!CART.length){ alert('Your cart is empty.'); return false; } 1660 document.getElementById('cartField').value = JSON.stringify(CART.map(function(e){ return {item_id:e.item_id, qty:e.qty, choices:e.choices||[], texts:e.texts||{}}; })); 1661 return true; 1662 } 1663 renderCart(); 1664 Object.keys(CAT).forEach(function(id){ syncCard(parseInt(id, 10)); }); 1665 1666 // Item-image carousels: auto-cycle every 5s, with manual prev/next/dots (which reset the timer). 1667 document.querySelectorAll('.shop-card .ph').forEach(function(ph){ 1668 var slides = ph.querySelectorAll('.csl'); if (slides.length < 2) return; 1669 var dots = ph.querySelectorAll('.cdot'); var i = 0, timer; 1670 function show(n){ i = (n + slides.length) % slides.length; 1671 slides.forEach(function(s,k){ s.classList.toggle('on', k===i); }); 1672 dots.forEach(function(d,k){ d.classList.toggle('on', k===i); }); } 1673 function start(){ timer = setInterval(function(){ show(i+1); }, 5000); } 1674 function reset(){ clearInterval(timer); start(); } 1675 var nx = ph.querySelector('.cnext'), pv = ph.querySelector('.cprev'); 1676 if (nx) nx.addEventListener('click', function(e){ e.stopPropagation(); show(i+1); reset(); }); 1677 if (pv) pv.addEventListener('click', function(e){ e.stopPropagation(); show(i-1); reset(); }); 1678 dots.forEach(function(d,k){ d.addEventListener('click', function(e){ e.stopPropagation(); show(k); reset(); }); }); 1679 start(); 1680 }); 1681 1682 // Tap/click a product image to view it larger. Clicking the enlarged image advances to the 1683 // next image for that item (looping); if the item has only one image, it closes. 1684 (function(){ 1685 var lb = document.getElementById('shopLightbox'); 1686 if (!lb) return; 1687 var lbImg = document.getElementById('shopLightboxImg'); 1688 var lbCount = document.getElementById('shopLightboxCount'); 1689 var list = [], idx = 0; 1690 function render(){ 1691 lbImg.src = list[idx] || ''; 1692 if (list.length > 1){ 1693 lbCount.style.display = 'block'; 1694 lbCount.textContent = (idx + 1) + ' / ' + list.length; 1695 lbImg.style.cursor = 'pointer'; 1696 } else { 1697 lbCount.style.display = 'none'; 1698 lbImg.style.cursor = 'zoom-out'; 1699 } 1700 } 1701 function open(srcs, start, alt){ list = srcs; idx = start || 0; lbImg.alt = alt || ''; lb.classList.add('on'); document.body.style.overflow = 'hidden'; render(); } 1702 function close(){ lb.classList.remove('on'); lbImg.src = ''; list = []; document.body.style.overflow = ''; } 1703 document.querySelectorAll('.shop-card .ph').forEach(function(ph){ 1704 var imgs = Array.prototype.slice.call(ph.querySelectorAll('img')); 1705 imgs.forEach(function(im, k){ 1706 im.addEventListener('click', function(e){ 1707 e.stopPropagation(); 1708 var srcs = imgs.map(function(x){ return x.currentSrc || x.src; }); 1709 open(srcs, k, im.alt); 1710 }); 1711 }); 1712 }); 1713 // Click the big image: next image, or close if there's only one. 1714 lbImg.addEventListener('click', function(e){ 1715 e.stopPropagation(); 1716 if (list.length > 1){ idx = (idx + 1) % list.length; render(); } 1717 else { close(); } 1718 }); 1719 lb.querySelector('.lbx').addEventListener('click', function(e){ e.stopPropagation(); close(); }); 1720 lb.addEventListener('click', function(e){ if (e.target === lb) close(); }); // backdrop only 1721 document.addEventListener('keydown', function(e){ if (e.key === 'Escape' && lb.classList.contains('on')) close(); }); 1722 })(); 1723 </script>
1723 1724
1725<script> 1726// --------------------------------------------------------------------------- 1727// Shop browsing: search, category filter, popularity sort, grid/list density. 1728// All client-side - the catalogue is small and already on the page, so filtering 1729// is instant and shareable state lives in the URL hash. 1730// --------------------------------------------------------------------------- 1731var shopCat = ''; 1732function shopFilter(btn){ 1733 shopCat = btn.dataset.cat || ''; 1734 document.querySelectorAll('.chip[data-cat]').forEach(function(c){ 1735 c.classList.toggle('on', c === btn); 1736 }); 1737 applyShopFilter(); 1738} 1739function clearShopFilter(){ 1740 var q = document.getElementById('shopQ'); 1741 if (q) q.value = ''; 1742 var all = document.querySelector('.chip[data-cat=""]'); 1743 if (all) shopFilter(all); else applyShopFilter(); 1744} 1745function toggleOpts(id, btn){ 1746 var w = document.getElementById('opts_' + id); 1747 if (!w) return; 1748 var open = w.hasAttribute('hidden'); 1749 if (open) w.removeAttribute('hidden'); else w.setAttribute('hidden', ''); 1750 btn.setAttribute('aria-expanded', open ? 'true' : 'false'); 1751 btn.textContent = open ? 'Hide options \u25b4' : 'Choose options \u25be'; 1752} 1753function setShopView(v){ 1754 document.querySelectorAll('.shop-grid').forEach(function(g){ 1755 g.classList.toggle('list', v === 'list'); 1756 }); 1757 var g = document.getElementById('viewGrid'), l = document.getElementById('viewList'); 1758 if (g) g.classList.toggle('on', v !== 'list'); 1759 if (l) l.classList.toggle('on', v === 'list'); 1760 try { localStorage.setItem('nicsfix_shopview', v); } catch (e) {} 1761} 1762function applyShopFilter(){ 1763 var box = document.getElementById('shopQ'); 1764 var q = (box ? box.value : '').trim().toLowerCase(); 1765 // Every word must appear somewhere in the item's text - "usb dock" finds the dock. 1766 var words = q ? q.split(/\s+/) : []; 1767 var shown = 0; 1768 document.querySelectorAll('.shop-card').forEach(function(card){ 1769 var hay = card.dataset.search || ''; 1770 var okCat = !shopCat || card.dataset.cat === shopCat; 1771 var okQ = words.every(function(w){ return hay.indexOf(w) !== -1; }); 1772 var vis = okCat && okQ; 1773 card.classList.toggle('is-hidden', !vis); 1774 if (vis) shown++; 1775 }); 1776 // Hide a category heading once nothing under it survives the filter. 1777 document.querySelectorAll('.shop-sec').forEach(function(sec){ 1778 var any = sec.querySelector('.shop-card:not(.is-hidden)'); 1779 sec.classList.toggle('is-hidden', !any); 1780 }); 1781 // Popularity sort reorders within each section; off restores the shop's own order. 1782 var pop = document.getElementById('popFirst'); 1783 document.querySelectorAll('.shop-grid').forEach(function(grid){ 1784 var cards = Array.prototype.slice.call(grid.querySelectorAll('.shop-card')); 1785 cards.sort(function(a, b){ 1786 if (pop && pop.checked){ 1787 var d = (+b.dataset.units || 0) - (+a.dataset.units || 0); 1788 if (d) return d; 1789 } else { 1790 return (+a.dataset.ord || 0) - (+b.dataset.ord || 0); 1791 } 1792 return (+a.dataset.ord || 0) - (+b.dataset.ord || 0); 1793 }); 1794 cards.forEach(function(c){ grid.appendChild(c); }); 1795 }); 1796 var empty = document.getElementById('shopEmpty'); 1797 if (empty) empty.hidden = shown !== 0; 1798 var count = document.getElementById('shopCount'); 1799 if (count){ 1800 var total = document.querySelectorAll('.shop-card').length; 1801 count.textContent = (shown === total) 1802 ? (total + ' item' + (total === 1 ? '' : 's')) 1803 : ('Showing ' + shown + ' of ' + total); 1804 } 1805 var x = document.getElementById('shopQX'); 1806 if (x) x.hidden = !q; 1807} 1808(function initShopTools(){ 1809 // Remember each card's original position so the sort can be undone exactly. 1810 document.querySelectorAll('.shop-grid').forEach(function(grid){ 1811 Array.prototype.slice.call(grid.querySelectorAll('.shop-card')) 1812 .forEach(function(c, i){ c.dataset.ord = i; }); 1813 }); 1814 var box = document.getElementById('shopQ'); 1815 if (box){ 1816 box.addEventListener('input', applyShopFilter); 1817 box.addEventListener('keydown', function(e){ 1818 if (e.key === 'Escape'){ box.value = ''; applyShopFilter(); } 1819 }); 1820 } 1821 var x = document.getElementById('shopQX'); 1822 if (x) x.addEventListener('click', function(){ box.value = ''; box.focus(); applyShopFilter(); }); 1823 // "/" focuses search, the same shortcut the staff pages use. 1824 document.addEventListener('keydown', function(e){ 1825 var t = e.target, tag = (t.tagName || '').toLowerCase(); 1826 if (e.key === '/' && tag !== 'input' && tag !== 'textarea' && !t.isContentEditable){ 1827 e.preventDefault(); if (box) { box.focus(); box.select(); } 1828 } 1829 }); 1830 try { 1831 var v = localStorage.getItem('nicsfix_shopview'); 1832 if (v === 'list') setShopView('list'); 1833 } catch (e) {} 1834 // #sec-N links from elsewhere still work; a ?q= in the URL pre-fills the search. 1835 var m = location.search.match(/[?&]q=([^&]*)/); 1836 if (m && box){ box.value = decodeURIComponent(m[1].replace(/\+/g, ' ')); } 1837 applyShopFilter(); 1838})(); 1839</script>
1839 1840
1841<script> 1842(function(){ 1843 function initIntroClamp(){ 1844 var i = document.getElementById('shopIntro'), b = document.getElementById('shopIntroMore'); 1845 if (i && b && i.scrollHeight > i.clientHeight + 2) b.hidden = false; 1846 } 1847 function initClamp(){ 1848 // Item notes clamp the same way descriptions do - a long note made cards enormous. 1849 document.querySelectorAll('.shop-card .itemnote[data-clampable-note]').forEach(function(n){ 1850 var btn = n.nextElementSibling; 1851 if (!btn || !btn.classList.contains('notemore') || n.dataset.clampInit) return; 1852 n.dataset.clampInit = '1'; 1853 n.classList.add('clamped'); 1854 if (n.scrollHeight > n.clientHeight + 2){ 1855 btn.hidden = false; 1856 btn.onclick = function(){ 1857 var open = n.classList.toggle('clamped'); 1858 btn.textContent = open ? 'Read more' : 'Show less'; 1859 }; 1860 } 1861 }); 1862 document.querySelectorAll('.shop-card .desc[data-clampable]').forEach(function(d){ 1863 var btn = d.nextElementSibling; 1864 if (!btn || !btn.classList.contains('readmore') || d.dataset.clampInit) return; 1865 d.dataset.clampInit = '1'; 1866 d.classList.add('clamped'); 1867 // Only offer "Read more" when the text actually overflows the clamp. 1868 if (d.scrollHeight <= d.clientHeight + 2){ d.classList.remove('clamped'); return; } 1869 btn.hidden = false; 1870 btn.addEventListener('click', function(){ 1871 btn.textContent = d.classList.toggle('clamped') ? 'Read more' : 'Read less'; 1872 }); 1873 }); 1874 } 1875 function initAllClamps(){ initIntroClamp(); initClamp(); } 1876 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initAllClamps); 1877 else initAllClamps(); 1878 window.addEventListener('load', initAllClamps); // re-measure once fonts/images settle 1879})(); 1880</script>
1880 1881 1882</div> 1883 1884 1885<style> 1886 #chatFab{position:fixed;right:18px;bottom:18px;z-index:9998;border:none;border-radius:999px; 1887 background:var(--accent,#2f6fed);color:#fff;padding:12px 18px;font-weight:600;cursor:pointer; 1888 box-shadow:0 6px 20px rgba(0,0,0,.25);font-size:15px;} 1889 #chatPanel{position:fixed;right:18px;bottom:74px;z-index:9999;width:340px;max-width:calc(100vw - 36px); 1890 background:var(--surface,#fff);border:1px solid var(--border,#e3e3e8);border-radius:16px; 1891 box-shadow:0 12px 40px rgba(0,0,0,.28);display:none;flex-direction:column;overflow:hidden;} 1892 #chatPanel.open{display:flex;} 1893 #chatPanel .ch-head{background:var(--accent,#2f6fed);color:#fff;padding:12px 14px;display:flex; 1894 justify-content:space-between;align-items:center;} 1895 #chatPanel .ch-head button{background:none;border:none;color:#fff;font-size:20px;cursor:pointer;line-height:1;} 1896 #chatBody{padding:12px 14px;max-height:46vh;overflow:auto;display:flex;flex-direction:column;gap:8px;} 1897 .ch-msg{max-width:84%;padding:7px 11px;border-radius:12px;font-size:13px;line-height:1.4;} 1898 .ch-msg.them{background:var(--surface-2,#f1f1f4);align-self:flex-start;} 1899 .ch-msg.me{background:var(--accent,#2f6fed);color:#fff;align-self:flex-end;} 1900 .ch-msg .who{display:block;font-size:10px;opacity:.7;margin-bottom:2px;} 1901 #chatBody .ch-meta{font-size:11px;color:var(--muted,#999);text-align:center;} 1902 .ch-foot{border-top:1px solid var(--border,#eee);padding:10px 12px;} 1903 .ch-foot textarea,.ch-foot input{width:100%;} 1904 .ch-row{display:flex;gap:6px;} 1905 .ch-end{font-size:12px;color:var(--muted,#999);background:none;border:none;cursor:pointer;text-decoration:underline;} 1906</style> 1907 1908<button id="chatFab" type="button" onclick="chatToggle()">ð¬ Chat</button> 1909<div id="chatPanel" aria-live="polite"> 1910 <div class="ch-head"><span id="chatTitle">Chat with Nic's Fix</span><button type="button" onclick="chatToggle()" aria-label="Close">Ã</button></div> 1911 1912 <div id="chatPre" style="padding:14px;"> 1913 <p class="muted" style="margin:0 0 10px;font-size:13px;"></p> 1914 <form id="chatStartForm" onsubmit="return chatStart(event)"> 1915 <div class="row" style="gap:8px;"> 1916 <label class="field"><span class="lab">First name *</span><input type="text" name="first_name" required></label> 1917 <label class="field"><span class="lab">Last name *</span><input type="text" name="last_name" required></label> 1918 </div> 1919 <label class="field"><span class="lab">Email *</span><input type="email" name="email" required></label> 1920 <label class="field"><span class="lab">Message</span><textarea name="message" rows="2" placeholder="How can we help?"></textarea></label> 1921 1922 1923<div class="cf-turnstile" data-sitekey="0x4AAAAAADn2hnkY8P04I2ik" style="margin:12px 0;"></div>
1924<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
1924 1925 1926 <div id="chatErr" class="flash error" style="display:none;font-size:12px;margin:6px 0;"></div> 1927 <button class="btn" type="submit" style="width:100%;justify-content:center;">Start chat</button> 1928 </form> 1929 </div> 1930 1931 <div id="chatView" style="display:none;flex-direction:column;flex:1;"> 1932 <div id="chatBody"></div> 1933 <div class="ch-foot"> 1934 <form onsubmit="return chatSend(event)"> 1935 <div class="ch-row"> 1936 <input type="text" id="chatInput" placeholder="Type a messageâ¦" autocomplete="off"> 1937 <button class="btn sm" type="submit">Send</button> 1938 </div> 1939 </form> 1940 <div style="margin-top:8px;text-align:center;"> 1941 <button class="ch-end" type="button" onclick="chatEnd()">End chat</button> 1942 </div> 1943 <div id="chatEndBox" style="display:none;margin-top:8px;border-top:1px solid var(--border,#eee);padding-top:8px;"> 1944 <label class="check" style="font-size:13px;"><input type="checkbox" id="chatEmailCopy" checked> Email me a copy of this chat</label> 1945 <div class="ch-row" style="margin-top:6px;"> 1946 <button class="btn sm" type="button" onclick="chatConfirmEnd()">End & close</button> 1947 <button class="btn ghost sm" type="button" onclick="document.getElementById('chatEndBox').style.display='none';">Cancel</button> 1948 </div> 1949 </div> 1950 </div> 1951 </div> 1952</div> 1953
1954<script> 1955(function(){ 1956 var TOKEN = null, lastId = 0, timer = null; 1957 var SHOP = "Nic\u0027s Fix"; 1958 try { TOKEN = localStorage.getItem('nicsfix_chat_token'); } catch(e){} 1959 1960 window.chatToggle = function(){ 1961 var p = document.getElementById('chatPanel'); 1962 var open = p.classList.toggle('open'); 1963 if (open){ if (TOKEN){ showView(); startPolling(); } else { showPre(); } } 1964 else { stopPolling(); } 1965 }; 1966 function showPre(){ document.getElementById('chatPre').style.display='block'; document.getElementById('chatView').style.display='none'; } 1967 function showView(){ document.getElementById('chatPre').style.display='none'; document.getElementById('chatView').style.display='flex'; } 1968 function esc(s){ var d=document.createElement('div'); d.textContent=s; return d.innerHTML; } 1969 1970 function addMsg(m){ 1971 var body = document.getElementById('chatBody'); 1972 var div = document.createElement('div'); 1973 div.className = 'ch-msg ' + (m.sender === 'staff' ? 'them' : 'me'); 1974 var who = m.sender === 'staff' ? (m.author || SHOP) : 'You'; 1975 div.innerHTML = '<span class="who">'+esc(who)+'</span>'+esc(m.body); 1976 body.appendChild(div); 1977 body.scrollTop = body.scrollHeight; 1978 } 1979 function meta(text){ 1980 var body = document.getElementById('chatBody'); 1981 var d = document.createElement('div'); d.className='ch-meta'; d.textContent=text; body.appendChild(d); 1982 } 1983 1984 window.chatStart = function(ev){ 1985 ev.preventDefault(); 1986 var form = document.getElementById('chatStartForm'); 1987 var err = document.getElementById('chatErr'); err.style.display='none'; 1988 fetch('/chat/start', {method:'POST', body:new FormData(form)}) 1989 .then(function(r){ return r.json(); }) 1990 .then(function(d){ 1991 if(!d.ok){ err.textContent = d.error || 'Could not start chat.'; err.style.display='block'; return; } 1992 TOKEN = d.token; try{ localStorage.setItem('nicsfix_chat_token', TOKEN); }catch(e){} 1993 document.getElementById('chatBody').innerHTML=''; 1994 lastId = 0; showView(); startPolling(); 1995 }) 1996 .catch(function(){ err.textContent='Network error - please try again.'; err.style.display='block'; }); 1997 return false; 1998 }; 1999 2000 window.chatSend = function(ev){ 2001 ev.preventDefault(); 2002 var inp = document.getElementById('chatInput'); 2003 var text = inp.value.trim(); if(!text || !TOKEN) return false; 2004 inp.value=''; 2005 var fd = new FormData(); fd.append('message', text); 2006 fetch('/chat/'+encodeURIComponent(TOKEN)+'/send', {method:'POST', body:fd}) 2007 .then(function(r){ return r.json(); }).then(function(){ pollOnce(); }); 2008 return false; 2009 }; 2010 2011 function pollOnce(){ 2012 if(!TOKEN) return; 2013 fetch('/chat/'+encodeURIComponent(TOKEN)+'/poll?after='+lastId) 2014 .then(function(r){ if(r.status===404){ resetChat(); throw 0; } return r.json(); }) 2015 .then(function(d){ 2016 if(!d.ok) return; 2017 (d.messages||[]).forEach(function(m){ addMsg(m); lastId = Math.max(lastId, m.id); }); 2018 }).catch(function(){}); 2019 } 2020 function startPolling(){ pollOnce(); stopPolling(); timer = setInterval(pollOnce, 4000); } 2021 function stopPolling(){ if(timer){ clearInterval(timer); timer=null; } } 2022 2023 window.chatEnd = function(){ document.getElementById('chatEndBox').style.display='block'; }; 2024 window.chatConfirmEnd = function(){ 2025 if(!TOKEN){ resetChat(); return; } 2026 var copy = document.getElementById('chatEmailCopy').checked; 2027 var fd = new FormData(); fd.append('email_copy', copy ? '1':'0'); 2028 fetch('/chat/'+encodeURIComponent(TOKEN)+'/close', {method:'POST', body:fd}) 2029 .then(function(r){ return r.json(); }) 2030 .then(function(d){ meta(d.emailed ? 'Chat ended - a copy was emailed to you.' : 'Chat ended. Thanks!'); resetChat(); }); 2031 }; 2032 function resetChat(){ 2033 stopPolling(); TOKEN=null; lastId=0; 2034 try{ localStorage.removeItem('nicsfix_chat_token'); }catch(e){} 2035 document.getElementById('chatEndBox').style.display='none'; 2036 setTimeout(function(){ document.getElementById('chatBody').innerHTML=''; showPre(); 2037 var f=document.getElementById('chatStartForm'); if(f) f.reset(); }, 1500); 2038 } 2039})(); 2040</script>
2040 2041
2042<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"eac2729b69fa4a9eba2fc98858bc43fa","r":1,"spa":2}' crossorigin="anonymous"></script>
2042 2043</body> 2044</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.