PageSourceSearch

https://portal.nicsfix.com/shop

html nicsfix.com collected 2026-10-02 23:22:46 UTC 131,797 bytes, 2,044 lines download raw bytes

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&#39;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&#x27;s an unusually large volume of requests and repairs right now. There will be delays! It&#x27;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&#39;s Fix" style="width:100%; height:100%; object-fit:cover; border-radius:12px;"></div>
42    <h1>Nic&#39;s Fix</h1>
43    <p>Pods, docks &amp; 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&#x27;t guarantee my parts will fix your problem! �<br>�<br>You&#x27;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&#x27;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&#39;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&#39;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 &#34;custom color&#34; (+$20), tell me what you want, and i&#39;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&#39;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&#x27;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&#x27;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 &quot;Custom color&quot; (+$20), tell me what you want, and I&#x27;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&#39;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&#39;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&#x27;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&#39;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&#39;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&#x27;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&#x27;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&#39;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&#39;re lucky, you&#39;ll get a soft, foam mesh version of the card. size biggie mini" data-units="0"
453             data-name="nic&#39;s business card">
454          <div class="ph">
455            
456              <div class="csl on"><img loading="lazy" src="/shop/item-media/36" alt="Nic&#39;s Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/37" alt="Nic&#39;s Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/38" alt="Nic&#39;s Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/39" alt="Nic&#39;s Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/40" alt="Nic&#39;s Business Card"></div><div class="csl"><img loading="lazy" src="/shop/item-media/41" alt="Nic&#39;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&#39;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&#x27;re lucky, you&#x27;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&#39;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&#39;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&#x27;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&#39;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&#39;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&#x27;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&#39;t find anything that&#39;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&#39;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&#x27;t find anything that&#x27;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&#x27;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 &amp; small cracks, one piece (+$20) d - scratches &amp; 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 &amp; small cracks, one piece (+$20)</option><option value="6" data-delta="10.0">D - scratches &amp; 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&#160;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&#160;protected]</a></strong> (Goods &amp; 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&#x27;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&#x27;s have fun and hang out while we work!</p><p></p><p>I&#x27;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&#x27;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&#x27;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&#x27;ll get early access to uploads before they&#x27;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&#x27;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&#x27;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 &amp; 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&#160;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&#39;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 &amp; 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.