- Client-side: Laplacian variance blur detection in photo quality check
(very blurry / somewhat blurry / acceptable / good)
- Server-side: async Ollama vision model validates uploaded image is a
real government ID (minicpm-v:8b), flags non-ID uploads
- Corporate check: sole proprietors now get yellow 'form an LLC' upsell,
formal entities get annual report/RA reminder
Previous approach relied on the main is:inline script block which
could be blocked by FCC step crashes. New approach: tiny self-contained
script right next to the QR img element, runs immediately, fetches
upload token and generates QR. Falls back to page URL on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QR code hidden via CSS on screens < 640px (phones). On mobile,
file input gets capture=environment so tapping the button opens
the camera directly. Tablets still show QR code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- API: POST /api/v1/id-upload/token generates upload token
- API: POST /api/v1/id-upload/:token receives base64 image, stores in MinIO
- API: GET /api/v1/id-upload/:token/status returns upload status + thumbnail
- Mobile page: sends image as base64 with upload_token
- Desktop intake: requests token, generates QR with upload URL, polls
every 3s for phone upload, auto-shows thumbnail when detected
- MinIO storage with presigned URLs for thumbnails
- Compliance order intake_data updated with photo_id_uploaded flag
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QR code displayed inline below the upload button so truckers can
immediately scan with their phone to take a photo of their ID.
Clear instructions: 'Scan with your phone camera to take a photo'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Yellow instruction box explains 3 methods in plain English
(phone photo, computer upload, scanner)
- One big orange "Add Photo of Your ID" button
- Webcam + QR code kept in code but simplified UI
- Accepted formats note
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Desktop users can now use their webcam to photograph their ID:
- Click "Use Camera" → browser requests webcam permission
- Live video preview with orange guide rectangle for ID placement
- Capture button takes high-res JPEG (1280x720)
- Cancel button stops webcam and returns to upload options
- Captured image goes through same quality check flow
- Works on Chrome, Firefox, Edge, Safari (desktop + mobile)
- No libraries needed — native WebRTC API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Intake data now persists to DB after each step completion (non-blocking).
If browser crashes, data is recoverable from compliance_orders.intake_data.
Partial saves (_partial: true) only update intake_data without changing
payment_status or marking intake_data_validated. Final submit still
triggers the full validation + worker dispatch flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows uploaded image at larger size with automated quality checks:
- File size (too small = low quality warning)
- File type validation (JPEG, PNG, PDF, HEIC)
- Resolution check (minimum 400x250 for readable text)
- Aspect ratio check (should look like an ID card)
Green checkmark for passing checks, red X for issues.
Yellow warning box for quality problems with specific guidance.
Accept & Continue button to confirm, Retake to re-upload.
After accept, collapses to small preview with "Change ID" option.
Front of ID only (sufficient for FMCSA MCS-150 filing).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three upload methods:
- Upload File: standard file picker
- Camera / Scanner: uses capture attribute for camera on mobile
or TWAIN/WIA scanner devices on desktop
- QR Code: generates QR with current page URL so user can scan
with phone and take a photo of their ID on mobile
QR generated via api.qrserver.com (no library dependency).
Remove button restores all upload options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FCC step scripts crash on DOT pages due to missing elements. By
using is:inline, DOT intake script runs independently, not in the
hoisted bundle. Stripped TypeScript annotations for plain JS compat.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wraps entire script in element existence check to prevent running
on FCC pages where other step scripts crash from missing elements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MCS150Step script compiled into hoisted JS for all order pages.
Non-null assertions on photo ID elements crashed on non-MCS150 pages,
preventing DOTIntakeStep's showRelevantSections from running.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Script crashed on 'Cannot read properties of null' because photo ID
elements are inside a hidden section. All element refs now use
optional chaining instead of non-null assertions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sections were visible by default in HTML. Now all hidden, then
showRelevantSections() runs immediately + on pw:step-shown.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single DOTIntakeStep shows/hides sections based on services ordered:
- Company info + address + signer (always)
- Entity & operations (MCS-150, USDOT, MC Auth, bundles)
- Fleet info (MCS-150, UCR, bundles)
- UCR fleet bracket + base state (UCR)
- Cargo types (MCS-150, bundles)
- D&A program (CDL drivers, DER, consortium)
- BOC-3 docket info (BOC-3)
- Photo ID upload (MCS-150, MC Auth)
- Security/encryption notices
All DOT services now use ["dot-intake", "review"] instead of ["review"].
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Security notice: SSL encryption, encrypted at rest, no third-party sharing
- EIN field added (required for MCS-150 form field 19)
- Photo ID upload with camera capture on mobile
- ID auto-deleted after filing processed
- Preview with remove button
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Collects all fields needed for FMCSA Form MCS-150:
- Legal name, DBA, DOT#, MC#
- Principal business address
- Entity type, carrier operation, interstate/intrastate
- Fleet info (power units, drivers, annual miles)
- 29 cargo type checkboxes
- Authorized signer name and title
Filed via fax to FMCSA at 202-366-3477 (VitalPBX).
Previously was review-only with no data collection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Footer subscribe modal: new "I'm interested in" dropdown with 3 options
- Hoisted JS: reads interest field, validates selection, passes to API
- Subscribe API: routes to different Listmonk lists by interest
(telecom→list 3, trucking→list 8, formation→list 9)
- Interest stored as subscriber attribute for campaign segmentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8 new Astro intake pages for DOT services:
mcs150-update, boc3-filing, ucr-registration, dot-registration,
mc-authority, dot-drug-alcohol, dot-audit-prep, dot-full-compliance.
All use entity + review wizard steps (admin-assisted services).
Added to INTAKE_MANIFEST and SERVICE_META with correct pricing.
Fixes 404 on /order/mcs150-update?order=CO-xxx from confirmation emails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nav dropdown: Trucking/DOT section with DOT Compliance Services,
MCS-150/BOC-3/UCR link, and DOT Compliance Check [FREE] badge.
Added to both desktop mega-dropdown and mobile menu.
Footer: DOT/Trucking in services list, DOT Compliance Check in
free tools list.
This updates all Astro-compiled pages site-wide (nav partial).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When intake loads from ?order=CO-xxx, the FRN is in the order's
intake_data, not the URL. Now checks state.intake_data.frn and
state.entity.frn as fallback sources for auto-fill.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the intake page is loaded with ?order=CO-xxx (from the
confirmation email), fetch the order and pre-fill customer name,
email, and FRN from the order record. Previously only worked
with JWT token-based links.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- "Email address (yours)" → "Your email address" with helper text
- "Your name" → "Your first and last name" with placeholder
- "Carrier legal name" → "Carrier entity legal name" with examples
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email passes through the full funnel: campaign email (?email=) →
compliance checker → order page. Reduces friction for campaign
recipients who would otherwise have to type their email manually.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Compliance checker reads ?code= from URL, stores it, passes it
through to the order page CTA link
- Allows email campaigns to link to checker with coupon pre-applied
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The lead capture template literal used \` (escaped backtick) which
passed through Astro's compiler literally, creating an invalid JS
token that prevented the entire script from executing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
as HTMLInputElement and as HTMLElement leaked into compiled JS,
causing SyntaxError in the lead capture handler block.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Record<string, string> type annotation leaked into compiled JS output,
causing SyntaxError that silently broke the entire script block.
runCheck() and all event handlers stopped working.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sticky bar fixed to bottom: "N issues found — your filing needs to be
corrected" with "See How to Fix This" + "Call Us" buttons
- Per-filing penalty warnings on red/yellow cards: RMD removal from network,
CPNI $239K/violation, 499-A Red Light blocks all FCC apps, CALEA $10K/day,
CORES can't legally operate
- CTA copy changed: "Your filing is inaccurate" urgency box + "Fix My Filing"
button with "no commitment until you pay" reassurance
- Lead capture form: "Get your compliance report emailed" for people not
ready to buy — creates ticket + tracks lead-capture event in Umami
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hide individual service prices and total/discount row from the
compliance checker results. Users see service names + checkboxes
only. Prices revealed on the order page after clicking Get Started.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Loading message: shows estimated time (30-90 seconds) + rotating status
updates (RMD, CPNI, USAC, BDC, STIR/SHAKEN, compiling report)
- Timeout increased to 90s (was 60s)
- Error messages: "try again in a few minutes" (not "moments" or "check internet")
- New compliance_check_log table: logs every FCC lookup with FRN, entity,
IP, user agent, referrer, issue count, severity, response time
- Enables conversion funnel analysis and follow-up
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Created /js/pw-analytics.js with conversion funnel events
- Added to Base.astro layout (all Astro pages) + 6 static HTML pages
- Events tracked: compliance-check-start, compliance-check-complete,
order-cta-click, checkout-page-view, checkout-start, esign-opened,
esign-submitted, campaign-click (UTM attribution), contact-form-submit
- Server-side payment-complete event from checkout webhook via Umami API
- Auto-tracks any element with data-track="event-name" attribute
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>