Lawyer Directory Builder

repo: ~/Projects/lawyer-directory-builder · 37 commits · 0 in last 24h, 0 in last 7d ·

Search the build

37 commits indexed

  1. 40951c7 2026-07-03 chore: macstudio3 migration — reconcile from mac2 + repoint paths (stevestudio2→macstudio3, node/npm/npx→/opt/homebrew)
  2. 650dec4 2026-05-30 fix: add rel=noopener noreferrer to target=_blank links in dashboard; add backup file patterns to .gitignore
  3. 94f1067 2026-05-13 snapshot: 26 file(s) changed, +6 new, ~20 modified
  4. 1e3890c 2026-05-07 tighten .gitignore: add missing standing-rule patterns (build/ .next/)
  5. 2ad78f2 2026-05-04 Iter 45: SEO essentials — robots.txt, sitemap.xml, favicon (4× formats)
  6. 31d0eaf 2026-05-04 Iter 44: OG share image (1200×630) + summary_large_image cards everywhere
  7. ee84e9e 2026-05-04 Iter 43: OG + JSON-LD propagated to /methodology, /audit, /find-a-lawyer
  8. 4ff44cf 2026-05-04 Iter 42: landing — OG + Twitter card + JSON-LD structured data + canonical
  9. a0b97b0 2026-05-04 Iter 40: landing footer — add methodology + State Bar lookup links, fix mislabel
  10. a14af3b 2026-05-04 Iter 39: legal lockdown — three remaining LRS-flavor copy fixes
  11. 3fe9d64 2026-05-04 Iter 38: landing — critical §6155 fixes from cold-eye debate critique
  12. f10bb42 2026-05-04 Iter 37: /data/thanks editorial confirmation matching iter 25 pattern
  13. e6238ca 2026-05-04 Iter 36: /find-a-lawyer/thanks — State Bar deep-link + Rule 7.1 honesty
  14. 7d98dc9 2026-05-04 Iter 35: Lawyer Pro pitch + dashboard FTC copy — three latent compliance fixes
  15. c18ff69 2026-05-04 Iter 34: landing stat ledger — fix misleading §6155 stat + Rule 7.1 substantiation
  16. 9d4bfa7 2026-05-04 Iter 33: walker — added 4 new surfaces from overnight loop
  17. 4041f14 2026-05-04 Iter 32: /privacy + /terms — Volume strip masthead + italic-metal display
  18. 41bda2f 2026-05-04 Iter 31: /404 — path-aware editorial recovery surface
  19. 370aff1 2026-05-04 Iter 30: /signup + /login submit-disable + loading state
  20. c9f2fae 2026-05-04 Iter 29: /methodology page — closes the Rule 7.1 substantiation gap
  21. afcb5d7 2026-05-04 Iter 28: /directory — champagne tiers + monogram + Volume strip + critical compliance decoupling
  22. 50c720e 2026-05-04 Iter 27: /find-a-lawyer submit-disable + loading state + 'curated'→'indexed' compliance fix
  23. 95b1ccc 2026-05-04 Iter 26: /audit.html — champagne-tier scoring + critique-applied refinements
  24. 85d7704 2026-05-04 upgrade.ts: harden ?_demo=paid gate against Host-header spoofing
  25. bf9516d 2026-05-04 Iter 25: /upgrade/thanks — post-Stripe-success editorial confirmation
  26. b93b18b 2026-05-04 Iter 24: /upgrade EZ Upgrade page — editorial pass + critique-applied compliance fixes
  27. 1982ca0 2026-05-03 Iter 23: /signup/welcome editorial confirmation + critique-applied refinements
  28. 8cae410 2026-05-03 Iter 22: enrich populated /results firm cards with audit-grade + size chips
  29. 4704a6f 2026-05-03 Add 7 Express route modules: auth, admin, billing, community, directory, showcase, upgrade
  30. 03e8b75 2026-05-03 Loop housekeeping: migrations, ingest scripts, compliance docs, build-agent automation
  31. ade90a7 2026-05-03 Counsel & Bar visual loop: 21 iterations of Aston Martin DB12 / Genesis G80 redesign
  32. 331776b 2026-04-30 Dashboard: add professionals + phones + emails counters; new wiki-lawyers importer
  33. fd5c251 2026-04-30 Add Wikipedia infobox enricher + firm-website contact crawler
  34. c99aeb7 2026-04-30 Add OSM name-match importer + ghost-address cleanup
  35. cec3ccf 2026-04-30 Add LA City Active Business Licenses importer (NAICS 5411)
  36. 6003ada 2026-04-30 Dashboard: Grid + List + Map views with column slider
  37. 9315e69 2026-04-30 Initial commit: LA Lawyer Directory — office-mapping pipeline

Authors

Agents used

  • debate-team-fast6

Skills used

  • /find-a-lawyer21
  • /thanks15
  • /upgrade14
  • /signup12
  • /audit11
  • /dashboard10
  • /methodology10
  • /directory9
  • /server9
  • /terms7
  • /login6
  • /data6
  • /welcome5
  • /results5
  • /lawyers3
  • /attorney3
  • /privacy3
  • /ingest3
  • /follow2
  • /index2
  • /iii2
  • /enrich2
  • /views2
  • /npm1
  • /npx1
  • /homebrew1
  • /robots1
  • /sitemap1
  • /favicon1
  • /favicon-321

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

File tree

94 files tracked. Click any to browse the source at HEAD.

Other build journals

← Launchd Reminders  ·  all 4 projects  ·  Li Followhub →

Export

commits.csv · feed.atom · project.json · commits.json

rendered in 1ms