Add homepage section ordering and switch to MLS-only data source

Homepage Improvements:
- Add Section Order tab with enable/disable toggles and order numbers
- Support up to 2 custom WYSIWYG content areas with titles
- All 4 sections (Service Cards, Property Types, Custom 1, Custom 2)
  can be reordered and toggled independently

Property Type Boxes:
- Convert to ACF repeater (remove MLS count queries)
- Add red accent color to icons
- Center-align icons, increase title size, add spacing

MLS Data Source:
- Remove property CPT and taxonomies (MLS Editor is now source of truth)
- Commercial section now pulls from MLS with priority:
  1. Featured (favorited) commercial properties
  2. HomeProz-listed commercial properties
  3. Random commercial/land/farm properties
- Featured Homes section updated to same priority order

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Hanson.xyz Dev
2025-12-30 13:00:33 -06:00
parent cf56d57225
commit 361007d36b
11 changed files with 1485 additions and 448 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+163 -58
View File
@@ -12,42 +12,49 @@ if (!defined('ABSPATH')) {
get_header();
// Get homepage ACF fields (with fallbacks)
$hero_title = get_field('home_hero_title') ?: 'Find Your Dream Home Today';
$hero_subtitle = get_field('home_hero_subtitle') ?: 'Expert real estate services for buyers and sellers in Albert Lea and the surrounding Minnesota communities.';
$hero_primary_cta_text = get_field('home_hero_primary_cta_text') ?: 'View All Properties';
$hero_primary_cta_url = get_field('home_hero_primary_cta_url') ?: home_url('/properties/');
$hero_secondary_cta_text = get_field('home_hero_secondary_cta_text') ?: 'Contact Us';
$hero_secondary_cta_url = get_field('home_hero_secondary_cta_url') ?: home_url('/contact/');
$featured_homes_title = get_field('home_featured_homes_title') ?: 'Featured Homes';
$featured_homes_subtitle = get_field('home_featured_homes_subtitle') ?: 'Browse our residential properties for sale';
$commercial_title = get_field('home_commercial_title') ?: 'Commercial & Land';
$commercial_subtitle = get_field('home_commercial_subtitle') ?: 'Explore commercial properties and land for sale';
$cta_title = get_field('home_cta_title') ?: 'Ready to Find Your Dream Home?';
$cta_text = get_field('home_cta_text') ?: 'Whether you\'re buying or selling, our team is here to help you every step of the way.';
$cta_button_text = get_field('home_cta_button_text') ?: 'Contact Us Today';
$cta_button_url = get_field('home_cta_button_url') ?: home_url('/contact/');
// Get featured MLS listings for JSON data
$featured_mls_listings = homeproz_get_featured_mls_listings(10); // Get more than needed for random selection
// Get featured commercial/land properties from WordPress (3 most recent active commercial or land listings)
$featured_commercial = new WP_Query(array(
'post_type' => 'property',
'posts_per_page' => 3,
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'property_status',
'field' => 'slug',
'terms' => 'active',
),
array(
'taxonomy' => 'property_type',
'field' => 'slug',
'terms' => array('commercial', 'land'),
),
),
'orderby' => 'date',
'order' => 'DESC',
));
// Get featured commercial/land properties from MLS data
$featured_commercial_listings = homeproz_get_featured_commercial_listings(3);
?>
<main id="primary" class="site-main homepage-main">
<?php
// Hero images
$hero_image_split = homeproz_get_transformed_theme_image('assets/images/hero-original.png', 30, 2560, 90);
$hero_image_card = homeproz_get_transformed_theme_image('assets/images/hero-original.png', 35, 2560, 90);
$fallback_image = get_template_directory_uri() . '/assets/images/hero.webp';
$logo_cropped = get_template_directory_uri() . '/assets/images/logo-cropped.webp';
// Build gallery images array for desktop hero rotation
// Include the original hero plus the 5 additional gallery images
// Check for ACF gallery first, otherwise use default theme images
$acf_gallery = get_field('home_hero_gallery');
$use_acf_gallery = $acf_gallery && is_array($acf_gallery) && !empty($acf_gallery);
$gallery_images = array();
if ($use_acf_gallery) {
// Use ACF gallery images (already URLs from return_format)
$gallery_images = $acf_gallery;
} else {
// Fallback to default theme images
$gallery_source_images = array(
'assets/images/hero-original.png',
'assets/images/hero-gallery/hero-2.png',
@@ -57,13 +64,16 @@ $featured_commercial = new WP_Query(array(
'assets/images/hero-gallery/hero-6.png',
);
$gallery_images = array();
foreach ($gallery_source_images as $source) {
$transformed = homeproz_get_transformed_theme_image($source, 30, 2560, 90);
if ($transformed) {
$gallery_images[] = $transformed;
}
}
}
// Use first gallery image as initial background (whether from ACF or defaults)
$initial_background = !empty($gallery_images) ? $gallery_images[0] : $fallback_image;
// Design 2: Two-column split (shown >= 1450px)
?>
@@ -71,15 +81,15 @@ $featured_commercial = new WP_Query(array(
<?php
get_template_part('template-parts/components/hero-section', null, array(
'logo' => $logo_cropped,
'title' => 'Find Your Dream Home Today',
'subtitle' => 'Expert real estate services for buyers and sellers in Albert Lea and the surrounding Minnesota communities.',
'title' => $hero_title,
'subtitle' => $hero_subtitle,
'show_location_search' => true,
'primary_cta_text' => 'View All Properties',
'primary_cta_url' => home_url('/properties/'),
'primary_cta_text' => $hero_primary_cta_text,
'primary_cta_url' => $hero_primary_cta_url,
'primary_cta_icon' => 'map',
'secondary_cta_text' => 'Contact Us',
'secondary_cta_url' => home_url('/contact/'),
'background_image' => $hero_image_split ?: $fallback_image,
'secondary_cta_text' => $hero_secondary_cta_text,
'secondary_cta_url' => $hero_secondary_cta_url,
'background_image' => $initial_background,
'gallery_images' => $gallery_images,
'size' => 'large',
));
@@ -93,34 +103,97 @@ $featured_commercial = new WP_Query(array(
<?php
get_template_part('template-parts/components/hero-section-card', null, array(
'logo' => $logo_cropped,
'title' => 'Find Your Dream Home Today',
'subtitle' => 'Expert real estate services for buyers and sellers in Albert Lea and the surrounding Minnesota communities.',
'title' => $hero_title,
'subtitle' => $hero_subtitle,
'show_location_search' => true,
'primary_cta_text' => 'View All Properties',
'primary_cta_url' => home_url('/properties/'),
'primary_cta_text' => $hero_primary_cta_text,
'primary_cta_url' => $hero_primary_cta_url,
'primary_cta_icon' => 'map',
'secondary_cta_text' => 'Contact Us',
'secondary_cta_url' => home_url('/contact/'),
'background_image' => $hero_image_card ?: $fallback_image,
'secondary_cta_text' => $hero_secondary_cta_text,
'secondary_cta_url' => $hero_secondary_cta_url,
'background_image' => $initial_background,
'size' => 'large',
));
?>
</div>
<?php
// Service Cards Section (Buy/Rent/Sell)
get_template_part('template-parts/components/service-cards');
// Build ordered sections array
$homepage_sections = array();
// Property Type Showcase Boxes
// Service Cards Section
$service_cards_enabled = get_field('home_service_cards_enabled');
if ($service_cards_enabled === null) $service_cards_enabled = true; // Default to enabled
if ($service_cards_enabled) {
$homepage_sections[] = array(
'type' => 'service_cards',
'order' => get_field('home_service_cards_order') ?: 1,
);
}
// Property Types Section
$property_types_enabled = get_field('home_property_types_enabled');
if ($property_types_enabled === null) $property_types_enabled = true; // Default to enabled
if ($property_types_enabled) {
$homepage_sections[] = array(
'type' => 'property_types',
'order' => get_field('home_property_types_order') ?: 2,
);
}
// Custom Content 1
$custom_1_enabled = get_field('home_custom_1_enabled');
if ($custom_1_enabled) {
$homepage_sections[] = array(
'type' => 'custom_1',
'order' => get_field('home_custom_1_order') ?: 3,
'title' => get_field('home_custom_1_title'),
'content' => get_field('home_custom_1_content'),
);
}
// Custom Content 2
$custom_2_enabled = get_field('home_custom_2_enabled');
if ($custom_2_enabled) {
$homepage_sections[] = array(
'type' => 'custom_2',
'order' => get_field('home_custom_2_order') ?: 4,
'title' => get_field('home_custom_2_title'),
'content' => get_field('home_custom_2_content'),
);
}
// Sort sections by order
usort($homepage_sections, function($a, $b) {
return $a['order'] - $b['order'];
});
// Render sections in order
foreach ($homepage_sections as $section) {
switch ($section['type']) {
case 'service_cards':
get_template_part('template-parts/components/service-cards');
break;
case 'property_types':
get_template_part('template-parts/components/property-type-boxes');
break;
case 'custom_1':
case 'custom_2':
get_template_part('template-parts/components/custom-content-section', null, array(
'title' => $section['title'],
'content' => $section['content'],
));
break;
}
}
?>
<!-- Featured Homes Section (MLS Listings) -->
<section class="featured-properties-section">
<div class="container">
<header class="section-header">
<h2 class="section-title">Featured Homes</h2>
<p class="section-subtitle">Browse our residential properties for sale</p>
<h2 class="section-title"><?php echo esc_html($featured_homes_title); ?></h2>
<p class="section-subtitle"><?php echo esc_html($featured_homes_subtitle); ?></p>
</header>
<div id="featured-listings-grid" class="property-grid property-grid--3col">
@@ -151,23 +224,55 @@ $featured_commercial = new WP_Query(array(
<section class="featured-properties-section featured-properties-section--alt">
<div class="container">
<header class="section-header">
<h2 class="section-title">Commercial & Land</h2>
<p class="section-subtitle">Explore commercial properties and land for sale</p>
<h2 class="section-title"><?php echo esc_html($commercial_title); ?></h2>
<p class="section-subtitle"><?php echo esc_html($commercial_subtitle); ?></p>
</header>
<?php if ($featured_commercial->have_posts()) : ?>
<?php if (!empty($featured_commercial_listings)) : ?>
<div class="property-grid property-grid--3col">
<?php
while ($featured_commercial->have_posts()) :
$featured_commercial->the_post();
get_template_part('template-parts/property/property-card');
endwhile;
wp_reset_postdata();
?>
<?php foreach ($featured_commercial_listings as $listing) : ?>
<article class="property-card card">
<a href="<?php echo esc_url($listing['url']); ?>" class="property-card-link-overlay" aria-hidden="true" tabindex="-1"></a>
<div class="property-card-image">
<?php if (!empty($listing['image_url'])) : ?>
<img src="<?php echo esc_url($listing['image_url']); ?>" alt="<?php echo esc_attr($listing['address']); ?>" loading="lazy">
<?php else : ?>
<div class="property-card-placeholder">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
</div>
<?php endif; ?>
<span class="property-card-badge badge badge-active">Active</span>
</div>
<div class="property-card-content">
<div class="property-card-price"><?php echo esc_html($listing['price_formatted']); ?></div>
<h3 class="property-card-title"><?php echo esc_html($listing['address']); ?></h3>
<?php if ($listing['sqft'] > 0) : ?>
<ul class="property-card-specs">
<li class="spec-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18M9 3v18"/>
</svg>
<span><?php echo esc_html(number_format($listing['sqft'])); ?> sqft</span>
</li>
</ul>
<?php endif; ?>
<span class="property-card-link">
View Details
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</span>
</div>
</article>
<?php endforeach; ?>
</div>
<div class="section-footer">
<a href="<?php echo esc_url(home_url('/properties/?type=commercial,land')); ?>" class="btn btn-secondary">
<a href="<?php echo esc_url(home_url('/properties/?property_type=Commercial+Sale,Land,Farm')); ?>" class="btn btn-secondary">
View All Commercial
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
@@ -183,10 +288,10 @@ $featured_commercial = new WP_Query(array(
<?php
// Contact CTA Section
get_template_part('template-parts/components/cta-section', null, array(
'title' => 'Ready to Find Your Dream Home?',
'text' => 'Whether you\'re buying or selling, our team is here to help you every step of the way.',
'button_text' => 'Contact Us Today',
'button_url' => home_url('/contact/'),
'title' => $cta_title,
'text' => $cta_text,
'button_text' => $cta_button_text,
'button_url' => $cta_button_url,
'style' => 'accent',
));
?>
+724 -11
View File
@@ -443,6 +443,51 @@ function homeproz_register_acf_fields() {
'rows' => 2,
'default_value' => 'Your trusted partner for buying and selling homes in Albert Lea, Minnesota and surrounding areas.',
),
array(
'key' => 'field_theme_legal_links',
'label' => 'Legal Links',
'name' => 'theme_legal_links',
'type' => 'repeater',
'instructions' => 'Links displayed in footer legal section (Privacy Policy, Fair Housing, etc.)',
'min' => 0,
'max' => 10,
'layout' => 'table',
'button_label' => 'Add Legal Link',
'sub_fields' => array(
array(
'key' => 'field_legal_link_label',
'label' => 'Label',
'name' => 'label',
'type' => 'text',
'required' => 1,
'wrapper' => array('width' => '30'),
),
array(
'key' => 'field_legal_link_url',
'label' => 'URL',
'name' => 'url',
'type' => 'url',
'required' => 1,
'wrapper' => array('width' => '40'),
),
array(
'key' => 'field_legal_link_icon',
'label' => 'Icon',
'name' => 'icon',
'type' => 'select',
'choices' => array(
'shield' => 'Shield (Privacy)',
'home' => 'Home (Fair Housing)',
'document' => 'Document (Disclaimer)',
'clipboard' => 'Clipboard (Disclosure)',
'info' => 'Info Circle',
'none' => 'No Icon',
),
'default_value' => 'document',
'wrapper' => array('width' => '30'),
),
),
),
// Office Hours
array(
@@ -518,6 +563,68 @@ function homeproz_register_acf_fields() {
'instructions' => 'Paste the full iframe embed code from Google Maps. Get this from maps.google.com > Share > Embed a map.',
'rows' => 4,
),
// 404 Page Tab
array(
'key' => 'field_theme_tab_404',
'label' => '404 Page',
'name' => '',
'type' => 'tab',
'placement' => 'left',
),
array(
'key' => 'field_theme_404_title',
'label' => '404 Title',
'name' => 'theme_404_title',
'type' => 'text',
'default_value' => '404',
),
array(
'key' => 'field_theme_404_subtitle',
'label' => '404 Subtitle',
'name' => 'theme_404_subtitle',
'type' => 'text',
'default_value' => 'Page Not Found',
),
array(
'key' => 'field_theme_404_message',
'label' => '404 Message',
'name' => 'theme_404_message',
'type' => 'textarea',
'rows' => 2,
'default_value' => 'The page you\'re looking for doesn\'t exist or has been moved.',
),
array(
'key' => 'field_theme_404_primary_button',
'label' => 'Primary Button Text',
'name' => 'theme_404_primary_button',
'type' => 'text',
'default_value' => 'Go Home',
),
array(
'key' => 'field_theme_404_secondary_button',
'label' => 'Secondary Button Text',
'name' => 'theme_404_secondary_button',
'type' => 'text',
'default_value' => 'View Properties',
),
// Footer Copyright Tab
array(
'key' => 'field_theme_tab_copyright',
'label' => 'Footer Copyright',
'name' => '',
'type' => 'tab',
'placement' => 'left',
),
array(
'key' => 'field_theme_footer_copyright',
'label' => 'Copyright Text',
'name' => 'theme_footer_copyright',
'type' => 'text',
'instructions' => 'Copyright text shown in footer. Year is added automatically.',
'default_value' => 'HomeProz Real Estate LLC. All rights reserved.',
),
),
'location' => array(
array(
@@ -567,6 +674,17 @@ function homeproz_register_acf_fields() {
'library' => 'all',
'mime_types' => 'jpg, jpeg, png, webp',
),
array(
'key' => 'field_page_enable_title_animations',
'label' => 'Enable Section Title Animations',
'name' => 'enable_title_animations',
'type' => 'true_false',
'instructions' => 'Enable subtle hover animations on section titles (letter-spacing expansion).',
'default_value' => 1,
'ui' => 1,
'ui_on_text' => 'Enabled',
'ui_off_text' => 'Disabled',
),
),
'location' => array(
// All pages except front page
@@ -697,13 +815,21 @@ function homeproz_register_acf_fields() {
'type' => 'select',
'choices' => array(
'home' => 'Home',
'key' => 'Key',
'sell' => 'For Sale Sign',
'book' => 'Book/Guide',
'dollar-sign' => 'Dollar Sign',
'users' => 'Users/Team',
'map-pin' => 'Map Pin',
'key' => 'Key',
'building' => 'Building',
'search' => 'Search',
'handshake' => 'Handshake',
'phone' => 'Phone',
'mail' => 'Email',
'star' => 'Star',
'shield' => 'Shield',
'clipboard' => 'Clipboard',
'document' => 'Document',
),
'default_value' => 'home',
),
@@ -837,6 +963,253 @@ function homeproz_register_acf_fields() {
'rows' => 2,
'default_value' => 'Find the perfect property for your needs',
),
array(
'key' => 'field_home_property_type_boxes',
'label' => 'Property Type Boxes',
'name' => 'home_property_type_boxes',
'type' => 'repeater',
'instructions' => 'Configure the property type boxes displayed in this section',
'min' => 1,
'max' => 8,
'layout' => 'block',
'button_label' => 'Add Property Type',
'sub_fields' => array(
array(
'key' => 'field_property_type_box_title',
'label' => 'Title',
'name' => 'title',
'type' => 'text',
'required' => 1,
'wrapper' => array('width' => '30'),
),
array(
'key' => 'field_property_type_box_description',
'label' => 'Description',
'name' => 'description',
'type' => 'text',
'required' => 1,
'wrapper' => array('width' => '40'),
),
array(
'key' => 'field_property_type_box_url',
'label' => 'Link URL',
'name' => 'url',
'type' => 'url',
'required' => 1,
'wrapper' => array('width' => '30'),
),
array(
'key' => 'field_property_type_box_icon',
'label' => 'Icon',
'name' => 'icon',
'type' => 'select',
'choices' => array(
'home' => 'Home (Residential)',
'land' => 'Land/Mountains',
'building' => 'Building (Commercial)',
'farm' => 'Farm/Barn',
'multi-family' => 'Multi-Family',
'key' => 'Key',
'map-pin' => 'Map Pin',
'dollar-sign' => 'Dollar Sign',
),
'default_value' => 'home',
'wrapper' => array('width' => '30'),
),
),
),
// Hero Gallery Tab
array(
'key' => 'field_home_tab_gallery',
'label' => 'Hero Gallery',
'name' => '',
'type' => 'tab',
'placement' => 'left',
),
array(
'key' => 'field_home_hero_gallery',
'label' => 'Hero Gallery Images',
'name' => 'home_hero_gallery',
'type' => 'gallery',
'instructions' => 'Upload images for the rotating hero background. If empty, uses default images from theme.',
'min' => 0,
'max' => 10,
'return_format' => 'url',
'preview_size' => 'medium',
'library' => 'all',
'mime_types' => 'jpg, jpeg, png, webp',
),
// Section Order Tab
array(
'key' => 'field_home_tab_section_order',
'label' => 'Section Order',
'name' => '',
'type' => 'tab',
'placement' => 'left',
),
array(
'key' => 'field_home_section_order_message',
'label' => '',
'name' => '',
'type' => 'message',
'message' => 'Configure which sections appear on the homepage and in what order. Lower order numbers appear first.',
),
// Service Cards Section
array(
'key' => 'field_home_service_cards_enabled',
'label' => 'Service Cards Section',
'name' => 'home_service_cards_enabled',
'type' => 'true_false',
'instructions' => 'Show the "Go with the Proz" service cards section',
'default_value' => 1,
'ui' => 1,
'wrapper' => array('width' => '70'),
),
array(
'key' => 'field_home_service_cards_order',
'label' => 'Order',
'name' => 'home_service_cards_order',
'type' => 'number',
'default_value' => 1,
'min' => 1,
'max' => 10,
'wrapper' => array('width' => '30'),
),
// Property Types Section
array(
'key' => 'field_home_property_types_enabled',
'label' => 'Property Types Section',
'name' => 'home_property_types_enabled',
'type' => 'true_false',
'instructions' => 'Show the "Browse by Property Type" boxes section',
'default_value' => 1,
'ui' => 1,
'wrapper' => array('width' => '70'),
),
array(
'key' => 'field_home_property_types_order',
'label' => 'Order',
'name' => 'home_property_types_order',
'type' => 'number',
'default_value' => 2,
'min' => 1,
'max' => 10,
'wrapper' => array('width' => '30'),
),
// Custom Content 1
array(
'key' => 'field_home_custom_1_enabled',
'label' => 'Custom Content Area 1',
'name' => 'home_custom_1_enabled',
'type' => 'true_false',
'instructions' => 'Show custom content area 1',
'default_value' => 0,
'ui' => 1,
'wrapper' => array('width' => '70'),
),
array(
'key' => 'field_home_custom_1_order',
'label' => 'Order',
'name' => 'home_custom_1_order',
'type' => 'number',
'default_value' => 3,
'min' => 1,
'max' => 10,
'wrapper' => array('width' => '30'),
),
array(
'key' => 'field_home_custom_1_title',
'label' => 'Title',
'name' => 'home_custom_1_title',
'type' => 'text',
'conditional_logic' => array(
array(
array(
'field' => 'field_home_custom_1_enabled',
'operator' => '==',
'value' => '1',
),
),
),
),
array(
'key' => 'field_home_custom_1_content',
'label' => 'Content',
'name' => 'home_custom_1_content',
'type' => 'wysiwyg',
'tabs' => 'all',
'toolbar' => 'full',
'media_upload' => 1,
'conditional_logic' => array(
array(
array(
'field' => 'field_home_custom_1_enabled',
'operator' => '==',
'value' => '1',
),
),
),
),
// Custom Content 2
array(
'key' => 'field_home_custom_2_enabled',
'label' => 'Custom Content Area 2',
'name' => 'home_custom_2_enabled',
'type' => 'true_false',
'instructions' => 'Show custom content area 2',
'default_value' => 0,
'ui' => 1,
'wrapper' => array('width' => '70'),
),
array(
'key' => 'field_home_custom_2_order',
'label' => 'Order',
'name' => 'home_custom_2_order',
'type' => 'number',
'default_value' => 4,
'min' => 1,
'max' => 10,
'wrapper' => array('width' => '30'),
),
array(
'key' => 'field_home_custom_2_title',
'label' => 'Title',
'name' => 'home_custom_2_title',
'type' => 'text',
'conditional_logic' => array(
array(
array(
'field' => 'field_home_custom_2_enabled',
'operator' => '==',
'value' => '1',
),
),
),
),
array(
'key' => 'field_home_custom_2_content',
'label' => 'Content',
'name' => 'home_custom_2_content',
'type' => 'wysiwyg',
'tabs' => 'all',
'toolbar' => 'full',
'media_upload' => 1,
'conditional_logic' => array(
array(
array(
'field' => 'field_home_custom_2_enabled',
'operator' => '==',
'value' => '1',
),
),
),
),
),
'location' => array(
array(
@@ -910,6 +1283,15 @@ function homeproz_register_acf_fields() {
'type' => 'text',
'instructions' => 'NorthstarMLS agent ID (e.g., NST503517068)',
),
array(
'key' => 'field_agent_credentials',
'label' => 'Professional Credentials',
'name' => 'agent_credentials',
'type' => 'textarea',
'instructions' => 'Licenses, certifications, areas served, or other professional details. Displayed below contact buttons on profile.',
'rows' => 3,
'new_lines' => 'br',
),
// Bio Tab
array(
@@ -1006,16 +1388,6 @@ function homeproz_register_acf_fields() {
'type' => 'tab',
'placement' => 'top',
),
array(
'key' => 'field_agent_order',
'label' => 'Display Order',
'name' => 'agent_order',
'type' => 'number',
'instructions' => 'Lower numbers appear first on the Agents page. Agents with the same order are sorted alphabetically.',
'default_value' => 10,
'min' => 0,
'max' => 999,
),
array(
'key' => 'field_agent_disabled',
'label' => 'Disable Agent',
@@ -1263,6 +1635,296 @@ function homeproz_register_acf_fields() {
'position' => 'normal',
'active' => true,
));
// ===========================================
// MLS OVERRIDE FIELD GROUP
// ===========================================
// MLS Override Fields
acf_add_local_field_group(array(
'key' => 'group_mls_override',
'title' => 'MLS Override Settings',
'fields' => array(
array(
'key' => 'field_mls_override_id',
'label' => 'MLS ID',
'name' => 'mls_override_id',
'type' => 'text',
'instructions' => 'Enter the MLS listing ID (the number shown on the property, e.g., "12345678"). This is NOT the listing key.',
'required' => 1,
'placeholder' => 'e.g., 12345678',
),
array(
'key' => 'field_mls_override_featured_photo',
'label' => 'Featured Photo',
'name' => 'mls_override_featured_photo',
'type' => 'image',
'instructions' => 'Upload a custom featured photo to use instead of the MLS photo. This will appear on property cards and as the first image in the gallery.',
'required' => 0,
'return_format' => 'array',
'preview_size' => 'medium',
'library' => 'all',
'mime_types' => 'jpg, jpeg, png, webp',
),
array(
'key' => 'field_mls_override_featured',
'label' => 'Featured Property',
'name' => 'mls_override_featured',
'type' => 'true_false',
'instructions' => 'Check to feature this property on the homepage rotation and prioritize it in property listings.',
'required' => 0,
'default_value' => 0,
'ui' => 1,
'ui_on_text' => 'Featured',
'ui_off_text' => '',
),
),
'location' => array(
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'mls_override',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'active' => true,
));
// ===========================================
// PAGE-SPECIFIC FIELD GROUPS (CMS Migration)
// ===========================================
// About Page Field Group
acf_add_local_field_group(array(
'key' => 'group_about_page',
'title' => 'About Page Content',
'fields' => array(
// Team Section
array(
'key' => 'field_about_team_title',
'label' => 'Team Section Title',
'name' => 'about_team_title',
'type' => 'text',
'default_value' => 'Meet Our Team',
),
array(
'key' => 'field_about_team_subtitle',
'label' => 'Team Section Subtitle',
'name' => 'about_team_subtitle',
'type' => 'textarea',
'rows' => 2,
'default_value' => 'A dedicated group of real estate professionals committed to your success',
),
// Featured Image
array(
'key' => 'field_about_featured_image',
'label' => 'Featured Image',
'name' => 'about_featured_image',
'type' => 'image',
'instructions' => 'Image shown in the company story section. If empty, uses default about-us.webp.',
'return_format' => 'url',
'preview_size' => 'medium',
),
// Broker Information
array(
'key' => 'field_about_broker_title',
'label' => 'Broker Section Title',
'name' => 'about_broker_title',
'type' => 'text',
'default_value' => 'Broker Information',
),
array(
'key' => 'field_about_broker_text',
'label' => 'Broker Information Text',
'name' => 'about_broker_text',
'type' => 'wysiwyg',
'instructions' => 'Legal/compliance broker information. Use line breaks for formatting.',
'tabs' => 'all',
'toolbar' => 'basic',
'media_upload' => 0,
'default_value' => "HomeProz Real Estate LLC DBA LandProz Real Estate, LLC<br>\n111 East Clark Street, Albert Lea, MN 56007<br>\nBroker Brian Haugen - MN | Broker/Auctioneer Greg Jensen - MN, IA - 24-21",
),
// CTA Section
array(
'key' => 'field_about_cta_title',
'label' => 'CTA Title',
'name' => 'about_cta_title',
'type' => 'text',
'default_value' => 'Ready to Work With Us?',
),
array(
'key' => 'field_about_cta_text',
'label' => 'CTA Text',
'name' => 'about_cta_text',
'type' => 'textarea',
'rows' => 2,
'default_value' => 'Contact our team today to start your real estate journey.',
),
array(
'key' => 'field_about_cta_button_text',
'label' => 'CTA Button Text',
'name' => 'about_cta_button_text',
'type' => 'text',
'default_value' => 'Get in Touch',
),
array(
'key' => 'field_about_cta_button_url',
'label' => 'CTA Button URL',
'name' => 'about_cta_button_url',
'type' => 'url',
'instructions' => 'Leave empty to use /contact/',
),
),
'location' => array(
array(
array(
'param' => 'page_template',
'operator' => '==',
'value' => 'page-about.php',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'active' => true,
));
// Contact Page Field Group
acf_add_local_field_group(array(
'key' => 'group_contact_page',
'title' => 'Contact Page Content',
'fields' => array(
array(
'key' => 'field_contact_form_title',
'label' => 'Form Section Title',
'name' => 'contact_form_title',
'type' => 'text',
'default_value' => 'Send Us a Message',
),
array(
'key' => 'field_contact_info_title',
'label' => 'Info Section Title',
'name' => 'contact_info_title',
'type' => 'text',
'default_value' => 'Contact Information',
),
),
'location' => array(
array(
array(
'param' => 'page_template',
'operator' => '==',
'value' => 'page-contact.php',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'active' => true,
));
// Property Inquiry Page Field Group
acf_add_local_field_group(array(
'key' => 'group_property_inquiry',
'title' => 'Property Inquiry Page Content',
'fields' => array(
array(
'key' => 'field_inquiry_hero_title',
'label' => 'Hero Title',
'name' => 'inquiry_hero_title',
'type' => 'text',
'default_value' => 'Request Property Information',
),
array(
'key' => 'field_inquiry_hero_subtitle',
'label' => 'Hero Subtitle',
'name' => 'inquiry_hero_subtitle',
'type' => 'text',
'default_value' => 'Get more details about this listing',
),
array(
'key' => 'field_inquiry_preview_title',
'label' => 'Preview Section Title',
'name' => 'inquiry_preview_title',
'type' => 'text',
'default_value' => "Property You're Inquiring About",
),
),
'location' => array(
array(
array(
'param' => 'page_template',
'operator' => '==',
'value' => 'page-property-inquiry.php',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'active' => true,
));
// Thank You Page Field Group
acf_add_local_field_group(array(
'key' => 'group_thank_you_page',
'title' => 'Thank You Page Content',
'fields' => array(
array(
'key' => 'field_thankyou_hero_title',
'label' => 'Hero Title',
'name' => 'thankyou_hero_title',
'type' => 'text',
'default_value' => 'Thank You!',
),
array(
'key' => 'field_thankyou_hero_subtitle',
'label' => 'Hero Subtitle',
'name' => 'thankyou_hero_subtitle',
'type' => 'text',
'default_value' => 'Your inquiry has been submitted',
),
array(
'key' => 'field_thankyou_heading',
'label' => 'Main Heading',
'name' => 'thankyou_heading',
'type' => 'text',
'default_value' => "We've Received Your Request",
),
array(
'key' => 'field_thankyou_message',
'label' => 'Thank You Message',
'name' => 'thankyou_message',
'type' => 'textarea',
'rows' => 2,
'default_value' => 'One of our team members will review your inquiry and get back to you shortly. We typically respond within 24 hours during business days.',
),
array(
'key' => 'field_thankyou_property_heading',
'label' => 'Property Section Heading',
'name' => 'thankyou_property_heading',
'type' => 'text',
'default_value' => 'Thank you for your interest in:',
),
),
'location' => array(
array(
array(
'param' => 'page_template',
'operator' => '==',
'value' => 'page-inquiry-thank-you.php',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'active' => true,
));
}
add_action('acf/init', 'homeproz_register_acf_fields');
@@ -1286,6 +1948,49 @@ function homeproz_register_acf_options_page() {
}
add_action('acf/init', 'homeproz_register_acf_options_page');
/**
* Enable deep-linking to ACF tabs via URL parameter
* Usage: /wp-admin/admin.php?page=theme-options&tab=properties_page
*/
function homeproz_acf_tab_deeplink() {
$screen = get_current_screen();
if (!$screen || $screen->id !== 'toplevel_page_theme-options') {
return;
}
?>
<script>
(function() {
var params = new URLSearchParams(window.location.search);
var tab = params.get('tab');
if (!tab) return;
// Map friendly tab names to ACF field keys
var tabMap = {
'contact': 'field_theme_tab_contact',
'properties_page': 'field_theme_tab_properties',
'social': 'field_theme_tab_social',
'footer': 'field_theme_tab_footer',
'location': 'field_theme_tab_location',
'404': 'field_theme_tab_404'
};
var fieldKey = tabMap[tab] || tab;
// Wait for ACF to initialize
if (typeof acf !== 'undefined') {
acf.addAction('ready', function() {
var $tab = jQuery('.acf-tab-button[data-key="' + fieldKey + '"]');
if ($tab.length) {
$tab.trigger('click');
}
});
}
})();
</script>
<?php
}
add_action('admin_footer', 'homeproz_acf_tab_deeplink');
/**
* Update homeproz_get_option to use ACF if available
*/
@@ -1309,6 +2014,14 @@ function homeproz_get_acf_option($key, $default = '') {
'latitude' => 'theme_latitude',
'longitude' => 'theme_longitude',
'map_embed' => 'theme_map_embed',
// 404 Page
'404_title' => 'theme_404_title',
'404_subtitle' => 'theme_404_subtitle',
'404_message' => 'theme_404_message',
'404_primary_button' => 'theme_404_primary_button',
'404_secondary_button' => 'theme_404_secondary_button',
// Footer Copyright
'footer_copyright' => 'theme_footer_copyright',
);
// If ACF is active and we have an options page
@@ -10,184 +10,6 @@ if (!defined('ABSPATH')) {
exit;
}
/**
* Register Property Custom Post Type
*/
function homeproz_register_property_cpt() {
$labels = array(
'name' => _x('Properties', 'Post type general name', 'homeproz'),
'singular_name' => _x('Property', 'Post type singular name', 'homeproz'),
'menu_name' => _x('Properties', 'Admin Menu text', 'homeproz'),
'name_admin_bar' => _x('Property', 'Add New on Toolbar', 'homeproz'),
'add_new' => __('Add New', 'homeproz'),
'add_new_item' => __('Add New Property', 'homeproz'),
'new_item' => __('New Property', 'homeproz'),
'edit_item' => __('Edit Property', 'homeproz'),
'view_item' => __('View Property', 'homeproz'),
'all_items' => __('All Properties', 'homeproz'),
'search_items' => __('Search Properties', 'homeproz'),
'parent_item_colon' => __('Parent Properties:', 'homeproz'),
'not_found' => __('No properties found.', 'homeproz'),
'not_found_in_trash' => __('No properties found in Trash.', 'homeproz'),
'featured_image' => _x('Property Featured Image', 'Overrides the "Featured Image" phrase', 'homeproz'),
'set_featured_image' => _x('Set featured image', 'Overrides the "Set featured image" phrase', 'homeproz'),
'remove_featured_image' => _x('Remove featured image', 'Overrides the "Remove featured image" phrase', 'homeproz'),
'use_featured_image' => _x('Use as featured image', 'Overrides the "Use as featured image" phrase', 'homeproz'),
'archives' => _x('Property archives', 'The post type archive label used in nav menus', 'homeproz'),
'insert_into_item' => _x('Insert into property', 'Overrides the "Insert into post" phrase', 'homeproz'),
'uploaded_to_this_item' => _x('Uploaded to this property', 'Overrides the "Uploaded to this post" phrase', 'homeproz'),
'filter_items_list' => _x('Filter properties list', 'Screen reader text', 'homeproz'),
'items_list_navigation' => _x('Properties list navigation', 'Screen reader text', 'homeproz'),
'items_list' => _x('Properties list', 'Screen reader text', 'homeproz'),
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'properties', 'with_front' => false),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 5,
'menu_icon' => 'dashicons-building',
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
'show_in_rest' => true,
);
register_post_type('property', $args);
}
add_action('init', 'homeproz_register_property_cpt');
/**
* Register Property Taxonomies
*/
function homeproz_register_property_taxonomies() {
// Property Type (Residential, Commercial, Land, Multi-Family)
$type_labels = array(
'name' => _x('Property Types', 'Taxonomy general name', 'homeproz'),
'singular_name' => _x('Property Type', 'Taxonomy singular name', 'homeproz'),
'search_items' => __('Search Property Types', 'homeproz'),
'popular_items' => __('Popular Property Types', 'homeproz'),
'all_items' => __('All Property Types', 'homeproz'),
'parent_item' => null,
'parent_item_colon' => null,
'edit_item' => __('Edit Property Type', 'homeproz'),
'update_item' => __('Update Property Type', 'homeproz'),
'add_new_item' => __('Add New Property Type', 'homeproz'),
'new_item_name' => __('New Property Type Name', 'homeproz'),
'separate_items_with_commas' => __('Separate property types with commas', 'homeproz'),
'add_or_remove_items' => __('Add or remove property types', 'homeproz'),
'choose_from_most_used' => __('Choose from the most used property types', 'homeproz'),
'not_found' => __('No property types found.', 'homeproz'),
'menu_name' => __('Property Types', 'homeproz'),
);
register_taxonomy('property_type', array('property'), array(
'hierarchical' => true,
'labels' => $type_labels,
'show_ui' => true,
'show_in_rest' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array('slug' => 'property-type'),
));
// Property Status (Active, Pending, Sold)
$status_labels = array(
'name' => _x('Property Status', 'Taxonomy general name', 'homeproz'),
'singular_name' => _x('Property Status', 'Taxonomy singular name', 'homeproz'),
'search_items' => __('Search Property Statuses', 'homeproz'),
'popular_items' => __('Popular Property Statuses', 'homeproz'),
'all_items' => __('All Property Statuses', 'homeproz'),
'parent_item' => null,
'parent_item_colon' => null,
'edit_item' => __('Edit Property Status', 'homeproz'),
'update_item' => __('Update Property Status', 'homeproz'),
'add_new_item' => __('Add New Property Status', 'homeproz'),
'new_item_name' => __('New Property Status Name', 'homeproz'),
'separate_items_with_commas' => __('Separate statuses with commas', 'homeproz'),
'add_or_remove_items' => __('Add or remove statuses', 'homeproz'),
'choose_from_most_used' => __('Choose from the most used statuses', 'homeproz'),
'not_found' => __('No statuses found.', 'homeproz'),
'menu_name' => __('Status', 'homeproz'),
);
register_taxonomy('property_status', array('property'), array(
'hierarchical' => true,
'labels' => $status_labels,
'show_ui' => true,
'show_in_rest' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array('slug' => 'property-status'),
));
// Property Location (Cities/Areas)
$location_labels = array(
'name' => _x('Locations', 'Taxonomy general name', 'homeproz'),
'singular_name' => _x('Location', 'Taxonomy singular name', 'homeproz'),
'search_items' => __('Search Locations', 'homeproz'),
'popular_items' => __('Popular Locations', 'homeproz'),
'all_items' => __('All Locations', 'homeproz'),
'parent_item' => __('Parent Location', 'homeproz'),
'parent_item_colon' => __('Parent Location:', 'homeproz'),
'edit_item' => __('Edit Location', 'homeproz'),
'update_item' => __('Update Location', 'homeproz'),
'add_new_item' => __('Add New Location', 'homeproz'),
'new_item_name' => __('New Location Name', 'homeproz'),
'separate_items_with_commas' => __('Separate locations with commas', 'homeproz'),
'add_or_remove_items' => __('Add or remove locations', 'homeproz'),
'choose_from_most_used' => __('Choose from the most used locations', 'homeproz'),
'not_found' => __('No locations found.', 'homeproz'),
'menu_name' => __('Locations', 'homeproz'),
);
register_taxonomy('property_location', array('property'), array(
'hierarchical' => true,
'labels' => $location_labels,
'show_ui' => true,
'show_in_rest' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array('slug' => 'location'),
));
}
add_action('init', 'homeproz_register_property_taxonomies');
/**
* Add default taxonomy terms on theme activation
*/
function homeproz_add_default_terms() {
// Default Property Types
$property_types = array('Residential', 'Commercial', 'Land', 'Multi-Family');
foreach ($property_types as $type) {
if (!term_exists($type, 'property_type')) {
wp_insert_term($type, 'property_type');
}
}
// Default Property Statuses
$property_statuses = array('Active', 'Pending', 'Sold');
foreach ($property_statuses as $status) {
if (!term_exists($status, 'property_status')) {
wp_insert_term($status, 'property_status');
}
}
// Default Locations (Minnesota cities near Albert Lea)
$locations = array('Albert Lea', 'Austin', 'Owatonna', 'Faribault', 'Mankato', 'Rochester');
foreach ($locations as $location) {
if (!term_exists($location, 'property_location')) {
wp_insert_term($location, 'property_location');
}
}
}
add_action('after_switch_theme', 'homeproz_add_default_terms');
/**
* Register Agent Custom Post Type
*/
@@ -429,8 +251,6 @@ function homeproz_get_featured_mls_ids() {
* Flush rewrite rules on theme activation
*/
function homeproz_rewrite_flush() {
homeproz_register_property_cpt();
homeproz_register_property_taxonomies();
homeproz_register_agent_cpt();
homeproz_register_mls_override_cpt();
flush_rewrite_rules();
@@ -77,6 +77,15 @@ function homeproz_body_classes($classes) {
$classes[] = 'no-sidebar';
}
// Add title animations class if enabled (default true)
if (is_page() && !is_front_page()) {
$enable_animations = get_field('enable_title_animations');
// Default to true if not set
if ($enable_animations === null || $enable_animations === '' || $enable_animations) {
$classes[] = 'has-title-animations';
}
}
return $classes;
}
add_filter('body_class', 'homeproz_body_classes');
@@ -144,17 +153,6 @@ function homeproz_fallback_menu() {
echo '</ul>';
}
/**
* Fallback footer menu when no menu is assigned
*/
function homeproz_footer_fallback_menu() {
echo '<ul class="footer-menu">';
echo '<li class="menu-item"><a href="' . esc_url(home_url('/')) . '">Home</a></li>';
echo '<li class="menu-item"><a href="' . esc_url(home_url('/properties/')) . '">Properties</a></li>';
echo '<li class="menu-item"><a href="' . esc_url(get_post_type_archive_link('agent')) . '">Agents</a></li>';
echo '<li class="menu-item"><a href="' . esc_url(home_url('/about/')) . '">About</a></li>';
echo '</ul>';
}
/**
* Get status sort order value
@@ -246,100 +244,228 @@ function homeproz_get_featured_mls_listings($count = 3) {
}
$table = $wpdb->prefix . 'mls_properties';
$residential_types = array('Residential', 'Residential Income');
// Albert Lea coordinates and 30-mile bounding box
// 30 miles ~ 0.43 deg latitude, ~0.6 deg longitude at this latitude
$albert_lea_lat = 43.679;
$albert_lea_lon = -93.360;
$lat_range = 0.43;
$lon_range = 0.60;
// Track all listing keys we've added
$added_keys = array();
$listings = array();
$min_lat = $albert_lea_lat - $lat_range;
$max_lat = $albert_lea_lat + $lat_range;
$min_lon = $albert_lea_lon - $lon_range;
$max_lon = $albert_lea_lon + $lon_range;
// 1. Get featured MLS IDs from the override system (FIRST PRIORITY)
$featured_mls_ids = function_exists('homeproz_get_featured_mls_ids')
? homeproz_get_featured_mls_ids()
: array();
// Get all HomeProz listings (active only, exclude TBD addresses)
// Uses \b word boundary for whole-word match, case-insensitive
$homeproz_listings = $wpdb->get_results(
"SELECT listing_key, list_price, street_number, street_name, street_suffix,
// Add featured residential listings first
if (!empty($featured_mls_ids)) {
$id_placeholders = implode(',', array_fill(0, count($featured_mls_ids), '%s'));
$type_placeholders = implode(',', array_fill(0, count($residential_types), '%s'));
$featured_query = $wpdb->prepare(
"SELECT listing_key, listing_id, list_price, street_number, street_name, street_suffix,
city, state_or_province, postal_code, bedrooms_total, bathrooms_total,
living_area, standard_status, property_type, photos_count
FROM {$table}
WHERE listing_id IN ({$id_placeholders})
AND property_type IN ({$type_placeholders})
AND standard_status = 'Active'
AND mlg_can_view = 1
AND photos_count > 0
ORDER BY modification_timestamp DESC",
...array_merge($featured_mls_ids, $residential_types)
);
$featured_listings = $wpdb->get_results($featured_query);
foreach ($featured_listings as $listing) {
if (count($listings) >= $count) break;
$listings[] = homeproz_format_mls_listing_for_json($listing, false);
$added_keys[] = $listing->listing_key;
}
}
// 2. Add HomeProz residential listings (if we need more)
if (count($listings) < $count) {
$type_placeholders = implode(',', array_fill(0, count($residential_types), '%s'));
$exclude_clause = '';
if (!empty($added_keys)) {
$key_placeholders = implode(',', array_fill(0, count($added_keys), '%s'));
$exclude_clause = $wpdb->prepare(" AND listing_key NOT IN ({$key_placeholders})", ...$added_keys);
}
$homeproz_query = $wpdb->prepare(
"SELECT listing_key, listing_id, list_price, street_number, street_name, street_suffix,
city, state_or_province, postal_code, bedrooms_total, bathrooms_total,
living_area, standard_status, property_type, photos_count
FROM {$table}
WHERE is_homeproz = 1
AND property_type IN ({$type_placeholders})
AND standard_status = 'Active'
AND mlg_can_view = 1
AND COALESCE(street_name, '') NOT REGEXP '\\\\bTBD\\\\b'
AND COALESCE(street_number, '') NOT REGEXP '\\\\bTBD\\\\b'
AND photos_count > 0
ORDER BY modification_timestamp DESC"
{$exclude_clause}
ORDER BY modification_timestamp DESC",
...$residential_types
);
$homeproz_listings = $wpdb->get_results($homeproz_query);
$listings = array();
// Add HomeProz listings first
foreach ($homeproz_listings as $listing) {
if (count($listings) >= $count) break;
$listings[] = homeproz_format_mls_listing_for_json($listing, true);
$added_keys[] = $listing->listing_key;
}
}
// If we need padding, get nearby average-priced listings
// 3. Fill remaining slots with random residential listings
if (count($listings) < $count) {
$needed = $count - count($listings);
$homeproz_keys = array_column($homeproz_listings, 'listing_key');
$type_placeholders = implode(',', array_fill(0, count($residential_types), '%s'));
// Get average price for residential properties near Albert Lea
$avg_price = $wpdb->get_var($wpdb->prepare(
"SELECT AVG(list_price)
FROM {$table}
WHERE standard_status = 'Active'
AND mlg_can_view = 1
AND property_type = 'Residential'
AND latitude BETWEEN %f AND %f
AND longitude BETWEEN %f AND %f
AND list_price > 0",
$min_lat, $max_lat, $min_lon, $max_lon
));
if ($avg_price > 0) {
// +/- 10% of average price
$min_price = $avg_price * 0.9;
$max_price = $avg_price * 1.1;
// Build exclusion clause for HomeProz listings
$exclude_clause = '';
if (!empty($homeproz_keys)) {
$placeholders = implode(',', array_fill(0, count($homeproz_keys), '%s'));
$exclude_clause = $wpdb->prepare(
" AND listing_key NOT IN ({$placeholders})",
...$homeproz_keys
);
if (!empty($added_keys)) {
$key_placeholders = implode(',', array_fill(0, count($added_keys), '%s'));
$exclude_clause = $wpdb->prepare(" AND listing_key NOT IN ({$key_placeholders})", ...$added_keys);
}
// Get random padding listings within price range and distance
$padding_listings = $wpdb->get_results($wpdb->prepare(
"SELECT listing_key, list_price, street_number, street_name, street_suffix,
$random_query = $wpdb->prepare(
"SELECT listing_key, listing_id, list_price, street_number, street_name, street_suffix,
city, state_or_province, postal_code, bedrooms_total, bathrooms_total,
living_area, standard_status, property_type, photos_count
FROM {$table}
WHERE standard_status = 'Active'
WHERE property_type IN ({$type_placeholders})
AND standard_status = 'Active'
AND mlg_can_view = 1
AND property_type = 'Residential'
AND latitude BETWEEN %f AND %f
AND longitude BETWEEN %f AND %f
AND list_price BETWEEN %f AND %f
AND photos_count > 0
{$exclude_clause}
ORDER BY RAND()
LIMIT %d",
$min_lat, $max_lat, $min_lon, $max_lon,
$min_price, $max_price,
$needed
));
...array_merge($residential_types, array($needed))
);
$random_listings = $wpdb->get_results($random_query);
foreach ($padding_listings as $listing) {
foreach ($random_listings as $listing) {
$listings[] = homeproz_format_mls_listing_for_json($listing, false);
}
}
return $listings;
}
/**
* Get featured commercial/land MLS listings for homepage
*
* Priority order:
* 1. Featured (favorited) commercial properties from MLS Editor
* 2. HomeProz-listed commercial properties
* 3. Random commercial/land properties to fill remaining slots
*
* @param int $count Number of listings to return (default 3)
* @return array Array of formatted listing data
*/
function homeproz_get_featured_commercial_listings($count = 3) {
global $wpdb;
if (!function_exists('mls_get_image_url')) {
return array();
}
$table = $wpdb->prefix . 'mls_properties';
$commercial_types = array('Commercial Sale', 'Land', 'Farm');
$type_placeholders = implode(',', array_fill(0, count($commercial_types), '%s'));
// Track all listing keys we've added
$added_keys = array();
$listings = array();
// 1. Get featured MLS IDs from the override system
$featured_mls_ids = function_exists('homeproz_get_featured_mls_ids')
? homeproz_get_featured_mls_ids()
: array();
// Add featured commercial listings first
if (!empty($featured_mls_ids)) {
$id_placeholders = implode(',', array_fill(0, count($featured_mls_ids), '%s'));
$featured_query = $wpdb->prepare(
"SELECT listing_key, listing_id, list_price, street_number, street_name, street_suffix,
city, state_or_province, postal_code, bedrooms_total, bathrooms_total,
living_area, standard_status, property_type, photos_count
FROM {$table}
WHERE listing_id IN ({$id_placeholders})
AND property_type IN ({$type_placeholders})
AND standard_status = 'Active'
AND mlg_can_view = 1
AND photos_count > 0
ORDER BY modification_timestamp DESC",
...array_merge($featured_mls_ids, $commercial_types)
);
$featured_listings = $wpdb->get_results($featured_query);
foreach ($featured_listings as $listing) {
if (count($listings) >= $count) break;
$listings[] = homeproz_format_mls_listing_for_json($listing, false);
$added_keys[] = $listing->listing_key;
}
}
// 2. Add HomeProz commercial listings (if we need more)
if (count($listings) < $count) {
$exclude_clause = '';
if (!empty($added_keys)) {
$key_placeholders = implode(',', array_fill(0, count($added_keys), '%s'));
$exclude_clause = $wpdb->prepare(" AND listing_key NOT IN ({$key_placeholders})", ...$added_keys);
}
$homeproz_query = $wpdb->prepare(
"SELECT listing_key, listing_id, list_price, street_number, street_name, street_suffix,
city, state_or_province, postal_code, bedrooms_total, bathrooms_total,
living_area, standard_status, property_type, photos_count
FROM {$table}
WHERE is_homeproz = 1
AND property_type IN ({$type_placeholders})
AND standard_status = 'Active'
AND mlg_can_view = 1
AND photos_count > 0
{$exclude_clause}
ORDER BY modification_timestamp DESC",
...$commercial_types
);
$homeproz_listings = $wpdb->get_results($homeproz_query);
foreach ($homeproz_listings as $listing) {
if (count($listings) >= $count) break;
$listings[] = homeproz_format_mls_listing_for_json($listing, true);
$added_keys[] = $listing->listing_key;
}
}
// 3. Fill remaining slots with random commercial/land listings
if (count($listings) < $count) {
$needed = $count - count($listings);
$exclude_clause = '';
if (!empty($added_keys)) {
$key_placeholders = implode(',', array_fill(0, count($added_keys), '%s'));
$exclude_clause = $wpdb->prepare(" AND listing_key NOT IN ({$key_placeholders})", ...$added_keys);
}
$random_query = $wpdb->prepare(
"SELECT listing_key, listing_id, list_price, street_number, street_name, street_suffix,
city, state_or_province, postal_code, bedrooms_total, bathrooms_total,
living_area, standard_status, property_type, photos_count
FROM {$table}
WHERE property_type IN ({$type_placeholders})
AND standard_status = 'Active'
AND mlg_can_view = 1
AND photos_count > 0
{$exclude_clause}
ORDER BY RAND()
LIMIT %d",
...array_merge($commercial_types, array($needed))
);
$random_listings = $wpdb->get_results($random_query);
foreach ($random_listings as $listing) {
$listings[] = homeproz_format_mls_listing_for_json($listing, false);
}
}
return $listings;
@@ -369,10 +495,23 @@ function homeproz_format_mls_listing_for_json($listing, $is_homeproz = false) {
$full_address .= ', ' . $listing->state_or_province;
}
// Check for MLS override (custom featured photo)
$listing_id = isset($listing->listing_id) ? $listing->listing_id : null;
$override = function_exists('homeproz_get_mls_override') ? homeproz_get_mls_override($listing_id) : null;
// Get image URL - use override if available, otherwise MLS image
if ($override && !empty($override['featured_photo'])) {
$image_url = isset($override['featured_photo']['sizes']['medium_large'])
? $override['featured_photo']['sizes']['medium_large']
: $override['featured_photo']['url'];
} else {
$image_url = mls_get_image_url($listing->listing_key, 1, 'thumb');
}
return array(
'listing_key' => $listing->listing_key,
'url' => home_url('/properties/?listing=' . $listing->listing_key),
'image_url' => mls_get_image_url($listing->listing_key, 1, 'thumb'),
'image_url' => $image_url,
'price' => (float) $listing->list_price,
'price_formatted' => homeproz_format_price($listing->list_price),
'address' => $full_address,
@@ -438,13 +577,14 @@ function homeproz_get_mls_cities($min_listings = 5) {
// Get cities with at least $min_listings active properties
// Also filter to MN and IA only
$cities = $wpdb->get_col($wpdb->prepare(
"SELECT city FROM {$table}
// Returns objects with city and state_code for "City, SS" display
$cities = $wpdb->get_results($wpdb->prepare(
"SELECT city, state_or_province as state_code FROM {$table}
WHERE mlg_can_view = 1
AND standard_status = 'Active'
AND city IS NOT NULL
AND state_or_province IN ('MN', 'IA')
GROUP BY city
GROUP BY city, state_or_province
HAVING COUNT(*) >= %d
ORDER BY city ASC",
$min_listings
@@ -518,3 +658,43 @@ function homeproz_get_active_locations() {
'order' => 'ASC',
));
}
/**
* Add "Edit Page" link to admin bar for archive pages with Theme Options
*
* Since archive pages don't have a traditional "Edit" link, this adds
* a link to the relevant Theme Options tab for editing hero content, etc.
*/
function homeproz_admin_bar_edit_link($wp_admin_bar) {
if (!is_user_logged_in() || !current_user_can('edit_posts')) {
return;
}
$edit_link = null;
$title = null;
// Properties archive
if (is_post_type_archive('property')) {
$edit_link = admin_url('admin.php?page=theme-options&tab=properties_page');
$title = 'Edit Page';
}
// Add more archive pages as needed:
// if (is_post_type_archive('agent')) {
// $edit_link = admin_url('admin.php?page=theme-options&tab=agents_page');
// $title = 'Edit Page';
// }
if ($edit_link) {
$wp_admin_bar->add_node(array(
'id' => 'edit-archive-page',
'title' => $title,
'href' => $edit_link,
'meta' => array(
'class' => 'ab-item',
),
));
}
}
add_action('admin_bar_menu', 'homeproz_admin_bar_edit_link', 80);
+63 -14
View File
@@ -19,6 +19,7 @@
@import '../template-parts/content/content-join.scss';
@import '../template-parts/content/content-contact.scss';
@import '../template-parts/content/content-archive.scss';
@import '../template-parts/content/content-property-inquiry.scss';
@import '../template-parts/sidebar/blog-sidebar.scss';
@import '../template-parts/content/content-templates.scss';
@import '../template-parts/content/content-communities.scss';
@@ -39,6 +40,7 @@
@import '../template-parts/components/feature-block.scss';
@import '../template-parts/components/service-cards.scss';
@import '../template-parts/components/property-type-boxes.scss';
@import '../template-parts/components/custom-content-section.scss';
// Import page templates
@import '../page-templates/page-templates.scss';
@@ -89,20 +91,6 @@ body {
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
// Subtle noise texture overlay for luxury feel
&::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
opacity: 0.03;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
}
}
// ============================================
@@ -197,6 +185,12 @@ a {
color: white;
}
}
&-sm {
padding: 0.5rem 1rem;
font-size: 0.75rem;
gap: 0.375rem;
}
}
// Status badges
@@ -223,6 +217,28 @@ a {
background-color: var(--color-sold);
color: white;
}
// Property type badge
&-type {
background-color: #1E40AF;
color: #DBEAFE;
}
// Status badges
&-active {
background-color: #065F46;
color: #D1FAE5;
}
&-pending {
background-color: #92400E;
color: #FEF3C7;
}
&-sold {
background-color: #374151;
color: #D1D5DB;
}
}
// Cards
@@ -262,6 +278,11 @@ select {
}
}
// Contact Form 7 - remove auto-inserted br tags
.wpcf7 br {
display: none;
}
// ============================================
// Utility Classes
// ============================================
@@ -329,3 +350,31 @@ select {
::view-transition-new(root) {
animation: none;
}
// ============================================
// Section Title Hover Animations
// ============================================
.has-title-animations {
// Shared animated title styles
.about-team-title,
.about-broker-title,
.about-story-content h2,
.about-story-content h3,
.contact-form-title,
.contact-info-title,
.join-team-title,
.join-benefit-title,
.section-title,
.resource-featured-title,
.resource-card-title {
display: inline-block;
transition: letter-spacing 0.2s ease, transform 0.2s ease;
cursor: default;
&:hover {
letter-spacing: 0.02em;
transform: translateX(2px);
}
}
}
@@ -0,0 +1,37 @@
<?php
/**
* Custom Content Section
*
* Displays a WYSIWYG content area with optional title.
*
* @package HomeProz
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Get passed arguments
$title = $args['title'] ?? '';
$content = $args['content'] ?? '';
// Don't render if no content
if (empty($content)) {
return;
}
?>
<section class="custom-content-section">
<div class="container">
<?php if (!empty($title)) : ?>
<header class="section-header">
<h2 class="section-title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="custom-content-body">
<?php echo wp_kses_post($content); ?>
</div>
</div>
</section>
@@ -0,0 +1,127 @@
/**
* Custom Content Section Styles
*
* WYSIWYG content area for homepage.
*
* @package HomeProz
*/
.custom-content-section {
padding: 4rem 0;
background-color: var(--color-bg);
@media (max-width: 768px) {
padding: 3rem 0;
}
// Alternate background for visual variety
&:nth-of-type(even) {
background-color: var(--color-bg-card);
}
}
.custom-content-body {
max-width: 900px;
margin: 0 auto;
font-size: 1rem;
line-height: 1.7;
color: var(--color-text);
h2, h3, h4, h5, h6 {
font-family: var(--font-display);
color: var(--color-text);
margin-top: 2rem;
margin-bottom: 1rem;
&:first-child {
margin-top: 0;
}
}
h2 {
font-size: 1.75rem;
}
h3 {
font-size: 1.5rem;
}
h4 {
font-size: 1.25rem;
}
p {
margin-bottom: 1.25rem;
&:last-child {
margin-bottom: 0;
}
}
a {
color: var(--color-accent);
text-decoration: underline;
&:hover {
color: var(--color-accent-hover);
}
}
ul, ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
li {
margin-bottom: 0.5rem;
}
}
ul {
list-style-type: disc;
}
ol {
list-style-type: decimal;
}
blockquote {
border-left: 4px solid var(--color-accent);
padding-left: 1.5rem;
margin: 1.5rem 0;
font-style: italic;
color: var(--color-text-muted);
}
img {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
margin: 1.5rem 0;
}
// WordPress alignment classes
.alignleft {
float: left;
margin-right: 1.5rem;
margin-bottom: 1rem;
}
.alignright {
float: right;
margin-left: 1.5rem;
margin-bottom: 1rem;
}
.aligncenter {
display: block;
margin-left: auto;
margin-right: auto;
}
// Clear floats
&::after {
content: '';
display: table;
clear: both;
}
}
+62 -50
View File
@@ -3,7 +3,7 @@
* Property Type Showcase Boxes
*
* Displays category boxes for browsing properties by type.
* Links to the property archive with type filter applied.
* Fully configurable via ACF repeater on homepage.
*
* @package HomeProz
*/
@@ -13,72 +13,84 @@ if (!defined('ABSPATH')) {
exit;
}
// Get property types with counts from MLS
$property_types = homeproz_get_mls_property_types();
// Get section title/subtitle from ACF
$section_title = get_field('home_property_types_title') ?: 'Browse by Property Type';
$section_subtitle = get_field('home_property_types_subtitle') ?: 'Find the perfect property for your needs';
// Get property type boxes from ACF repeater
$property_types = get_field('home_property_type_boxes');
// If no ACF data, use defaults
if (empty($property_types)) {
return;
$property_types = array(
array(
'title' => 'Residential',
'description' => 'Single family homes, condos & townhomes',
'url' => home_url('/properties/?property_type=Residential'),
'icon' => 'home',
),
array(
'title' => 'Land',
'description' => 'Vacant lots & acreages',
'url' => home_url('/properties/?property_type=Land'),
'icon' => 'land',
),
array(
'title' => 'Commercial',
'description' => 'Office, retail & industrial',
'url' => home_url('/properties/?property_type=Commercial+Sale'),
'icon' => 'building',
),
array(
'title' => 'Farm',
'description' => 'Agricultural & hobby farms',
'url' => home_url('/properties/?property_type=Farm'),
'icon' => 'farm',
),
array(
'title' => 'Multi-Family',
'description' => 'Duplexes, triplexes & apartments',
'url' => home_url('/properties/?property_type=Residential+Income'),
'icon' => 'multi-family',
),
);
}
// Define display configuration for each type
// Icons use Feather Icons (already in use throughout the site)
$type_config = array(
'Residential' => array(
'icon' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>',
'description' => 'Single family homes, condos & townhomes',
),
'Land' => array(
'icon' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 22h20"/><path d="M6.36 17.4L4 22h16l-4.24-7.94a.5.5 0 0 0-.88 0L12.5 18.1l-2.5-4.5a.5.5 0 0 0-.88 0z"/><circle cx="13.5" cy="5.5" r="2.5"/></svg>',
'description' => 'Vacant lots & acreages',
),
'Commercial Sale' => array(
'icon' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="4" y="2" width="16" height="20" rx="2" ry="2"/><path d="M9 22v-4h6v4"/><path d="M8 6h.01M12 6h.01M16 6h.01M8 10h.01M12 10h.01M16 10h.01M8 14h.01M12 14h.01M16 14h.01"/></svg>',
'label' => 'Commercial',
'description' => 'Office, retail & industrial',
),
'Farm' => array(
'icon' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 21h18"/><path d="M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/><path d="M10 9h4"/></svg>',
'description' => 'Agricultural & hobby farms',
),
'Residential Income' => array(
'icon' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 12h6"/><path d="M9 16h6"/></svg>',
'label' => 'Multi-Family',
'description' => 'Duplexes, triplexes & apartments',
),
);
/**
* Render icon SVG based on icon name
*/
function homeproz_render_property_type_icon($icon) {
$icons = array(
'home' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>',
'land' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 22h20"/><path d="M6.36 17.4L4 22h16l-4.24-7.94a.5.5 0 0 0-.88 0L12.5 18.1l-2.5-4.5a.5.5 0 0 0-.88 0z"/><circle cx="13.5" cy="5.5" r="2.5"/></svg>',
'building' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="4" y="2" width="16" height="20" rx="2" ry="2"/><path d="M9 22v-4h6v4"/><path d="M8 6h.01M12 6h.01M16 6h.01M8 10h.01M12 10h.01M16 10h.01M8 14h.01M12 14h.01M16 14h.01"/></svg>',
'farm' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 21h18"/><path d="M5 21V7l7-4 7 4v14"/><path d="M9 21v-6h6v6"/><path d="M10 9h4"/></svg>',
'multi-family' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 12h6"/><path d="M9 16h6"/></svg>',
'key' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>',
'map-pin' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>',
'dollar-sign' => '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>',
);
// Filter to only show configured types and sort by count
$display_types = array();
foreach ($property_types as $type) {
if (isset($type_config[$type->property_type])) {
$display_types[] = $type;
}
return isset($icons[$icon]) ? $icons[$icon] : $icons['home'];
}
?>
<section class="property-type-boxes">
<div class="container">
<header class="section-header">
<h2 class="section-title">Browse by Property Type</h2>
<p class="section-subtitle">Find the perfect property for your needs</p>
<h2 class="section-title"><?php echo esc_html($section_title); ?></h2>
<p class="section-subtitle"><?php echo esc_html($section_subtitle); ?></p>
</header>
<div class="type-boxes-grid">
<?php foreach ($display_types as $type) :
$type_name = $type->property_type;
$config = $type_config[$type_name];
$label = isset($config['label']) ? $config['label'] : $type_name;
$count = number_format($type->count);
$url = add_query_arg('property_type', urlencode($type_name), home_url('/properties/'));
?>
<a href="<?php echo esc_url($url); ?>" class="type-box">
<?php foreach ($property_types as $type) : ?>
<a href="<?php echo esc_url($type['url']); ?>" class="type-box">
<div class="type-box-icon">
<?php echo $config['icon']; ?>
<?php echo homeproz_render_property_type_icon($type['icon']); ?>
</div>
<div class="type-box-content">
<h3 class="type-box-title"><?php echo esc_html($label); ?></h3>
<p class="type-box-description"><?php echo esc_html($config['description']); ?></p>
<span class="type-box-count"><?php echo esc_html($count); ?> listings</span>
<h3 class="type-box-title"><?php echo esc_html($type['title']); ?></h3>
<p class="type-box-description"><?php echo esc_html($type['description']); ?></p>
</div>
<div class="type-box-arrow">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+21 -27
View File
@@ -37,7 +37,9 @@
.type-box {
display: flex;
flex-direction: column;
padding: 1.5rem;
align-items: center;
text-align: center;
padding: 1.75rem 1.5rem;
background-color: var(--color-bg-dark);
border: 1px solid var(--color-border);
border-radius: 0.5rem;
@@ -48,10 +50,6 @@
border-color: var(--color-accent);
transform: translateY(-2px);
.type-box-icon {
color: var(--color-accent);
}
.type-box-arrow {
opacity: 1;
transform: translateX(0);
@@ -59,13 +57,15 @@
}
@media (max-width: 768px) {
padding: 1.25rem;
padding: 1.25rem 1rem;
}
}
.type-box-icon {
color: var(--color-text-muted);
margin-bottom: 1rem;
display: flex;
justify-content: center;
color: var(--color-accent);
margin-bottom: 1.25rem;
transition: color 0.2s ease;
svg {
@@ -73,7 +73,7 @@
}
@media (max-width: 768px) {
margin-bottom: 0.75rem;
margin-bottom: 1rem;
svg {
width: 28px;
@@ -84,48 +84,42 @@
.type-box-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.type-box-title {
font-family: var(--font-display);
font-size: 1.125rem;
font-weight: 600;
font-size: 1.375rem;
font-weight: 400;
color: var(--color-text);
margin-bottom: 0.25rem;
margin-bottom: 0.625rem;
@media (max-width: 768px) {
font-size: 1rem;
font-size: 1.125rem;
margin-bottom: 0.5rem;
}
}
.type-box-description {
font-size: 0.8125rem;
color: var(--color-text-muted);
line-height: 1.4;
margin-bottom: 0.75rem;
line-height: 1.5;
margin-bottom: 0;
@media (max-width: 768px) {
font-size: 0.75rem;
margin-bottom: 0.5rem;
}
}
.type-box-count {
display: inline-block;
font-size: 0.75rem;
font-weight: 500;
color: var(--color-accent-light);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.type-box-arrow {
align-self: flex-end;
align-self: center;
color: var(--color-accent);
opacity: 0;
transform: translateX(-8px);
transition: all 0.2s ease;
margin-top: 0.75rem;
margin-top: 1rem;
@media (max-width: 768px) {
display: none;