Creative ideas + design notes
Commits with substantial prose (≥120 chars) — the rationale behind each move.
2ad78f2 · 2026-05-04 · Iter 45: SEO essentials — robots.txt, sitemap.xml, favicon (4× formats)
All three were 404 in production. Google had no efficient way to crawl
the site; the methodology v1.0 audit's own signal #8 ("Favicon
present") was failing on Counsel & Bar's own site, which is the kind
of irony only a Rule 7.1 plaintiff would notice.
Files shipped:
public/robots.txt:
- Allow all by default
- Disallow /admin/, /api/, /dashboard, /login, post-payment
confirmation pages (no SEO value, surface PII to crawlers)
- Crawl-delay: 1 (gentle hint)
- Sitemap reference
public/sitemap.xml:
- 10 public surfaces with lastmod, changefreq, priority
- image:image entry on landing pointing at og.png so Google Image
can index the share tile
- Priorities: landing 1.0, find/directory 0.9, audit/methodology
0.8, upgrade/data 0.7, signup 0.6, privacy/terms 0.3
public/favicon.svg + public/favicon-32.png:
- SVG primary (CB monogram in champagne gradient on noir, scales
perfectly across pixel densities)
- PNG 32×32 fallback (Playwright-rendered from same SVG) for legacy
browsers + Apple Touch Icon
- meta name="theme-color" content="#0a0a0c" so mobile browsers tint
the URL bar to match noir background
Wired <link rel="icon" type="image/svg+xml"> + PNG fallback +
apple-touch-icon + theme-color into landing, /methodology, /audit, and
leads.ts layout (covers /find-a-lawyer + all dynamic routes).
Verified live: all four assets return 200, 3 rel=icon links per
surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
31d0eaf · 2026-05-04 · Iter 44: OG share image (1200×630) + summary_large_image cards everywhere
Iter 42-43 added share metadata text but no og:image. Social previews on
LinkedIn / X / Slack / Discord / Facebook rendered as small summary
cards (text-only). Now full preview cards with brand image.
Image (public/og.svg → public/og.png via Playwright render):
- 1200×630 noir gradient bg
- Subtle italic 'I' watermark in deep right (4% opacity, 780px)
- Volume strip masthead: 'Volume I · Edition 2026 · Counsel & Bar'
- CB monogram top-right
- Italic-metal display 'Every California-licensed attorney, indexed.'
('indexed.' in italic-metal, rest in ivory)
- Lede in ink-soft: 'Drawn from the State Bar of California public roll.
Independently audited. Methodology v1.0 published.'
- Footer strip with hairline rule:
· 'lawyers.agentabrams.com' (italic-metal)
· 'A DIRECTORY · NOT A REFERRAL SERVICE · NOT THE STATE BAR'
(ink-mute caps, §6155 disambiguation in the share preview itself)
Wiring (all 4 high-traffic surfaces):
- og:image, og:image:width=1200, og:image:height=630, og:image:alt
- twitter:card upgraded summary → summary_large_image
- twitter:image points to same og.png
Deployed to prod via rsync (og.png 200KB, og.svg 1.5KB, plus updated
HTML for 3 static pages and leads.ts for dynamic routes). Verified
live: og.png 200/204782 bytes; 4-5 og:image meta tags per surface.
Brand language now travels with every shared link — including the
§6155 'NOT THE STATE BAR' disambiguation, baked into the image itself
in case a regulator screenshots a social preview.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ee84e9e · 2026-05-04 · Iter 43: OG + JSON-LD propagated to /methodology, /audit, /find-a-lawyer
Iter 42 added share-metadata to landing only. The other 3 high-traffic
public surfaces still rendered as bare URLs in social preview cards.
Now full coverage:
/methodology.html:
- canonical, robots index/follow
- og:type=article, og:title, og:description (52-char preview), og:url
- twitter:card=summary
- JSON-LD TechArticle (datePublished/dateModified 2026-05-04, version
1.0, author + publisher Organization, mainEntityOfPage)
/audit.html:
- canonical, robots index/follow, full description meta
- og:website, og:title, og:description (mentions 1,016 audited sites,
methodology v1.0)
- twitter:card=summary
- JSON-LD Dataset (description enumerates all 12 signals,
creator+publisher, license link to /terms.html, isAccessibleForFree
true). Dataset is the right Schema.org type for an aggregated
public-firm scoring; gives Google Dataset Search eligibility.
/find-a-lawyer (and all leads.ts-served routes via the layout helper):
- og:website, og:title (page-title from layout('...')), og:description
pulls in §6155-safe boilerplate ('Independent. Not a referral
service. Not the State Bar.')
- og:locale=en_US
- twitter:card=summary
JSON-LD intentionally NOT added to find-a-lawyer routes — page is the
configurator, not a content article; SearchAction is already declared
on landing's WebSite entity.
Verified live on https://lawyers.agentabrams.com/ — all 3 surfaces
return 200 with 8-11 meta/structured-data tags each.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4ff44cf · 2026-05-04 · Iter 42: landing — OG + Twitter card + JSON-LD structured data + canonical
Landing had zero share-metadata. Link previews on LinkedIn / X / Discord /
Slack rendered as bare URLs, no preview card. Added the full set:
- <link rel="canonical" href="https://lawyers.agentabrams.com/">
- <meta name="robots" content="index, follow">
- og:type=website, og:site_name, og:title, og:description, og:url,
og:locale=en_US (no og:image yet — use summary card; can add a CSS-
rendered noir/champagne tile later if a paid social campaign needs it)
- twitter:card=summary, twitter:title, twitter:description (both tighter
copy than OG so they fit X's 70-char render window)
JSON-LD structured data:
Organization (@id #org) + WebSite (@id #website) graph entries
- name, alternateName, url, description (with §6155 cite verbatim)
- areaServed: California
- knowsAbout: California attorneys, lawyer directory, California State
Bar
- sameAs: https://www.calbar.ca.gov/ (Schema.org disambiguation —
tells Google + crawlers explicitly that we are NOT calbar.ca.gov,
we just reference it as authoritative source — same §6155-correct
disambiguation we already do in the visual eyebrow + title)
- publisher relation linking WebSite to Organization
- potentialAction: SearchAction targeting /find-a-lawyer?zip={zip}
so Google can render a sitelinks search box
Deployed to prod via rsync (public/index.html only — static, no pm2
restart needed). Verified 14 meta + structured-data tags render live
at https://lawyers.agentabrams.com/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a0b97b0 · 2026-05-04 · Iter 40: landing footer — add methodology + State Bar lookup links, fix mislabel
Three improvements to the landing footer's link columns:
1. 'Browse the directory' (pointing at /audit.html) was a mislabel — that
link IS the site-audit page, not a directory browser. Relabeled to
'Site audit' (accurate) and added 'Audit methodology' link below it
pointing to /methodology.html (iter 29's substantiation document,
not previously linked from the footer).
2. Attorneys column added 'Find your firm' link to /directory (iter 28's
attorney self-service surface — was orphaned in the footer despite
being a major lawyer-facing entry point). 'Claim your firm' relabeled
to 'Claim your listing' to match the verb used everywhere else on
/signup ('Claim your listing →' button) and /signup/welcome
('Welcome to the registry').
3. Legal column added 'State Bar lookup ↗' as third link — directs
highest-stakes verification (license status) to the authoritative
apps.calbar.ca.gov/attorney/Licensee/Search rather than back into our
directory. Matches the §6155-correct deep-linking pattern from iter
31 (/404 suggest-grid) + iter 36 (/find-a-lawyer/thanks). Privacy
and Terms shortened to 'Privacy' / 'Terms' since the column header
provides the 'Legal' context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a14af3b · 2026-05-04 · Iter 39: legal lockdown — three remaining LRS-flavor copy fixes
Steve directive: 'Site must be legally bound. No gray areas.' Final §6155
sweep across all surfaces. Three fixes applied:
1. /find-a-lawyer URGENCY option label —
was: 'I need to talk to someone today'
Reads as 'we'll connect you to talk to someone' = LRS routing.
now: 'I plan to call a firm today'
User describes their own plan; we don't promise a connection.
2. /find-a-lawyer field-note under submit —
was: 'By submitting you authorize us to display matching firms.'
"Matching" is the §6155 LRS-pattern verb.
now: 'By submitting you authorize us to display California-licensed
firms in your area, ranked by proximity.'
Mathematical proximity ranking is not "matching." Honest copy.
3. /terms.html section 4 (How the directory works) —
was: 'See a list of California-licensed firms in your area that
match your practice area and ZIP.'
"match" again — same §6155 verb.
now: 'See a list of California-licensed firms in your area that you
can filter by practice area and ZIP.'
The user filters; we don't match.
After this iter, the user-visible verb 'match' (and its conjugations)
no longer appears anywhere on the public site outside of internal DB
schema (lead.matched_firm_ids column, status='matched' enum, CSS class
.pill.matched) which is admin-only and never user-rendered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3fe9d64 · 2026-05-04 · Iter 38: landing — critical §6155 fixes from cold-eye debate critique
Cold-eye debate-team-fast review caught two CRITICAL §6155 risks the
in-loop iterations had drifted into. Both fixed before commit:
1. <title> IMPERSONATION RISK (highest severity) —
was: 'The California Bar — Curated.'
"The California Bar" reads as the State Bar of California itself.
This is the line a regulator screenshots first — it appears in SERP
results, browser tabs, OG previews. Footer disclaimers can't undo
it. Plus 'Curated' was supposed to be removed in iter 27 but
somehow survived in the title.
now: 'Counsel & Bar — California attorney directory (independent ·
not the State Bar)'
Eyebrow line 405 also reframed:
was: 'California · Est. on the public bar registry'
now: 'California · Independent directory · Not the State Bar'
Triple-redundant disambiguation across title + eyebrow + footer.
2. FEATURE 02 §6155 FEE-HANDLING IMPLICATION —
was: 'Thirty minutes, by Zoom · Pay a flat fee for a real
consultation.'
Reads like Counsel & Bar arranges paid attorney consultations,
which veers toward the §6155 'for compensation, refers' definition.
now: 'Consultations on the attorney's terms · Each firm publishes
its own consultation fee, schedule, and booking flow. Counsel & Bar
never touches the money — payments and engagement letters run
directly between you and the attorney.'
Closes the fee-handling gap: we don't book consultations, we don't
collect attorney fees, attorneys handle their own intake.
3. VISUAL FIX — replaced the giant italic 'I' watermark (clamp 420-
1100px, opacity .045) which read as a vertical bar / accidental
glyph at desktop widths. Aston Martin doesn't put a giant
translucent letter behind the car. Replaced with .hero-folio in the
top-right: 'Vol. I' italic Cormorant + champagne hairline rule +
'MMXXVI' caps + '№ 001' caps. Real piece of typography that earns
the 'Edition 2026' framing the rest of the page is reaching for.
Old .hero-watermark class set to display:none (no-op, prevents
cache-error if older HTML is served).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
f10bb42 · 2026-05-04 · Iter 37: /data/thanks editorial confirmation matching iter 25 pattern
Brought /data/thanks paid path to the editorial standard set by
/upgrade/thanks (iter 25). Was eyebrow + h1 + button + bookmark URL —
minimal next to the polished /upgrade/thanks for the same money.
What shipped on the paid path:
- Volume strip 'Edition 2026 · Order Confirmed' (or 'Subscription
Active' for the recurring tier)
- Italic-metal display 'Your data is ready.' (kept; still right)
- Order № receipt strip — DATA-YYYY-MMDD-NNNN format, italic-metal
tabular-nums (matches EZ-YYYY-MMDD-NNNN on /upgrade/thanks). Three
cols: Order №, Logged, Charged ($amount). For subscription tier
shows '$X/mo' and label 'Recurring'.
- 'What's next' 3-cell grid:
i. Download the CSV — flat-noir + champagne-hairline button
ii. Bookmark your download URL — full URL in monospace code box
iii. Use the data lawfully — State Bar permitted-use rules,
CCPA/CPRA, no bulk SMS / robocalls / resale, inactive
attorneys excluded at export
- 'For the record' trust block — data sourcing (State Bar public DB +
publicly-indexed firm sites + LA City Active Business Licenses
NAICS 5411), 'directory listing is independent of any commercial
offering,' Stripe PCI disclosure.
Pending path also got Volume strip 'Edition 2026 · Reservation
Pending' + italic-metal 'Reservation noted.' + back-link to /data.
Same iter-25 architectural pattern, four surfaces deep now: /upgrade,
/upgrade/thanks, /signup/welcome, /data/thanks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
e6238ca · 2026-05-04 · Iter 36: /find-a-lawyer/thanks — State Bar deep-link + Rule 7.1 honesty
Two fixes to the Verify-with-State-Bar cell on /find-a-lawyer/thanks:
1. Deep-link the State Bar destination — was 'calbar.ca.gov' (homepage,
user has to navigate the menus to find licensee search). Now
apps.calbar.ca.gov/attorney/Licensee/Search opens the search form
directly. One-click verify, matches the deep-link pattern already
used in /find-a-lawyer/results firm-card 'verify on State Bar' chips
(iter 22) and /404 suggest-grid (iter 31).
2. Soften the unsubstantiated daily-refresh claim — was 'We refresh
against it daily but the live source is authoritative.' We do run
periodic CalBar scrapes, but no live 'daily' guarantee in
production. Now: 'We draw from that roll; the live source at
calbar.ca.gov remains authoritative for any disciplinary status
check.' Honest, §6155-correct, and explicitly directs the highest-
stakes verification (disciplinary status) to the authoritative
source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7d98dc9 · 2026-05-04 · Iter 35: Lawyer Pro pitch + dashboard FTC copy — three latent compliance fixes
Three latent §6155 / FTC issues in core code, caught while scanning for
'24 hour' / 'first call' / 'matched lead' patterns post-iter-34:
1. Lawyer Pro pitch (paid $29/mo product) used 'matched lead' / 'lead
alerts' / 'the moment a matched lead is submitted' — implies our
directory routes leads to attorneys, which IS the LRS pattern §6155
prohibits. Reframed:
- 'matched lead' → 'directory inquiry'
- 'lead alerts' → 'directory-inquiry alerts'
- 'Get notified the moment a matched lead is submitted in your
practice area + ZIP' → 'See an alert when a directory inquiry
mentions your practice area + ZIP — and you decide whether to
reach out'
- 'Direct messages with potential clients' → 'Direct messages
with visitors who initiate contact'
- 'Real-time lead alerts (practice area + ZIP)' → 'Directory-
inquiry alerts (practice area + ZIP) — for your awareness'
The product is unchanged; the copy now describes it in §6155-safe
language: directory shows inquiries → attorney decides to reach
out. No matching, no routing.
2. /dashboard EZ Upgrade pitch said 'live in 7 days' (unconditional FTC
promise). iter 24 fixed this on /upgrade but missed the dashboard
reference. Now matches: 'typical go-live: seven business days from
content intake.'
3. /directory inquiry success-flash said "We'll be in touch within 24
hours." — same unconditional SLA that violated FTC §5 advertising
rules on a service we don't actually staff 24 hours. Softened to
"We'll respond within two business days." Operationally honest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c18ff69 · 2026-05-04 · Iter 34: landing stat ledger — fix misleading §6155 stat + Rule 7.1 substantiation
CRITICAL §6155 fix: stat iv was '24 hrs · From submission to first call'
— this implies we route inquiries to attorneys who then call the consumer
within 24 hours, which IS the LRS pattern §6155 prohibits non-licensee
operators from running. We don't do that. We show a list of firms; the
user calls them.
Replaced with: '$0 to search · No fee from you, no fee from firms · we
do not refer cases or split fees · §6155'. Same visual weight, accurate
service description, codifies the §6155 posture in the stat ledger
itself.
Rule 7.1 substantiation: stats i-iii were bare integers ('89,390' /
'42,636' / '1,016') with no source attribution. Hardcoded numbers next
to real attorney names is the textbook Rule 7.1 trap (caught by Kimi in
iter 21 on the proof strip). Now:
- i. ~89,000 — '~' approximator + 'as of 2026-05-04 · State Bar public
roll' attribution. Reflects truth that the count fluctuates daily.
- ii. ~42,600 — same '~' + 'aggregated from the registry' attribution.
- iii. 1,016 — exact (we audit a finite cohort, count is precise) +
'methodology v1.0 →' link to the substantiation document (iter 29).
- iv. $0 to search — fee posture, with the §6155 rule cite right there.
Visual: each stat keeps the big italic Cormorant numeral + caps label,
gains a tiny italic-mute attribution sub-line below the label. No
dimensional change; just additional regulatory context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9d4bfa7 · 2026-05-04 · Iter 33: walker — added 4 new surfaces from overnight loop
Captures the full overnight loop progress in the click-through video:
New surfaces walked:
- 06b-methodology → /methodology.html (iter 29)
- 06c-directory → /directory (iter 28)
- 06d-upgrade → /upgrade (iter 24)
- 10-404 → /lawyer-near-me-typo → 404 page (iter 31)
Walker now captures 14 surfaces (was 11). Each gets its own captioned
PNG + a per-surface entry in the per-page Steve-avatar PiP overlay.
Brand-audit (monogram + volStrip + disclaimer presence) reports per
surface, including the new ones — note: the audit checks only for the
.public-vol class, so methodology/directory/upgrade/404 show
volStrip=false despite having Volume strips (under .method-vol /
.dir-vol / .up-vol / .nf-vol class names). Cosmetic walker false-
negative; surfaces themselves are correct.
Output: /tmp/lawyer-clickthrough/walkthrough-2026-05-04T12-11-22.webm
(6.0 MB, ~14 × 4-second frames + 1-second transitions)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4041f14 · 2026-05-04 · Iter 32: /privacy + /terms — Volume strip masthead + italic-metal display
Last two public-facing surfaces still on bare 'Privacy Policy.' / 'Terms
of Service.' headers and short-eyebrow chrome. Brought into the
editorial standard.
/privacy.html:
- Volume strip 'Edition 2026 · Privacy Policy · Effective 2026-05-03'
- Italic-metal display 'What we collect. What we don't.' (was 'Privacy
Policy.' — bare label, now reads as a statement of position)
- Lede gains strong-emphasis on 'what we collect, why, how we use it,
who it goes to' + 'CCPA / CPRA' (was muted-only, hard to scan)
/terms.html:
- Volume strip 'Edition 2026 · Terms of Service · Effective 2026-05-03'
- Italic-metal display 'The terms. Plain language.' (was 'Terms of
Service.' — restated as the document's actual posture)
- Lede explicit: 'most of this is the boring legal stuff every site
has to publish — we've kept it in plain English wherever the law
lets us'
Both pages now share the same Volume strip + italic-metal + responsive
clamp() display sizing as /find-a-lawyer, /signup, /dashboard, /audit,
/methodology, /directory, /upgrade, /404. Twelve consecutive public
surfaces sharing a masthead system.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
41bda2f · 2026-05-04 · Iter 31: /404 — path-aware editorial recovery surface
Replaced bare 'Return home / Find counsel' 404 with full editorial
recovery layout. Most 404 traffic on a directory site is typo'd or
stale-link — give the user a real path forward instead of a dead-end.
What shipped:
- Volume strip masthead 'Edition 2026 · Not Found' (was bare eyebrow
'Volume I · Edition 2026' floating without rule)
- Italic-metal display 'Not in the *registry.*' (kept; still the right
line)
- Path strip — surfaces requested URL verbatim in champagne monospace:
'REQUESTED /lawyer-near-me'. Path is HTML-escaped and truncated to 80
chars; query strings stripped (PII risk). User immediately knows what
we couldn't find.
- 3-cell 'Where you may have meant to land' grid with outlined italic
Romans (i/ii/iii):
i. Find counsel → /find-a-lawyer (the 3-section configurator)
ii. Audit a site → /audit.html (independent audit + methodology)
iii. Verify on the State Bar → apps.calbar.ca.gov licensee search
(if the visitor came here trying to look up a lawyer, send them
to the authoritative source — that's the §6155-correct move)
- Big italic '404' watermark stays at 5% opacity behind the content
(was 8% — softened so it doesn't fight the suggest-grid for attention)
- CTA gradient → flat noir + champagne hairline (consistent with
iter 24's editorial CTA standard, replacing brushed-metal that read
too 'marketing landing')
- Footer adds 'Methodology v1.0' link (closes another link to the
iter-29 substantiation document)
Removed cta-primary brushed-metal gradient (luxury-watch-ad feel
flagged in iter 24 critique). All CTAs now flat noir + champagne
hairline + letter-spacing-on-hover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
370aff1 · 2026-05-04 · Iter 30: /signup + /login submit-disable + loading state
Applies the iter-27 loading-state pattern to both auth forms.
/signup (was: 'Claim your listing →' static button → vanilla form post):
- 'Claiming your listing…' loading text + champagne-disc spinner
- Button disabled on submit; data-loading-text drives the swap
- Prevents double-submit during bcrypt password hash + DB INSERT
/login (was: 'Sign in →' static button):
- 'Signing in…' loading text + spinner
- Same submit-disable JS pattern
Shared CSS in portal layout:
- button.big-btn:disabled — oxidized gradient + .7 opacity (matches the
/upgrade .up-cta:disabled / /find-a-lawyer .cta-metal:disabled style)
- button.big-btn .cta-spinner — 13px champagne-bordered ring
- @keyframes portal-spin (720ms linear infinite)
- prefers-reduced-motion: animation disabled, spinner stays as static
champagne dot so the loading state still reads as a state change
Both auth surfaces now match /find-a-lawyer (consumer) and /upgrade
(payment) for submit-feedback consistency. Slow-network attorneys no
longer see a 'hung browser' on signup or login.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c9f2fae · 2026-05-04 · Iter 29: /methodology page — closes the Rule 7.1 substantiation gap
Two prior iterations (iter 26 audit, iter 28 directory) referenced
"methodology v1.0" without a real document to point at. Both critiques
flagged this as the largest substantiation gap. This iter ships the
canonical document.
NEW: public/methodology.html — full editorial substantiation document:
1. Twelve signals enumerated, each with:
- Italic Roman numeral (i. through xii.)
- Plain-English description
- Inline <code> with the actual technical name (HTTPS, og:image,
application/ld+json, etc.) so the criteria can be verified by
anyone with browser devtools
- Point weight (15+10+10+10+10+10+10+5+5+5+5+5 = 100)
The twelve match exactly what src/enrich/site_audit.ts measures.
2. Tier thresholds table — High 90+, Mid 70-89, Low 50-69, Weak <50.
Each tier in its own champagne shade matching the audit cards.
3. "What this score is NOT" disclosure block — explicit boundaries:
- Not a measure of legal skill, courtroom outcomes, expertise
- Not a measure of client satisfaction, retention, reputation
- Not a measure of bar standing, malpractice, or discipline
- Not an endorsement
- Audit independent of any commercial offering; purchasing
EZ Upgrade does not adjust the score.
4. Refresh cadence (90-day rolling, on-demand re-audit available)
5. Corrections & removal — audit@agentabrams.com, 5-business-day SLA
for re-audit. Directory listing draws on State Bar public roll;
removal must be requested at the State Bar directly.
6. Versionbar at the bottom — Version v1.0 / Last reviewed 2026-05-04
/ Next scheduled review 2026-08-01.
Wired up:
- public/audit.html keystrip — "Methodology v1.0 · 12 signals · how
we score →" now links to /methodology.html (was bare label)
- public/audit.html per-card stamp — "Audited [date] · methodology
v1.0" now linked
- src/server/directory.ts score panel — "Site signals · methodology
v1.0 →" now linked
Brand: Volume strip, italic-metal display, monogram, hairline-rule
sections matching the rest of the site. No Rule 7.1 fabricated stats.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
afcb5d7 · 2026-05-04 · Iter 28: /directory — champagne tiers + monogram + Volume strip + critical compliance decoupling
Visual:
- Monogram added to brand header (was bare text)
- Volume strip 'Edition 2026 · Attorney Self-Service'
- Italic-metal display 'Find your firm. Audit your site. Reserve an EZ Upgrade.'
- Score colors traffic-light → champagne tiers (tier-a/b/c/d/x), tabular-nums
- 'Marketing health score' → 'Site signals · methodology v1.0' (matches /audit.html)
- Lede: 'twelve published signals'; 'typical go-live: seven business days from content intake'
Debate-team-fast caught CRITICAL pay-to-play §6155 risk that I fixed before commit:
1. SCORE-UPGRADE DECOUPLING — old layout: low-score panel → directly into
$499 EZ Upgrade pitch with copy 'designed to close the gap your audit
just exposed.' That's a textbook §6155 implication: directory shows a
score, then sells you a fix for the score. Replaced with:
a. <details> disclosure 'See what an EZ Upgrade looks like for this
firm' between score and pitch — pitch is now collapsed by default
so a low score doesn't auto-render a paid-product offer next to it
b. 'Designed to close the gap your audit just exposed' copy DELETED.
Replaced with neutral 'An independent site-build service offered
by Counsel & Bar. Available to any California-licensed attorney
whether or not they're audited; the directory listing remains
free either way.'
c. Price card adds line 'Site-build service. NOT a directory
placement fee.'
2. 'WHAT THIS SCORE IS AND ISN'T' aside between score and upgrade — Rule
7.1 substantiation block: 'measures twelve technical/presentation
attributes... it is not a measure of legal skill, client outcomes, or
professional reputation.' Plus 'audit is independent of and not
contingent on the upgrade purchase' explicit decoupling.
3. PRICE FONT WEIGHT REDUCED — was 72px metal-glow $499 directly under
96px metal score, reading like 'big number → big number → buy.' Price
now 48px ink-soft. Score remains the dominant numeral on the page.
4. NO-AUDIT FALLBACK COPY rewritten — removed 'we'll run a full audit as
part of the upgrade' (bundles audit into paid product). Now: 'Site
not yet audited. Methodology v1.0 covers twelve published signals.
We'll add this firm to the audit queue if you claim the listing.'
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
50c720e · 2026-05-04 · 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>
95b1ccc · 2026-05-04 · Iter 26: /audit.html — champagne-tier scoring + critique-applied refinements
Replaced traffic-light RGB color logic with champagne-tier palette matching
iter 22's firm-card chips on /find-a-lawyer/results:
Scores:
- tier-a (90+): metal-glow #d4b683
- tier-b (70-89): metal #b89968
- tier-c (50-69): oxidized #a07a40
- tier-d (<50): deeper oxidized #5a4220 (widened from #6e5530 per critique
for better thumbnail-scale legibility)
- tier-x (null): muted ink + italic + smaller scale + serif
Signal pills:
- 'on': champagne-tint background + metal-glow text + champagne hairline
- 'off': transparent + muted ink + 55% opacity + thin strikethrough
(replaces red/green traffic-light pill bgs; strikethrough disambiguates
off-state per critique without screaming 'fail')
- removed ✓/✗ glyphs; pill text says signal name only
Debate-team-fast critique applied:
1. RULE 7.1 LANGUAGE — "marketing health" was clinical-judgment-y next
to real attorney names. Renamed to "site signals" (cards) and the
keystrip masthead. Lower comparative-claim risk.
2. METHODOLOGY VISIBLE — keystrip now shows "Methodology v1.0 · 8 signals
· refreshed weekly" right-aligned. Per-card "Audited [date] ·
methodology v1.0" stamp added above suggestions block. Anchors the
public score to disclosed criteria — biggest 7.1/7.2 risk-reducer
per Kimi.
3. REDUNDANT GRADE LETTER REMOVED — was rendering "100 A+" / "95 A"
alongside numeric (dual-encoding read as report-card, undid the
non-punitive frame). Cards now show numeric only; A/B/C tiers
reserved for future cohort-distribution view where they'd earn it.
4. PILL NAMES NEUTRALIZED — "OG image" → "Social card", "Mobile" →
"Mobile-ready", "H1" → "H1 heading", "Phone shown" → "Phone visible".
Plain attribute language; less jargon, less ambiguous when struck
through.
5. LEGEND BUCKETS — was "80+/60-79/<60" (3 buckets). Now matches actual
tier boundaries: "90+/70-89/50-69/<50" (4 buckets, aligned with
tier-a/b/c/d). Plus "Weak" label for the lowest tier.
6. SCORE FONT 36px → 42px for stronger visual anchor in the column.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85d7704 · 2026-05-04 · upgrade.ts: harden ?_demo=paid gate against Host-header spoofing
Replaced req.hostname check (spoofable when Express has 'trust proxy' or
when reverse proxies forward arbitrary Host headers) with strict
NODE_ENV !== 'production' gate. Local-dev preview still works on dev
machines; production traffic can no longer trigger the paid branch via
crafted Host header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bf9516d · 2026-05-04 · Iter 25: /upgrade/thanks — post-Stripe-success editorial confirmation
Replaced bare 4-line paid-success page with full editorial confirmation:
- Volume strip masthead 'Edition 2026 · Order Confirmed'
- Italic-metal display 'Order received, counselor.'
- Order № receipt strip (3 cols: Order № EZ-YYYY-MMDD-NNNN / Logged
timestamp / Charged $amount italic-metal tabular-nums)
- 'What happens next' 3-cell hairline-ruled grid:
* i. Intake brief — within 24 hours
* ii. First proof — five business days (7-day go-live clock starts here)
* iii. Revisions & go-live — 14-day window
- Footer: refund + buyer-delay carve-out + 'Open dashboard →' quiet CTA
- 'For the record' trust block: §6155 + neutral proximity ranking +
Stripe PCI flow + Rule 7.x buyer-responsibility line
Localhost-only ?_demo=paid flag for visual testing (gated on hostname).
Stripe checkout product description updated: 'live in 7 days' (unconditional)
→ 'typical go-live: 7 business days from content intake' — was emitting on
customer Stripe receipts, fixed at source.
Debate-team-fast critique applied:
1. PROMISE CALIBRATION — defined 'two rounds of substantive revisions'
as 'copy edits, image swaps, layout adjustments within the original
four-page scope. New pages or scope changes are quoted separately.'
Closes the FTC §5 ambiguity around 'substantive.'
2. BUYER-DELAY CARVE-OUT — 'Timeline begins on receipt of your content
brief; delays in returning the brief shift the proof and go-live
dates by the same number of days.' Closes the unconditional-SLA gap.
3. RULE 7.X CLARIFICATION — added to trust block: 'Site copy you supply
or approve remains your professional responsibility under California
Rule of Professional Conduct 7.x — we can flag obvious advertising-
rule risks but do not provide legal review.'
4. YEAR-2 HOSTING — 'invoiced separately at our standard rate' →
'billed separately at our published rate' (rate must be public, not
discretionary).
Pending for next iter: pull payment-method last-4 + Stripe lockup from
PaymentIntent metadata into the receipt strip ('Visa •••• 4242 ·
processed by Stripe') — currently absent, the only element that reads
'demo' rather than 'production' on this surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b93b18b · 2026-05-04 · Iter 24: /upgrade EZ Upgrade page — editorial pass + critique-applied compliance fixes
Visual upgrade:
- CB monogram added to brand header (was bare text)
- Volume strip masthead 'Edition 2026 · EZ Upgrade · One-time $499'
- Italic-metal display 'Reserve your EZ Upgrade.'
- Hairline-underline configurator inputs (no boxes, focus to champagne)
- Sticky order-summary aside with hairline rules + italic-metal '$499'
- Big italic-metal '$499' total + 'INCLUDED' caps for free items
- Scroll-reveal stagger
- Submit-disable JS prevents double-charge during Stripe redirect
Debate-team-fast critique applied (live-Stripe page = high-risk):
1. FALSE-TRUST FIX (highest priority) — replaced literally-inaccurate
"Counsel & Bar never sees your card" with accurate Stripe disclosure:
"Your full card number stays with Stripe; Counsel & Bar receives only
the last four digits and billing details needed for your receipt and
refund processing." Stripe ToS + deceptive-practices issue closed.
2. RULE 5.4 / §6155 BUNDLE FIX — removed "Counsel & Bar listing — premium
tier · INCLUDED" line from $499 order summary (was implying directory
placement is something you pay $499 for). Replaced with explicit
disclaimer: "This is a site-build service, NOT a directory placement
fee. The Counsel & Bar listing remains free for any California-licensed
attorney whether or not they purchase this upgrade."
3. FTC FALSE-ADVERTISING SOFTENED — "Live in seven days" (unconditional)
→ "Typical go-live: seven business days from content intake." Same
for revisions/hosting wording.
4. LRS-FLAVOR REMOVED — "the standard the directory deserves" implied the
directory judges attorneys → "the standard your firm deserves."
5. CTA TONE CORRECTED — brushed-metal gradient (luxury-watch-ad) replaced
with flat-noir + champagne hairline + champagne text + letter-spacing
ease on hover. More restrained, more DB12, less marketing-landing.
6. BUILD AGREEMENT inline disclosure under CTA (no dead /terms link):
"By continuing you agree to the Build Agreement: seven-business-day
typical go-live from content intake, 14-day revision window from
first proof, first-year hosting included, full refund any time
before first proof. Site-build deliverable only."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1982ca0 · 2026-05-03 · Iter 23: /signup/welcome editorial confirmation + critique-applied refinements
New surface — fires after fresh attorney signup, before /dashboard. Pattern
mirrors /find-a-lawyer/thanks: Volume strip + italic-metal display + Listing №
receipt + 3-cell path forward + brushed-metal CTA + trust block.
Shared portal layout CSS gained: .signup-vol/v-rule/v-text/v-meta,
.signup-display/-lede, .receipt/-row + .r-label/-val, .thanks-next/-step/
-grid/-cell/-num/-link, .public-trust/.t-eyebrow, .cta-metal — all duplicated
from leads.ts so /dashboard, /login, and welcome share the masthead pattern.
Routing: POST /signup with no explicit ?next= now redirects to /signup/welcome
instead of straight to /dashboard. Existing ?next= deep-links honored.
Debate-team-fast critique applied immediately:
1. COMPLIANCE — masthead changed "Listing Claimed" → "Listing on File"
(registrar voice, quieter §6155 posture). Added micro-disclaimer ABOVE
headline: "Counsel & Bar is a directory, NOT a lawyer referral service"
(Rule 7.x conspicuousness rule — disclaimer ≥ firm-name claim).
2. COPY — "shows up where consumers look" → "listed alongside other
California-licensed attorneys in proximity-ranked search results."
3. OVERSTATEMENTS REMOVED:
- Cell i: dropped "we cross-check within 24 hours and tag Bar-verified"
(promised non-existent worker) → "calbar.ca.gov remains authoritative"
- Cell ii: dropped "profiles with bios surface above otherwise-equal
cards" (fabricated Rule 7.x ranking-stat) → "every field rendered as
you wrote it. We don't edit, embellish, or rank by paid tier."
- Cell iii: dropped "Nothing curated" (literally false — we do rank +
audit-grade) → "rendered straight from the State Bar record"
4. DEAD ANCHORS FIXED — /dashboard now has <section id="bar"|"profile"
|"preview"> wrappers + scrollIntoView(location.hash) script. The three
"Open dashboard / Edit profile / Preview public listing" links from
/signup/welcome now actually scroll to a target.
5. Profile + Preview placeholder sections added to /dashboard so the
anchors land on real content (not bare empty divs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8cae410 · 2026-05-03 · 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>
4704a6f · 2026-05-03 · Add 7 Express route modules: auth, admin, billing, community, directory, showcase, upgrade
- src/server/auth.ts (144 LOC) — bcrypt session auth, /signup POST + /login POST
- src/server/admin.ts (537 LOC) — admin dashboard, queue review, manual veto on lead matches
- src/server/billing.ts (189 LOC) — Stripe checkout sessions, tier upgrades, webhook handler
- src/server/community.ts (652 LOC) — community board (questions/replies/views), per-tier gating
- src/server/directory.ts (493 LOC) — public firm/attorney detail pages
- src/server/showcase.ts (607 LOC) — mockup template gallery + per-firm rendered showcase
- src/server/upgrade.ts (331 LOC) — EZ Upgrade $499 funnel + post-checkout site provisioning
.gitignore: also exclude tmp/ (45 MB of walker scratch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
03e8b75 · 2026-05-03 · Loop housekeeping: migrations, ingest scripts, compliance docs, build-agent automation
Migrations 003-018: site_audits, app_users (+profile), leads, upgrade_orders,
prof_loc_unique, site_mockups (+extras), data_orders, contact_crawled_at,
user_tiers, ratings, community + replies/views, message_user_fk fix, site_intel.
Ingest:
- calbar_full_scrape.ts (full attorney scrape, bar# 29960→360000, conc=5)
- here_discover.ts (HERE Maps Discover API enricher for phone/website,
free-tier 30k/mo, 8,244 LA firms target)
Enrich:
- discover_websites.ts (web-search firm-website discovery)
- geocode_nominatim.ts (lat/lng for organizations)
- render_mockups.ts (Playwright mockup-template renderer)
- site_audit.ts (HEAD/GET timing + headers + marketing_score)
Scripts:
- build-agent/* (launchd watchdog/progress/nightly/weekly cadence)
- take-shots.mjs, click_through.ts (Playwright walkers)
- create_admin.ts (admin user provisioner)
- probe_calbar.ts (smoke test)
- setup_stripe_*.ts (lawyer-pro price + webhook + tier-webhook)
- run_overnight.ts (overnight pipeline orchestrator)
Docs:
- COMPLIANCE.md (Cal §6155 / Rule 7.x / 5.4 / CCPA standing rules)
- legal/attorney-engagement-letter.md (canonical engagement-letter draft)
- legal/MEETING_FOLLOWUP_2026-05-03.md (codex meeting verification)
- legal/PLATFORM_DECISION_2026-05-03.md (separate-repo vs monorepo decision)
- docs/COMMUNITY_PLATFORM_SPEC.md (PawCircles-style community sub-brand spec)
- CHANGES.md / CHANGES2.md (codex review applied-fix log)
Frontend:
- public/dashboard.html (admin dashboard, will be migrated to portal.ts later)
Build/walker artifacts:
- _clickthrough.mjs (Playwright site walker w/ Steve avatar PiP, video out)
- _shoot_mobile.mjs (375x812 mobile probe for overflow detection)
.gitignore: exclude public/screenshots, public/mockups, public/qa-shots,
public/mockup-templates (6+ GB of generated artifacts), *.webm, *.mp4.
.env.example: placeholder env template (no real keys).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ade90a7 · 2026-05-03 · Counsel & Bar visual loop: 21 iterations of Aston Martin DB12 / Genesis G80 redesign
Surfaces redesigned with cinematic noir + champagne-metal aesthetic:
- Landing (/) — italic Roman watermark, proof strip with real CA firms, scroll-reveal stagger
- /find-a-lawyer — 3-section configurator with outlined italic Romans (i/ii/iii)
- /find-a-lawyer/thanks — Volume strip + Inquiry № receipt with print stylesheet
- /find-a-lawyer/results — proximity-ranked firm cards + reason-branched empty state
- /signup — Volume strip + outlined italic Romans replacing Step 01-04
- /login + /dashboard — matching Volume strip masthead + italic-metal display
- /data — champagne-tier compliance callout (replaced amber-warning aesthetic)
- /audit + /privacy + /terms — monogram + Volume strip + italic-metal display
Brand tokens: --noir #0a0a0c, --ink #f4f1ea, --metal #b89968 (champagne),
brushed-metal CTA gradient, Cormorant Garamond italic + Inter sans pairing.
Compliance: §6155 directory-not-referral language, Rule 7.x no-fabricated-stats,
trust block conditional on populated vs empty results.
A11y: scroll-reveal honors prefers-reduced-motion, :focus-visible champagne ring,
@media print stylesheet on /thanks for receipt printing.
Iter 21 specifically: editorial empty state on /results with reason enum
(no_anchor / no_firms_in_radius / ok / no_zip) — replaces misleading LA-fallback
'near {ZIP}' claim that displayed downtown LA firms for unknown ZIPs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
331776b · 2026-04-30 · Dashboard: add professionals + phones + emails counters; new wiki-lawyers importer
- /api/stats now returns professionals_total, phones_total, emails_total
- Header shows: firms · lawyers · geo-mapped · phones · emails · multi-firm bldgs · cities
- Numbers formatted with thousands separators
- src/ingest/wikipedia_lawyers.ts: ingests Category:Lawyers_from_Los_Angeles
+ Category:California_lawyers (~880 articles). Extracts birth/death year,
education, firm, position from .infobox. Inserts into professionals; if firm
matches an existing organization, also creates professional_locations link.
fd5c251 · 2026-04-30 · Add Wikipedia infobox enricher + firm-website contact crawler
- src/ingest/wikipedia_infobox.ts: pulls Category:Law_firms_based_in_Los_Angeles
+ Category:Law_firms_based_in_California (28 articles), parses .infobox table
for headquarters, founded year, lawyers count, revenue, website. Updates
attorney_count + firm_size_band where missing.
- src/enrich/firm_website_contacts.ts: for the 154 firms with websites,
fetches homepage + likely contact pages (/contact, /about, /our-team, etc.),
regex-extracts emails + phones, inserts into emails/phones tables.
Robots-respected via fetchCompliant. 0.5 rps default.
- docs/blocked_sources.md: documented adjacent-city ArcGIS Hub portals
(Beverly Hills, Santa Monica, Pasadena, Long Beach) — all client-rendered
SPAs with no DCAT feed, blocked without per-portal investigation or Playwright.
c99aeb7 · 2026-04-30 · Add OSM name-match importer + ghost-address cleanup
- src/ingest/osm_name_search.ts: broader Overpass query for any LA-County
named entity matching legal-services vocabulary (law offices, attorneys,
counsel, legal, esquire). Excludes amenity tags that are clearly not firms
(school, restaurant, hospital, etc.). Yielded 0 incremental on first run —
office=lawyer already covers OSM's named LA legal entities. Keeping the
importer for future runs as OSM data grows.
- Cleanup: 23 organizations had degenerate address values ('Los Angeles',
', Los Angeles, CA', etc.) from upstream sources with empty street_address.
NULLed those so they don't pollute multi-firm building detection.
cec3ccf · 2026-04-30 · Add LA City Active Business Licenses importer (NAICS 5411)
7,992 LA City legal-services businesses imported via Socrata API.
Database grew 251 → 8,198 firms. Multi-firm buildings 4 → 585.
Geo-mapped coverage 223 → 7,342.
Real biglaw clusters now visible: 2049 + 2121 + 1801 Century Park East
(Century City), 355 S Grand DTLA, 12100 Wilshire Westwood.
Free, no key required. Filter: NAICS 541100 / 541110 / 541199. Excludes
notaries (541120) and title offices (541191) which aren't lawyers.
Dedup: address_norm + name match, then name + city fallback.
6003ada · 2026-04-30 · Dashboard: Grid + List + Map views with column slider
- Three view modes: Grid (default), List (sortable spreadsheet), Map (preserved Leaflet)
- Top-right toggle to switch views; localStorage remembers last choice
- Grid column slider 3-12, default 5; localStorage remembers last value
- List view: click column headers to sort (firm, city, address, phone, website, size, attys)
- Firm size pills color-coded (biglaw / large / medium / small / solo)
- Map lazy-init on first open (faster initial paint)
- City drill-down filters all three views simultaneously
9315e69 · 2026-04-30 · Initial commit: LA Lawyer Directory — office-mapping pipeline
- Compliance-first PostgreSQL schema (organizations, professionals, professional_locations, bar_admissions, education, emails, phones, sources, raw_records, scrape_jobs, schema_migrations)
- OSM Overpass importer (196 LA County firm offices, geo-tagged)
- Wikidata SPARQL importer (29 LA biglaw firms: Latham, Gibson Dunn, Paul Hastings, Manatt, Lewis Brisbois, etc.)
- Express read API + Leaflet dashboard on :9701
- Robots-aware compliance helper (rate limit, captcha detection, no bypass)
- Overnight orchestrator with checkpointed scrape_jobs
- Documented blocked sources (CA State Bar, CA SOS bizfile) requiring Playwright pass