← back to Lawyer Directory Builder
Iter 22: enrich populated /results firm cards with audit-grade + size chips
8cae410c5c9d72b7f4514073826812302bb7d9d4 · 2026-05-03 23:29:49 -0700 · SteveStudio2
Each firm card now displays three info chips between meta-line and actions:
1. Site audit chip — buckets marketing_score 0-100 into A/A−/B+/B/B−/C+/C
grades via auditChip() helper. Score null → "unaudited" (Inter caps,
distinguishable from graded italic-metal). Color tiers: A = metal-glow
(champagne hot), B = metal (champagne cool), C = #a07a40 (oxidized).
2. Firm size chip — band ("solo"/"small"/"medium"/"large") via sizeChip(),
falls back to "N attorneys" when band is null.
3. Verify-on-State-Bar micro-link — direct query into apps.calbar.ca.gov
licensee search w/ firm name pre-filled. Dotted underline → solid
champagne on hover. Compliance asset: turns the State Bar from a footnote
into a per-firm CTA.
CSS: .firm-chips row, .firm-chip pill (hairline rule, label-uppercase + serif
italic value), .firm-verify dotted-underline. .firm-card:hover lifts chip
border to .4 metal alpha. Print stylesheet untouched (chips inherit color
overrides naturally).
The marketing_score column was already retrieved by match_firms.ts via
site_audits subquery; this is the first surface to render it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 8cae410c5c9d72b7f4514073826812302bb7d9d4
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Sun May 3 23:29:49 2026 -0700
Iter 22: enrich populated /results firm cards with audit-grade + size chips
Each firm card now displays three info chips between meta-line and actions:
1. Site audit chip — buckets marketing_score 0-100 into A/A−/B+/B/B−/C+/C
grades via auditChip() helper. Score null → "unaudited" (Inter caps,
distinguishable from graded italic-metal). Color tiers: A = metal-glow
(champagne hot), B = metal (champagne cool), C = #a07a40 (oxidized).
2. Firm size chip — band ("solo"/"small"/"medium"/"large") via sizeChip(),
falls back to "N attorneys" when band is null.
3. Verify-on-State-Bar micro-link — direct query into apps.calbar.ca.gov
licensee search w/ firm name pre-filled. Dotted underline → solid
champagne on hover. Compliance asset: turns the State Bar from a footnote
into a per-firm CTA.
CSS: .firm-chips row, .firm-chip pill (hairline rule, label-uppercase + serif
italic value), .firm-verify dotted-underline. .firm-card:hover lifts chip
border to .4 metal alpha. Print stylesheet untouched (chips inherit color
overrides naturally).
The marketing_score column was already retrieved by match_firms.ts via
site_audits subquery; this is the first surface to render it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
src/server/leads.ts | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/src/server/leads.ts b/src/server/leads.ts
index 607f07d..eaf5621 100644
--- a/src/server/leads.ts
+++ b/src/server/leads.ts
@@ -202,6 +202,19 @@ th{color:var(--ink-mute);font-weight:500;font-size:10px;text-transform:uppercase
.firm-web{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--metal);font-weight:500;border-bottom:1px solid var(--rule);padding-bottom:2px;transition:border-color 200ms ease,color 200ms ease}
.firm-web:hover{color:var(--metal-glow);border-bottom-color:var(--metal)}
+/* ─── Firm chips (audit-grade + firm-size) ───────────────────────── */
+.firm-chips{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px;align-items:center}
+.firm-chip{display:inline-flex;align-items:baseline;gap:6px;padding:5px 10px 5px 9px;border:1px solid var(--rule);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-mute);font-weight:500;background:linear-gradient(180deg,rgba(184,153,104,.018),transparent);transition:border-color 200ms ease,color 200ms ease}
+.firm-card:hover .firm-chip{border-color:rgba(184,153,104,.4);color:var(--ink-soft)}
+.firm-chip-label{color:var(--ink-mute);font-size:9px;letter-spacing:.22em}
+.firm-chip-val{font-family:var(--serif);font-style:italic;font-weight:400;font-size:14px;letter-spacing:.01em;color:var(--metal);text-transform:none}
+.firm-chip.audit-A .firm-chip-val{color:var(--metal-glow)}
+.firm-chip.audit-B .firm-chip-val{color:var(--metal)}
+.firm-chip.audit-C .firm-chip-val{color:#a07a40}
+.firm-chip.audit-unaudited .firm-chip-val{color:var(--ink-mute);font-style:normal;font-family:var(--sans);font-size:10px;letter-spacing:.16em;text-transform:uppercase;font-weight:500}
+.firm-verify{display:inline-flex;align-items:center;gap:4px;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-mute);font-weight:500;border-bottom:1px dotted var(--rule);padding-bottom:2px;transition:color 200ms ease,border-color 200ms ease}
+.firm-verify:hover{color:var(--metal);border-bottom-color:var(--metal)}
+
/* ─── Empty state (zero firms in proximity) ─────────────────────── */
.empty-state{position:relative;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:96px 0 88px;margin:0 0 48px;overflow:hidden}
.empty-state::before{content:"\\2014";position:absolute;top:50%;left:50%;transform:translate(-50%,-58%);font-family:var(--serif);font-style:italic;font-weight:300;font-size:40vw;line-height:1;color:transparent;-webkit-text-stroke:1.5px rgba(184,153,104,.28);pointer-events:none;z-index:0;letter-spacing:-.04em}
@@ -458,6 +471,29 @@ router.get('/find-a-lawyer/results', async (req, res) => {
} catch (e) {
console.error('[find/results] match error', e);
}
+ const auditChip = (score: number | null): string => {
+ if (score == null) {
+ return `<span class="firm-chip audit-unaudited"><span class="firm-chip-label">Site audit</span><span class="firm-chip-val">unaudited</span></span>`;
+ }
+ let grade = 'C', cls = 'audit-C';
+ if (score >= 90) { grade = 'A'; cls = 'audit-A'; }
+ else if (score >= 80) { grade = 'A−'; cls = 'audit-A'; }
+ else if (score >= 70) { grade = 'B+'; cls = 'audit-B'; }
+ else if (score >= 60) { grade = 'B'; cls = 'audit-B'; }
+ else if (score >= 50) { grade = 'B−'; cls = 'audit-B'; }
+ else if (score >= 40) { grade = 'C+'; cls = 'audit-C'; }
+ return `<span class="firm-chip ${cls}"><span class="firm-chip-label">Site audit</span><span class="firm-chip-val">${grade}</span></span>`;
+ };
+ const sizeChip = (band: string | null, count: number | null): string => {
+ if (!band && !count) return '';
+ const display = band ? esc(band).replace(/_/g, ' ').toLowerCase() : `${count} attorney${count === 1 ? '' : 's'}`;
+ return `<span class="firm-chip"><span class="firm-chip-label">Firm size</span><span class="firm-chip-val">${display}</span></span>`;
+ };
+ const verifyLink = (name: string): string => {
+ const q = encodeURIComponent(name);
+ return `<a class="firm-verify" href="https://apps.calbar.ca.gov/attorney/Licensee/Search?Type=B&FreeText=${q}" target="_blank" rel="noopener">verify on State Bar →</a>`;
+ };
+
const firmCards = firms.length === 0
? ''
: firms.map((f, i) => `
@@ -470,6 +506,11 @@ router.get('/find-a-lawyer/results', async (req, res) => {
${f.distance_km != null ? `<span class="firm-dot">·</span><span>${f.distance_km.toFixed(1)} km</span>` : ''}
${f.attorney_count ? `<span class="firm-dot">·</span><span>${f.attorney_count} attorney${f.attorney_count === 1 ? '' : 's'}</span>` : ''}
</div>
+ <div class="firm-chips">
+ ${auditChip(f.marketing_score)}
+ ${sizeChip(f.firm_size_band, f.attorney_count)}
+ ${verifyLink(f.name || '')}
+ </div>
<div class="firm-actions">
${f.phone ? `<a class="firm-tel" href="tel:${esc(f.phone)}">${esc(f.phone)}</a>` : ''}
${f.website ? `<a class="firm-web" href="${esc(f.website)}" target="_blank" rel="noopener">Visit website →</a>` : ''}
← 4704a6f Add 7 Express route modules: auth, admin, billing, community
·
back to Lawyer Directory Builder
·
Iter 23: /signup/welcome editorial confirmation + critique-a 1982ca0 →