[object Object]

← back to Ventura Claw

/services: client-side search filter (mirrors /connectors pattern) — substring match across data-svc + name + description + category heading; live 'N / 18 match' count; sections hide when fully filtered; skips demo + how-it-works + compare sections

2c1f81fe550840ee1bae7dd97f6688e041033e4a · 2026-05-07 12:26:32 -0700 · Steve

Files touched

Diff

commit 2c1f81fe550840ee1bae7dd97f6688e041033e4a
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu May 7 12:26:32 2026 -0700

    /services: client-side search filter (mirrors /connectors pattern) — substring match across data-svc + name + description + category heading; live 'N / 18 match' count; sections hide when fully filtered; skips demo + how-it-works + compare sections
---
 server/public/services.html | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/server/public/services.html b/server/public/services.html
index f1a79ba..107473b 100644
--- a/server/public/services.html
+++ b/server/public/services.html
@@ -764,6 +764,44 @@
   </div>
 </section>
 
+<!-- SERVICE FILTER — client-side search across the 18-card catalog -->
+<section class="cat-section" style="padding-top:12px;padding-bottom:12px">
+  <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
+    <input type="search" id="svcFilter" placeholder='Filter the 18 services — try "stripe", "domain", "insurance"…'
+           oninput="filterSvc(this.value)"
+           style="flex:1;min-width:240px;background:var(--bg-2);color:var(--ink);border:1px solid var(--rule);padding:11px 14px;font-family:var(--mono);font-size:13px;outline:none;border-radius:3px" />
+    <span id="svcCount" style="font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:var(--ink-mute);text-transform:uppercase">18 services</span>
+  </div>
+</section>
+<script>
+function filterSvc(q) {
+  q = (q || '').trim().toLowerCase();
+  let total = 0, shown = 0;
+  document.querySelectorAll('section.cat-section').forEach(sec => {
+    if (sec.querySelector('#svcFilter')) return;          // skip filter row itself
+    if (sec.id === 'try-it') return;                      // skip demo widget
+    if (sec.classList.contains('how-section')) return;    // skip 'how it works' card
+    if (sec.classList.contains('compare-section')) return;
+    let any = false;
+    const cards = sec.querySelectorAll('.svc');
+    if (!cards.length) return;
+    cards.forEach(card => {
+      total++;
+      const svc = (card.dataset.svc || '').toLowerCase();
+      const name = (card.querySelector('.svc-name')?.textContent || '').toLowerCase();
+      const desc = (card.querySelector('.svc-desc')?.textContent || '').toLowerCase();
+      const head = (sec.querySelector('h2')?.textContent || '').toLowerCase();
+      const hit = !q || svc.includes(q) || name.includes(q) || desc.includes(q) || head.includes(q);
+      card.style.display = hit ? '' : 'none';
+      if (hit) { shown++; any = true; }
+    });
+    sec.style.display = any ? '' : 'none';
+  });
+  const c = document.getElementById('svcCount');
+  if (c) c.textContent = q ? (shown + ' / ' + total + ' match') : (total + ' services');
+}
+</script>
+
 <!-- DEMO WIDGET — domain + email setup, real plumbing -->
 <section class="cat-section demo-section" id="try-it">
   <div class="cat-head">

← 1843ec6 [ops] local ventura-claw process recovered (was missing from  ·  back to Ventura Claw  ·  tighten .gitignore: add missing standing-rule patterns (tmp/ 8cdeaf7 →