- Add MLS state filter for MN/IA only queries
- Add property inquiry form auto-population with read-only display
- Update broker info and office hours in footer
- Remove Bridge Realty text from about page
- Update service area to Minnesota and Iowa
- Add HomeProz listing identification (is_homeproz column)
- Add dynamic featured listings on front page
- Add gallery thumbnail preloading and loading spinners
- Update FEATURES_PENDING with completion status
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add single-property-mls.php template with full gallery support
- Route /properties/?listing=XXX to single property view
- Add HMAC-signed URLs for image endpoint (bot protection)
- Add MySQL advisory lock for image downloads (prevent stampede)
- Add infinite scroll module for property list (desktop map view)
- Load card images immediately on DOM ready (no scroll detection)
- Add cards_only AJAX parameter for infinite scroll
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major changes:
- Property list now updates when map pans/zooms
- Properties sorted by distance from map center (closest first)
- Shows "X properties in view" when viewport filtering active
- Min 30 properties required before grouping kicks in
- Added rule to CLAUDE.md: no commits unless asked
Backend:
- MLS_Query: Added bounds filtering and distance-based sorting
- AJAX handler: Accepts bounds/center, sorts by distance when provided
Frontend:
- Map move triggers property list refresh with same viewport
- Loop prevention flag to avoid map->filter->map recursion
- Resets to page 1 when viewport changes
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Density thresholds now scale with zoom level:
zoom 3: ~600 = high, zoom 7: ~150 = high, zoom 11: ~40 = high
- Warm color palette: green -> lime -> gold -> amber -> burnt orange
- 20% transparency on all dots for softer appearance
- Softer borders and shadows on dots
This makes the same property count appear as "low density" when
zoomed out (seeing 25k properties) but "high density" when zoomed
in (seeing only nearby properties).
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove Leaflet.heat in favor of consistent density dot visualization:
- Zoom 1-5: Density dots with 40% more density (24px spacing)
- Zoom 6-11: Density dots with normal spacing (40px)
- Zoom 12-15: Numbered cluster circles
- Zoom 16+: Individual property markers
Density dots provide clearer visualization than heatmap blobs for
high-density property data.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Zoom 3-7: Heatmap overlay showing property density
- Zoom 8-11: Density dots (small colored circles without numbers)
- Zoom 12-15: Numbered cluster circles
- Zoom 16+: Individual property markers
Backend returns different data types (heatmap/density/clusters/markers)
based on zoom level. Frontend uses Leaflet.heat for heatmap and custom
divIcons for density dots with color gradient based on count.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace geohash precision mapping with dynamic grid sizing based on
target pixel spacing (60px between cluster centers). Uses Leaflet/OSM
tile math to calculate degrees-per-pixel at each zoom level, adjusted
for Mercator projection at the viewport's center latitude.
At zoom 7, this gives ~52km cells and ~150 clusters statewide,
properly separating Minneapolis from St. Cloud.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The formData object was getting paged added, which then got included
in the URL query params by updateUrl(). Now paged is only sent in
the AJAX POST data, not added to formData.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update paginate_links() in both property-results.php and ajax-handlers.php
to generate #page=N links instead of ?paged=N query parameters.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevents conflicts with WordPress server-side pagination handling.
URLs now use #page=2 format instead of ?paged=2.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove 1000 property limit from count display
- Add MLS_Cluster class for geohash-based server-side clustering
- Add AJAX endpoint for dynamic cluster loading based on viewport/zoom
- Update property-results.php and ajax-handlers.php to use efficient counting
- Update map JavaScript to fetch clusters dynamically as user pans/zooms
- Server returns clusters at low zoom, individual markers at high zoom
- Fixes property count showing 1000 instead of actual ~30k properties
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Show spinning loader while images load
- Lazy load images as cards enter viewport (with 200px buffer)
- Use data-bg attribute to defer background-image loading
- MutationObserver detects AJAX-loaded content
- Spinner hides when image loads or on error
- Fallback to placeholder on load error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
MLS Grid media URLs expire after ~24 hours. Instead of running
scheduled syncs, this adds on-demand refresh when images are requested:
- Add is_url_expired() to parse expires timestamp from media URLs
- Add refresh_media_urls() to fetch fresh URLs from API for one listing
- Add get_property_media() API method using ListingId filter
- Image endpoint checks URL expiration before fetching
- If expired, refreshes URLs from API then proceeds with fetch
This is more efficient than scheduled full syncs because:
- Only refreshes URLs for listings actually being viewed
- Zero overhead for unviewed listings
- Scales naturally with traffic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create MLS_Image_Endpoint class with on-demand thumbnail generation
- Use ImageMagick to convert images to WebP format
- Thumbnail sizes: 800px (thumb) and 1800px (full), maintain aspect ratio
- Only downsize images, never upsize
- Cache thumbnails in wp-content/uploads/mls-thumbnails/
- Add mls_get_image_url() helper function (1-based index)
- Update property cards to display thumbnail as background-cover image
- Long cache headers (1 year) with ETag support
URL format: /mls-image/{listing_key}/{index}/{size}/
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major changes to sync strategy following MLS Grid best practices:
- Initial sync now fetches only Active/Pending properties (~30K vs 1.3M)
- Replication (incremental) fetches all changes, deletes non-Active/Pending
- On-demand media fetching replaces background queue (avoids rate limits)
- Media downloaded and cached when first viewed, not during sync
- Updated CLI commands: wp mls media status/fetch/clear
- Comprehensive documentation with troubleshooting guide
This fixes the "Value out of range" API error caused by high $skip values.
Co-Authored-By: Claude <noreply@anthropic.com>
- Add download_status, retry_after, queued_at columns to mls_media table
- Add mls_media_log table for download attempt tracking
- Rewrite media handler to queue downloads instead of immediate download
- Add 700ms delay between downloads (25% buffer over 2/sec limit)
- Add 3-hour backoff for rate-limited (429) responses
- Add max 5 attempts before marking as permanently failed
- Add wp mls media command: status, process, reset, logs
- Deprecate wp mls sync media in favor of wp mls media process
- Update documentation with queue system details and cron examples
Media downloads are now separate from property sync:
1. wp mls sync full/incremental - syncs properties, queues media
2. wp mls media process - downloads queued media with rate limiting
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add wp mls recovery list to show resumable syncs
- Add wp mls recovery auto to auto-resume most recent failed sync
- Add wp mls recovery cleanup to mark stale syncs (>1hr) as failed
- Track last_next_link during incremental sync pagination
- Add get_resumable_syncs(), cleanup_stale_syncs(), auto_resume() methods
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add --verbose flag to sync commands for detailed API request/response output
- Add progress indicators (.=#xPpE|) for compact sync output
- Implement exponential backoff (1s, 2s, 4s, 8s, 16s) for media downloads
- Log failed media downloads to wp-content/uploads/mls-missing-media.log
- Add 'wp mls cache missing' command to view/clear the log
- Retry on rate limit (429) and server errors (5xx)
- Update documentation with new features
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Features:
- Full sync of NorthStar MLS properties via MLS Grid API v2
- Incremental sync using ModificationTimestamp
- Local media download and storage
- Rate limit compliance (2 req/sec, 7200/hr, 40000/day)
- Sync state tracking with resume capability
- WP-CLI commands: test, sync, status, stats, cache
- Admin settings page with manual sync triggers
- Public API functions: mls_get_properties, mls_get_property, etc.
Database tables:
- mls_properties: Listing data with full field mapping
- mls_media: Downloaded images
- mls_sync_state: Sync progress tracking
- mls_rate_limits: API usage tracking
- mls_sync_log: Debug logging
Documentation:
- docs/CLAUDE.md: AI development guide
- docs/API.md: MLS Grid API reference
- docs/USAGE.md: User documentation
Tested: Connection, auth, sync 10 records, media download verified
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove completed decisions, outdated planning details, and verbose
documentation. Keep only essential rules and reference info.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Gallery thumbnails: constrain container to 88vw on mobile to prevent overflow
- Property specs: show as compact two-column list in single card on mobile,
keep boxed grid layout on desktop
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed label to flex column layout and hide br tags that CF7
inserts between label text and input fields.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adjusted negative margins so the clickable link area stays within
proper bounds and doesn't overlap the spacing below agent-info.
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added social icon links before phone button in header CTA area
- Icons styled with border, hover effects matching site aesthetic
- Only visible on desktop (>= 1024px) alongside phone button
- Uses theme options for URLs (same as footer)
Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed select name from 'location' to 'property_location' to match
the property archive filter parameter.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add homeproz_get_active_locations() helper function
- Only shows locations that have properties with Active or Pending status
- Applied to homepage community dropdown and properties page location filter
- Sold properties no longer count toward location visibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Hero changes:
- Remove top padding from .site-main (was causing inconsistent gaps)
- Remove margin-bottom and border from archive-hero
- Add padding-top to property archive container
- Change resources section background to --color-bg-dark
Service cards:
- Replace "Find a Rental" with "Resources & Guides"
- Reorder: Find a Home, Sell Your Home, Resources & Guides
- Update subtitle to remove rental reference
- Add book icon for resources card
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- No margin below hero on mobile/tablet (< 1200px)
- 2rem margin below hero on desktop (>= 1200px)
- Consistent hero styling across all interior pages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Currency inputs auto-format with thousand separators on input
- Only allows numeric characters (strips letters, symbols, etc.)
- .val() returns raw integer for calculations
- Percentage inputs allow only numbers and one decimal point
- Select all on focus for easy replacement
- Maintains cursor position while typing
- Real-time sync between down payment dollar and percent fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated global form styles in main.scss and mortgage calculator inputs
to use #000 background instead of color variables.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Two-column layout matching property details page pattern
- Real-time mortgage calculation with principal, interest, loan amount, and total interest
- Input formatting with currency/percentage display
- Down payment syncs between dollar amount and percentage
- Sidebar with quick tips, help contact widget, and related resources
- "How to Use" and "Keep in Mind" sections with practical guidance
- Widget titles use Times New Roman 18px bold to match existing styles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Property details page:
- Move address to header above gallery
- Add property type badges (blue residential, red commercial)
- Gallery autoplay with play/pause button, 5-second interval
- Fade transitions for autoplay, slide transitions for swipe
- Thumbnail navigation with sync
- Swipe support in gallery and lightbox
- Widget titles: 18px Times New Roman bold
- Remove breadcrumbs
Layout and styling:
- Container width: 1400px
- Contact page map 50% taller (375px)
- Contact info labels: Times New Roman 16px
- Agent photo backgrounds solid black
- CTA accent button hover: black text
Clickable components:
- Service cards fully clickable with stretched links
- Resource cards fully clickable with stretched links
- Addresses link to Google Maps (contact page, footer)
Footer updates:
- Add Send Us a Message link with paper airplane icon
- Replace credentials with legal section
- Privacy Policy, Fair Housing, MLS Disclaimer, Brokerage Disclosure links
- Credits: Web Design by HansonXyz
Other:
- Install Classic Editor plugin
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enhance archive-hero to support optional background images
- Add Page Hero ACF fields (title, subtitle, background) for all pages
- Add Properties Page hero settings in Theme Options
- Update all page templates to use consistent archive-hero style
- Resource pages now use archive-hero with featured image fallback
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add 5 new hero images to assets/images/hero-gallery/
- Create hero-section.js with image rotation (6s interval)
- Only preload/animate at >= 1450px to save mobile bandwidth
- Fade transition with slight grow effect
- Add overflow:hidden to prevent scrollbar during transition
- Images use 30% right crop via transform system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Matches service cards styling:
- Dark background (--color-bg-dark)
- Red accent border (--color-accent)
- Rounded corners with overflow hidden
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Doubled logo max-width from 200px to 400px
- Created logo-transparent.webp with transparent background
- Used flood fill to preserve internal black elements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added logo parameter to hero-section.php component
- Added .hero-section-logo CSS (200px max-width, centered)
- Updated front-page.php to pass logo.webp to hero
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use logo.webp from assets/images
- Logo height: 50px
- Negative margin (-4px) to fit within existing header height
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove status dropdown (always show all properties)
- Remove sort dropdown (use status-based sorting)
- Sort order: Active > Pending > Sold, then by modified date
- Map view: half height, 2-column property grid
- Beds field same width as others
- Add CLAUDE.md documentation for property system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- All filters now on one row at 1200px+
- Beds field narrower (70px) for single-digit values
- Price range wider with compact $Xk format
- Sort, Search, Reset moved inline after price range
- Shorter labels: Type, Status, Beds, Sort
- Responsive: 2-col mobile, 4-col tablet, full row desktop
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>