[object Object]

← back to Lawyer Directory Builder

Iter 27: /find-a-lawyer submit-disable + loading state + 'curated'→'indexed' compliance fix

50c720ef820843215f1dd3b0d4d122db24b7e2f3 · 2026-05-04 01:53:13 -0700 · SteveStudio2

VISUAL: form submit now shows 'LOGGING YOUR INQUIRY…' + champagne-disc
spinner, button .is-loading state with darker gradient + .7 opacity to
read as disabled. Prevents double-submit and gives feedback during
POST→/thanks redirect (felt broken on slow connections).

CSS: .cta-metal:disabled (oxidized gradient + opacity), .cta-spinner
(14px champagne-bordered ring + cta-spin keyframe), prefers-reduced-
motion fallback (no spin, just static dot).

JS: form.config submit handler disables button + flips text via
data-loading-text attribute. data-default-text preserved for potential
restore-on-error path (not wired yet).

§6155 COMPLIANCE FIX (caught in this iter):
"curated" → "indexed" across 4 surfaces:
- public/index.html meta description + H1 hero ("Every licensed
  attorney in California, indexed.")
- src/server/leads.ts meta description template + /find-a-lawyer
  H2 display ("Find your California attorney, indexed.")

"Curated" implies editorial selection of attorneys, which is exactly the
language §6155 attaches to lawyer referral services. We're a directory
of every CA-licensed attorney drawn from the State Bar public roll —
the right word is "indexed." Also factually more accurate; we don't
make selection judgments, we list everyone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 50c720ef820843215f1dd3b0d4d122db24b7e2f3
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Mon May 4 01:53:13 2026 -0700

    Iter 27: /find-a-lawyer submit-disable + loading state + 'curated'→'indexed' compliance fix
    
    VISUAL: form submit now shows 'LOGGING YOUR INQUIRY…' + champagne-disc
    spinner, button .is-loading state with darker gradient + .7 opacity to
    read as disabled. Prevents double-submit and gives feedback during
    POST→/thanks redirect (felt broken on slow connections).
    
    CSS: .cta-metal:disabled (oxidized gradient + opacity), .cta-spinner
    (14px champagne-bordered ring + cta-spin keyframe), prefers-reduced-
    motion fallback (no spin, just static dot).
    
    JS: form.config submit handler disables button + flips text via
    data-loading-text attribute. data-default-text preserved for potential
    restore-on-error path (not wired yet).
    
    §6155 COMPLIANCE FIX (caught in this iter):
    "curated" → "indexed" across 4 surfaces:
    - public/index.html meta description + H1 hero ("Every licensed
      attorney in California, indexed.")
    - src/server/leads.ts meta description template + /find-a-lawyer
      H2 display ("Find your California attorney, indexed.")
    
    "Curated" implies editorial selection of attorneys, which is exactly the
    language §6155 attaches to lawyer referral services. We're a directory
    of every CA-licensed attorney drawn from the State Bar public roll —
    the right word is "indexed." Also factually more accurate; we don't
    make selection judgments, we list everyone.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 public/index.html   |  4 ++--
 src/server/leads.ts | 35 ++++++++++++++++++++++++++++++++---
 2 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/public/index.html b/public/index.html
index 890c261..1b25040 100644
--- a/public/index.html
+++ b/public/index.html
@@ -4,7 +4,7 @@
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width,initial-scale=1">
 <title>The California Bar — Curated.</title>
-<meta name="description" content="A curated directory of every licensed California attorney. 89,000+ verified profiles, 42,000+ firms, 1,000+ audited websites. Talk to one in 24 hours.">
+<meta name="description" content="An indexed directory of every licensed California attorney, drawn from the State Bar public roll. 89,000+ profiles, 42,000+ firms, 1,000+ independently audited sites. Browse, verify, contact directly.">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
 <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;1,300;1,400&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
@@ -405,7 +405,7 @@
     <p class="eyebrow reveal d1">California<span class="dot"></span>Est. on the public bar registry</p>
     <h1 class="display reveal d2">
       Every licensed attorney<br>
-      in California, <em>curated.</em>
+      in California, <em>indexed.</em>
     </h1>
     <p class="lede reveal d3">
       A complete index of the people authorized to practice law in this state — verified against the State Bar's public roll. Browse firms by city and practice area. Contact attorneys directly through their listed channels. We're a directory, not a referral service: we don't accept fees for sending you to a specific attorney, and we don't route your case anywhere on your behalf.
diff --git a/src/server/leads.ts b/src/server/leads.ts
index eaf5621..48264ab 100644
--- a/src/server/leads.ts
+++ b/src/server/leads.ts
@@ -61,7 +61,7 @@ function layout(title: string, body: string, opts: { user?: any; variant?: 'publ
 <html lang="en"><head>
 <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
 <title>${esc(title)} · Counsel &amp; Bar</title>
-<meta name="description" content="${esc(title)} on Counsel &amp; Bar — California's curated attorney directory.">
+<meta name="description" content="${esc(title)} on Counsel &amp; Bar — California's indexed attorney directory, drawn from the State Bar public roll.">
 <meta name="robots" content="index,follow">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -158,6 +158,17 @@ th{color:var(--ink-mute);font-weight:500;font-size:10px;text-transform:uppercase
 .config-cta-row{display:flex;justify-content:space-between;align-items:center;gap:24px;padding:36px 0 0;border-top:1px solid var(--rule);margin-top:8px;flex-wrap:wrap}
 .cta-metal{display:inline-flex;align-items:center;gap:14px;padding:20px 36px;background:linear-gradient(180deg,#d4b683 0%,#b89968 38%,#9c7e4f 100%);color:var(--noir);border:1px solid var(--metal);font-size:12px;letter-spacing:.18em;text-transform:uppercase;font-weight:500;cursor:pointer;text-decoration:none;transition:all 240ms ease;box-shadow:inset 0 1px 0 rgba(255,235,200,.35),0 1px 0 rgba(0,0,0,.4);width:auto}
 .cta-metal:hover{background:linear-gradient(180deg,#e3c89a 0%,#c8a674 50%,#a98559 100%);transform:translateY(-1px);box-shadow:inset 0 1px 0 rgba(255,240,210,.5),0 4px 18px rgba(184,153,104,.18);color:var(--noir)}
+.cta-metal:disabled{cursor:not-allowed;opacity:.7;transform:none;background:linear-gradient(180deg,#9c8758 0%,#80684a 50%,#5e4a30 100%);box-shadow:inset 0 1px 0 rgba(255,235,200,.15)}
+.cta-metal:disabled:hover{background:linear-gradient(180deg,#9c8758 0%,#80684a 50%,#5e4a30 100%);transform:none}
+.cta-metal .cta-spinner{display:none;width:14px;height:14px;border:1.5px solid rgba(10,10,12,.25);border-top-color:var(--noir);border-radius:50%;animation:cta-spin 720ms linear infinite}
+.cta-metal.is-loading .cta-arrow{display:none}
+.cta-metal.is-loading .cta-spinner{display:inline-block}
+@keyframes cta-spin{to{transform:rotate(360deg)}}
+@media (prefers-reduced-motion: reduce){
+  .cta-metal .cta-spinner{animation:none;border-top-color:rgba(10,10,12,.25)}
+  .cta-metal.is-loading .cta-spinner{position:relative}
+  .cta-metal.is-loading .cta-spinner::after{content:'·';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);color:var(--noir);font-weight:700;font-size:18px;line-height:1}
+}
 .public-trust{margin-top:64px;padding:32px 36px;border:1px solid var(--rule);background:linear-gradient(180deg,rgba(184,153,104,.025),transparent)}
 .public-trust .t-eyebrow{font-size:10px;letter-spacing:.3em;text-transform:uppercase;color:var(--metal);font-weight:500;margin:0 0 12px}
 .public-trust p{margin:0;color:var(--ink-soft);font-size:13px;line-height:1.7;font-weight:300}
@@ -325,7 +336,7 @@ router.get('/find-a-lawyer', (req, res) => {
       <span class="v-text">Volume I</span>
       <span class="v-meta">Edition 2026 · Public Directory</span>
     </div>
-    <h2 class="public-display">Find your California attorney, <em>curated.</em></h2>
+    <h2 class="public-display">Find your California attorney, <em>indexed.</em></h2>
     <p class="public-lede">A short configurator. Three sections — about you, the matter, where you are. We'll show California-licensed firms that handle your practice area in your county. <b>You decide who to call.</b></p>
     <p class="config-tagline" style="font-family:var(--serif);font-style:italic;color:var(--metal);font-size:14px;letter-spacing:.04em;margin:-32px 0 56px">Three short sections. About sixty seconds. The State Bar's public roll, indexed.</p>
     ${flashFromQuery(req)}
@@ -389,10 +400,28 @@ router.get('/find-a-lawyer', (req, res) => {
       </div>
 
       <div class="config-cta-row">
-        <button type="submit" class="cta-metal">See California-licensed firms <span style="font-family:var(--serif);font-style:italic;font-size:18px;line-height:1">→</span></button>
+        <button type="submit" class="cta-metal" data-default-text="See California-licensed firms" data-loading-text="Logging your inquiry…">
+          <span class="cta-text">See California-licensed firms</span>
+          <span class="cta-arrow" style="font-family:var(--serif);font-style:italic;font-size:18px;line-height:1">→</span>
+          <span class="cta-spinner" aria-hidden="true"></span>
+        </button>
         <p class="field-note" style="margin:0;max-width:32ch">By submitting you authorize us to display matching firms. Your details are not sent to any firm until you click to contact one.</p>
       </div>
     </form>
+    <script>
+      (function(){
+        var f = document.querySelector('form.config');
+        if (!f) return;
+        f.addEventListener('submit', function(){
+          var btn = f.querySelector('button[type="submit"]');
+          if (!btn) return;
+          btn.disabled = true;
+          btn.classList.add('is-loading');
+          var t = btn.querySelector('.cta-text');
+          if (t) t.textContent = btn.dataset.loadingText || 'Submitting…';
+        });
+      })();
+    </script>
 
     <aside class="public-trust scroll-reveal">
       <p class="t-eyebrow">A directory, not a referral service</p>

← 95b1ccc Iter 26: /audit.html — champagne-tier scoring + critique-app  ·  back to Lawyer Directory Builder  ·  Iter 28: /directory — champagne tiers + monogram + Volume st afcb5d7 →