## Major Enhancements ### 🏗️ Architecture & Infrastructure - Implement comprehensive Docker testing infrastructure with hermetic environment - Add Forgejo Actions CI/CD pipeline for automated deployments - Create Page Object Model (POM) testing architecture reducing test duplication by 90% - Establish security-first development patterns with input validation and output escaping ### 🧪 Testing Framework Modernization - Migrate 146+ tests from 80 duplicate files to centralized architecture - Add comprehensive E2E test suites for all user roles and workflows - Implement WordPress error detection with automatic site health monitoring - Create robust browser lifecycle management with proper cleanup ### 📚 Documentation & Guides - Add comprehensive development best practices guide - Create detailed administrator setup documentation - Establish user guides for trainers and master trainers - Document security incident reports and migration guides ### 🔧 Core Plugin Features - Enhance trainer profile management with certification system - Improve find trainer functionality with advanced filtering - Strengthen master trainer area with content management - Add comprehensive venue and organizer management ### 🛡️ Security & Reliability - Implement security-first patterns throughout codebase - Add comprehensive input validation and output escaping - Create secure credential management system - Establish proper WordPress role-based access control ### 🎯 WordPress Integration - Strengthen singleton pattern implementation across all classes - Enhance template hierarchy with proper WordPress integration - Improve page manager with hierarchical URL structure - Add comprehensive shortcode and menu system ### 🔍 Developer Experience - Add extensive debugging and troubleshooting tools - Create comprehensive test data seeding scripts - Implement proper error handling and logging - Establish consistent code patterns and standards ### 📊 Performance & Optimization - Optimize database queries and caching strategies - Improve asset loading and script management - Enhance template rendering performance - Streamline user experience across all interfaces 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			444 lines
		
	
	
		
			No EOL
		
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			444 lines
		
	
	
		
			No EOL
		
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | ||
| /**
 | ||
|  * Template Name: HVAC Trainer Dashboard
 | ||
|  *
 | ||
|  * This template handles the display of the HVAC Trainer Dashboard.
 | ||
|  * It checks for user login and role, then displays stats and events.
 | ||
|  *
 | ||
|  * @package    HVAC Community Events
 | ||
|  * @subpackage Templates
 | ||
|  * @author     Roo
 | ||
|  * @version    1.0.1
 | ||
|  */
 | ||
| 
 | ||
| // Exit if accessed directly.
 | ||
| if ( ! defined( 'ABSPATH' ) ) {
 | ||
| 	exit;
 | ||
| }
 | ||
| 
 | ||
| // --- Security Check & Data Loading ---
 | ||
| echo '<!-- DEBUG: template-hvac-dashboard.php loaded -->';
 | ||
| 
 | ||
| // Ensure user is logged in and has access to the dashboard
 | ||
| if ( ! is_user_logged_in() ) {
 | ||
| 	// Redirect to login page if not logged in
 | ||
| 	wp_safe_redirect( home_url( '/community-login/' ) );
 | ||
| 	exit;
 | ||
| }
 | ||
| 
 | ||
| // Check if user has permission to view dashboard
 | ||
| // Allow administrators and users with view_hvac_dashboard capability
 | ||
| if ( ! current_user_can( 'view_hvac_dashboard' ) && ! current_user_can( 'manage_options' ) ) {
 | ||
| 	// Show access denied message instead of redirect to prevent loops
 | ||
| 	get_header();
 | ||
| 	?>
 | ||
| 	<style>
 | ||
| 	.hvac-access-denied {
 | ||
| 		max-width: 600px;
 | ||
| 		margin: 60px auto;
 | ||
| 		padding: 40px;
 | ||
| 		text-align: center;
 | ||
| 		background: #fff;
 | ||
| 		border-radius: 8px;
 | ||
| 		box-shadow: 0 2px 10px rgba(0,0,0,0.1);
 | ||
| 	}
 | ||
| 	.hvac-access-denied h1 {
 | ||
| 		color: #d63638;
 | ||
| 		margin-bottom: 20px;
 | ||
| 	}
 | ||
| 	.hvac-access-denied p {
 | ||
| 		margin-bottom: 15px;
 | ||
| 		color: #666;
 | ||
| 		line-height: 1.6;
 | ||
| 	}
 | ||
| 	.hvac-access-denied .button {
 | ||
| 		background: #0073aa;
 | ||
| 		color: white;
 | ||
| 		padding: 12px 24px;
 | ||
| 		text-decoration: none;
 | ||
| 		border-radius: 4px;
 | ||
| 		display: inline-block;
 | ||
| 		margin-top: 20px;
 | ||
| 	}
 | ||
| 	.hvac-access-denied .button:hover {
 | ||
| 		background: #005a87;
 | ||
| 		color: white;
 | ||
| 	}
 | ||
| 	</style>
 | ||
| 	<div class="content-area primary ast-container">
 | ||
| 		<main class="site-main">
 | ||
| 			<div class="hvac-access-denied">
 | ||
| 				<h1><?php _e('Access Denied', 'hvac-community-events'); ?></h1>
 | ||
| 				<p><?php _e('Sorry, you do not have permission to access the HVAC Trainer Dashboard.', 'hvac-community-events'); ?></p>
 | ||
| 				<p><?php _e('If you are an HVAC trainer, please contact an administrator to get the proper role assigned.', 'hvac-community-events'); ?></p>
 | ||
| 				<a href="<?php echo esc_url( home_url() ); ?>" class="button"><?php _e('Return to Home', 'hvac-community-events'); ?></a>
 | ||
| 			</div>
 | ||
| 		</main>
 | ||
| 	</div>
 | ||
| 	<?php
 | ||
| 	get_footer();
 | ||
| 	exit;
 | ||
| }
 | ||
| 
 | ||
| // Get the current user ID
 | ||
| $user_id = get_current_user_id();
 | ||
| 
 | ||
| // Dashboard data class is loaded during plugin initialization
 | ||
| $dashboard_data = new HVAC_Dashboard_Data( $user_id );
 | ||
| 
 | ||
| // Fetch data
 | ||
| $total_events    = $dashboard_data->get_total_events_count();
 | ||
| $upcoming_events = $dashboard_data->get_upcoming_events_count();
 | ||
| $past_events     = $dashboard_data->get_past_events_count();
 | ||
| $total_sold      = $dashboard_data->get_total_tickets_sold();
 | ||
| $total_revenue   = $dashboard_data->get_total_revenue();
 | ||
| $revenue_target  = $dashboard_data->get_annual_revenue_target();
 | ||
| 
 | ||
| // --- Template Start ---
 | ||
| 
 | ||
| // Don't call get_header() here - this template is included via shortcode
 | ||
| // The page template already handles header/footer
 | ||
| 
 | ||
| ?>
 | ||
| <div class="hvac-dashboard-wrapper"> <!-- Dashboard specific wrapper -->
 | ||
| 		<?php
 | ||
| 		// Navigation is handled by page template when using WordPress pages
 | ||
| 		// Never render navigation here - it should be in the page template only
 | ||
| 		?>
 | ||
| 		
 | ||
| 		<!-- Dashboard Header -->
 | ||
| 		<div class="hvac-dashboard-header">
 | ||
| 			<h1 class="entry-title">Trainer Dashboard</h1> <!-- Standard WP title class -->
 | ||
| 		</div>
 | ||
| 
 | ||
| 		<!-- Statistics Section -->
 | ||
| 		<section class="hvac-dashboard-stats">
 | ||
| 			<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 				'<h2>Your Stats</h2>',
 | ||
| 				'Overview of your event performance and revenue metrics',
 | ||
| 				'bottom'
 | ||
| 			); ?>
 | ||
| 			<div class="hvac-stats-row"> <!-- Use flexbox row layout -->
 | ||
| 
 | ||
| 				<!-- Stat Card: Total Events -->
 | ||
| 				<div class="hvac-stat-col"> <!-- Equal width flexbox column -->
 | ||
| 					<div class="hvac-stat-card">
 | ||
| 						<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 							'<h3>Total Events</h3>',
 | ||
| 							'All events you\'ve created, including drafts and published events'
 | ||
| 						); ?>
 | ||
| 						<p class="metric-value"><?php echo esc_html( $total_events ); ?></p>
 | ||
| 					</div>
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<!-- Stat Card: Upcoming Events -->
 | ||
| 				<div class="hvac-stat-col">
 | ||
| 					<div class="hvac-stat-card">
 | ||
| 						<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 							'<h3>Upcoming Events</h3>',
 | ||
| 							'Published events scheduled for future dates'
 | ||
| 						); ?>
 | ||
| 						<p class="metric-value"><?php echo esc_html( $upcoming_events ); ?></p>
 | ||
| 					</div>
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<!-- Stat Card: Past Events -->
 | ||
| 				<div class="hvac-stat-col">
 | ||
| 					<div class="hvac-stat-card">
 | ||
| 						<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 							'<h3>Past Events</h3>',
 | ||
| 							'Completed events where you can generate certificates'
 | ||
| 						); ?>
 | ||
| 						<p class="metric-value"><?php echo esc_html( $past_events ); ?></p>
 | ||
| 					</div>
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<!-- Stat Card: Total Tickets Sold -->
 | ||
| 				<div class="hvac-stat-col">
 | ||
| 					<div class="hvac-stat-card">
 | ||
| 						<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 							'<h3>Tickets Sold</h3>',
 | ||
| 							'Total number of tickets sold across all your events'
 | ||
| 						); ?>
 | ||
| 						<p class="metric-value"><?php echo esc_html( $total_sold ); ?></p>
 | ||
| 					</div>
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<!-- Stat Card: Total Revenue -->
 | ||
| 				<div class="hvac-stat-col">
 | ||
| 					<div class="hvac-stat-card">
 | ||
| 						<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 							'<h3>Total Revenue</h3>',
 | ||
| 							'Total earnings from all ticket sales (before Stripe fees)'
 | ||
| 						); ?>
 | ||
| 						<p class="metric-value">$<?php echo esc_html( number_format( $total_revenue, 2 ) ); ?></p>
 | ||
| 						<?php if ( $revenue_target ) : ?>
 | ||
| 							<small>Target: $<?php echo esc_html( number_format( $revenue_target, 2 ) ); ?></small>
 | ||
| 						<?php endif; ?>
 | ||
| 					</div>
 | ||
| 				</div>
 | ||
| 
 | ||
| 			</div> <!-- /.ast-row -->
 | ||
| 		</section>
 | ||
| 
 | ||
| 		<!-- Events Table Section -->
 | ||
| 		<section class="hvac-dashboard-events">
 | ||
| 			<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 				'<h2>Your Events</h2>',
 | ||
| 				'Detailed view of all your events with performance metrics and management options',
 | ||
| 				'bottom'
 | ||
| 			); ?>
 | ||
| 
 | ||
| 			<!-- Enhanced Filters and Controls -->
 | ||
| 			<div class="hvac-table-controls">
 | ||
| 				<!-- Search Box -->
 | ||
| 				<div class="hvac-search-box">
 | ||
| 					<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 						'<input type="search" id="hvac-event-search" placeholder="Search events..." class="regular-text">',
 | ||
| 						'Search events by name'
 | ||
| 					); ?>
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<!-- Date Range Filters -->
 | ||
| 				<div class="hvac-date-filters">
 | ||
| 					<label for="hvac-date-from">From:</label>
 | ||
| 					<input type="date" id="hvac-date-from" class="hvac-date-input">
 | ||
| 					<label for="hvac-date-to">To:</label>
 | ||
| 					<input type="date" id="hvac-date-to" class="hvac-date-input">
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<!-- Per Page Selector -->
 | ||
| 				<div class="hvac-per-page">
 | ||
| 					<label for="hvac-per-page">Show:</label>
 | ||
| 					<select id="hvac-per-page" class="hvac-per-page-select">
 | ||
| 						<option value="10" selected>10</option>
 | ||
| 						<option value="25">25</option>
 | ||
| 						<option value="50">50</option>
 | ||
| 						<option value="100">100</option>
 | ||
| 					</select>
 | ||
| 					<span>per page</span>
 | ||
| 				</div>
 | ||
| 			</div>
 | ||
| 
 | ||
| 			<!-- Tab Filters -->
 | ||
| 			<?php
 | ||
| 			$dashboard_url = get_permalink(); // Get the current page URL
 | ||
| 			$current_filter = isset( $_GET['event_status'] ) ? sanitize_key( $_GET['event_status'] ) : 'all';
 | ||
| 			$filter_statuses = ['all', 'publish', 'draft', 'pending', 'private']; // Added private based on requirements/query
 | ||
| 			?>
 | ||
| 			<div class="hvac-event-filters">
 | ||
| 				<?php echo HVAC_Help_System::add_tooltip(
 | ||
| 					'<span>Filter: </span>',
 | ||
| 					'Filter events by their publication status'
 | ||
| 				); ?>
 | ||
| 				<?php foreach ($filter_statuses as $status) :
 | ||
| 					$url = add_query_arg( 'event_status', $status, $dashboard_url );
 | ||
| 					$class = 'ast-button ast-button-secondary hvac-filter';
 | ||
| 					if ($status === $current_filter) {
 | ||
| 						// Add a class or style for the active filter
 | ||
| 						// Using primary button style for active state as an example
 | ||
| 						$class = 'ast-button ast-button-primary hvac-filter hvac-filter-active';
 | ||
| 					}
 | ||
| 					// Special case for 'all' filter link
 | ||
| 					if ($status === 'all') {
 | ||
| 						$url = remove_query_arg( 'event_status', $dashboard_url );
 | ||
| 					}
 | ||
| 				?>
 | ||
| 					<a href="<?php echo esc_url( $url ); ?>" class="<?php echo esc_attr( $class ); ?>" data-status="<?php echo esc_attr( $status ); ?>"><?php echo esc_html( ucfirst( $status ) ); ?></a>
 | ||
| 				<?php endforeach; ?>
 | ||
| 			</div>
 | ||
| 
 | ||
| 			<!-- Events Table -->
 | ||
| 			<?php
 | ||
| 			// $current_filter is already defined above
 | ||
| 			// Get events with new parameters
 | ||
| 			$args = array(
 | ||
| 				'status' => $current_filter,
 | ||
| 				'search' => isset($_GET['search']) ? sanitize_text_field($_GET['search']) : '',
 | ||
| 				'orderby' => isset($_GET['orderby']) ? sanitize_key($_GET['orderby']) : 'date',
 | ||
| 				'order' => isset($_GET['order']) ? sanitize_key($_GET['order']) : 'DESC',
 | ||
| 				'page' => isset($_GET['paged']) ? absint($_GET['paged']) : 1,
 | ||
| 				'per_page' => isset($_GET['per_page']) ? absint($_GET['per_page']) : 10,
 | ||
| 				'date_from' => isset($_GET['date_from']) ? sanitize_text_field($_GET['date_from']) : '',
 | ||
| 				'date_to' => isset($_GET['date_to']) ? sanitize_text_field($_GET['date_to']) : ''
 | ||
| 			);
 | ||
| 			
 | ||
| 			$result = $dashboard_data->get_events_table_data( $args );
 | ||
| 			$events = $result['events'];
 | ||
| 			$pagination = $result['pagination'];
 | ||
| 			?>
 | ||
| 
 | ||
| 			<div class="hvac-events-table-wrapper">
 | ||
| 				<table class="wp-list-table widefat fixed striped events-table">
 | ||
| 					<thead>
 | ||
| 						<tr>
 | ||
| 							<th scope="col" class="manage-column column-status">Status</th>
 | ||
| 							<th scope="col" class="manage-column column-title">Event Name</th>
 | ||
| 							<th scope="col" class="manage-column column-date">Date</th>
 | ||
| 							<th scope="col" class="manage-column column-organizer">Organizer</th>
 | ||
| 							<th scope="col" class="manage-column column-capacity">Capacity</th>
 | ||
| 							<th scope="col" class="manage-column column-sold">Sold</th>
 | ||
| 							<th scope="col" class="manage-column column-revenue">Revenue</th>
 | ||
| 							<th scope="col" class="manage-column column-actions">Actions</th> <!-- Added Actions Column -->
 | ||
| 						</tr>
 | ||
| 					</thead>
 | ||
| 					<tbody id="the-list">
 | ||
| 						<?php if ( ! empty( $events ) ) : ?>
 | ||
| 							<?php foreach ( $events as $event ) : ?>
 | ||
| 								<tr>
 | ||
| 									<td class="column-status"><?php echo esc_html( ucfirst( $event['status'] ) ); ?></td>
 | ||
| 									<td class="column-title">
 | ||
| 										<strong><a href="<?php echo esc_url( $event['link'] ); ?>" target="_blank"><?php echo esc_html( $event['name'] ); ?></a></strong>
 | ||
| 										<!-- Add Edit/View links below title if needed -->
 | ||
| 									</td>
 | ||
| 									<td class="column-date"><?php echo esc_html( date( 'Y-m-d H:i', $event['start_date_ts'] ) ); ?></td>
 | ||
| 									<td class="column-organizer"><?php
 | ||
| 										// Check if tribe_get_organizer function exists before calling
 | ||
| 										if ( function_exists( 'tribe_get_organizer' ) ) {
 | ||
| 											echo esc_html( tribe_get_organizer( $event['organizer_id'] ) );
 | ||
| 										} else {
 | ||
| 											echo 'Organizer ID: ' . esc_html( $event['organizer_id'] ); // Fallback
 | ||
| 										}
 | ||
| 									?></td>
 | ||
| 									<td class="column-capacity"><?php echo esc_html( $event['capacity'] ); ?></td>
 | ||
| 									<td class="column-sold"><?php echo esc_html( $event['sold'] ); ?></td>
 | ||
| 									<td class="column-revenue">$<?php echo esc_html( number_format( $event['revenue'], 2 ) ); ?></td>
 | ||
| 									<td class="column-actions">
 | ||
| 										<?php
 | ||
| 										// Link to the new page containing the TEC CE submission form shortcode
 | ||
| 										$edit_url = add_query_arg( 'event_id', $event['id'], home_url( '/trainer/event/manage/' ) );
 | ||
| 										// Link to the custom event summary page
 | ||
| 										$summary_url = add_query_arg( 'event_id', $event['id'], home_url( '/trainer/event/summary/' ) );
 | ||
| 										// Link to the standard WP single event view
 | ||
| 										$view_url = get_permalink( $event['id'] );
 | ||
| 										?>
 | ||
| 										<a href="<?php echo esc_url( $edit_url ); ?>">Edit</a> |
 | ||
| 										<a href="<?php echo esc_url( $summary_url ); ?>">Summary</a> |
 | ||
| 										<a href="<?php echo esc_url( $view_url ); ?>" target="_blank">View</a>
 | ||
| 									</td>
 | ||
| 								</tr>
 | ||
| 							<?php endforeach; ?>
 | ||
| 						<?php else : ?>
 | ||
| 							<tr>
 | ||
| 								<td colspan="8">No events found.</td> <!-- Updated colspan -->
 | ||
| 							</tr>
 | ||
| 						<?php endif; ?>
 | ||
| 					</tbody>
 | ||
| 				</table>
 | ||
| 			</div>
 | ||
| 
 | ||
| 			<div class="tablenav bottom">
 | ||
| 				<div class="tablenav-pages">
 | ||
| 					<span class="displaying-num"><?php echo esc_html($pagination['total_items']); ?> items</span>
 | ||
| 					<?php if ($pagination['total_pages'] > 1) : ?>
 | ||
| 					<span class="pagination-links">
 | ||
| 						<?php if ($pagination['has_prev']) : ?>
 | ||
| 							<a class="first-page button" href="#" data-page="1">
 | ||
| 								<span class="screen-reader-text">First page</span>
 | ||
| 								<span aria-hidden="true">«</span>
 | ||
| 							</a>
 | ||
| 							<a class="prev-page button" href="#" data-page="<?php echo esc_attr($pagination['current_page'] - 1); ?>">
 | ||
| 								<span class="screen-reader-text">Previous page</span>
 | ||
| 								<span aria-hidden="true">‹</span>
 | ||
| 							</a>
 | ||
| 						<?php else : ?>
 | ||
| 							<span class="tablenav-pages-navspan button disabled" aria-hidden="true">«</span>
 | ||
| 							<span class="tablenav-pages-navspan button disabled" aria-hidden="true">‹</span>
 | ||
| 						<?php endif; ?>
 | ||
| 						
 | ||
| 						<span class="paging-input">
 | ||
| 							<label for="current-page-selector" class="screen-reader-text">Current Page</label>
 | ||
| 							<input class="current-page" id="current-page-selector" type="text" name="paged" value="<?php echo esc_attr($pagination['current_page']); ?>" size="1" aria-describedby="table-paging">
 | ||
| 							<span class="tablenav-paging-text"> of <span class="total-pages"><?php echo esc_html($pagination['total_pages']); ?></span></span>
 | ||
| 						</span>
 | ||
| 						
 | ||
| 						<?php if ($pagination['has_next']) : ?>
 | ||
| 							<a class="next-page button" href="#" data-page="<?php echo esc_attr($pagination['current_page'] + 1); ?>">
 | ||
| 								<span class="screen-reader-text">Next page</span>
 | ||
| 								<span aria-hidden="true">›</span>
 | ||
| 							</a>
 | ||
| 							<a class="last-page button" href="#" data-page="<?php echo esc_attr($pagination['total_pages']); ?>">
 | ||
| 								<span class="screen-reader-text">Last page</span>
 | ||
| 								<span aria-hidden="true">»</span>
 | ||
| 							</a>
 | ||
| 						<?php else : ?>
 | ||
| 							<span class="tablenav-pages-navspan button disabled" aria-hidden="true">›</span>
 | ||
| 							<span class="tablenav-pages-navspan button disabled" aria-hidden="true">»</span>
 | ||
| 						<?php endif; ?>
 | ||
| 					</span>
 | ||
| 				</div>
 | ||
| 			</div>
 | ||
| 			<?php endif; ?>
 | ||
| 
 | ||
| 		</section>
 | ||
| 
 | ||
| </div> <!-- .hvac-dashboard-wrapper -->
 | ||
| 
 | ||
| <style>
 | ||
| /* Dashboard Stats Flexbox Layout */
 | ||
| .hvac-stats-row {
 | ||
|     display: flex;
 | ||
|     flex-direction: row;
 | ||
|     flex-wrap: wrap;
 | ||
|     margin: -10px;
 | ||
|     justify-content: space-between;
 | ||
|     align-items: stretch;
 | ||
| }
 | ||
| 
 | ||
| .hvac-stat-col {
 | ||
|     flex: 1;
 | ||
|     min-width: 160px;
 | ||
|     padding: 10px;
 | ||
| }
 | ||
| 
 | ||
| .hvac-stat-card {
 | ||
|     background: #f8f9fa;
 | ||
|     border: 1px solid #e9ecef;
 | ||
|     border-radius: 8px;
 | ||
|     padding: 20px;
 | ||
|     text-align: center;
 | ||
|     height: 100%;
 | ||
|     display: flex;
 | ||
|     flex-direction: column;
 | ||
|     justify-content: center;
 | ||
| }
 | ||
| 
 | ||
| .hvac-stat-card h3 {
 | ||
|     margin: 0 0 10px;
 | ||
|     font-size: 16px;
 | ||
|     font-weight: normal;
 | ||
|     color: #666;
 | ||
| }
 | ||
| 
 | ||
| .hvac-stat-card .metric-value {
 | ||
|     font-size: 32px;
 | ||
|     font-weight: bold;
 | ||
|     color: #E9AF28;
 | ||
|     margin: 0;
 | ||
|     line-height: 1.2;
 | ||
| }
 | ||
| 
 | ||
| /* Table controls spacing */
 | ||
| .hvac-table-controls {
 | ||
|     margin: 20px 0 25px 0;
 | ||
|     padding: 15px;
 | ||
|     background: #f8f9fa;
 | ||
|     border-radius: 6px;
 | ||
|     display: flex;
 | ||
|     flex-wrap: wrap;
 | ||
|     gap: 15px;
 | ||
|     align-items: center;
 | ||
| }
 | ||
| 
 | ||
| .hvac-event-filters {
 | ||
|     margin: 15px 0 25px 0;
 | ||
|     display: flex;
 | ||
|     flex-wrap: wrap;
 | ||
|     gap: 8px;
 | ||
|     align-items: center;
 | ||
| }
 | ||
| </style>
 | ||
| 
 | ||
| <?php
 | ||
| // Don't call get_footer() here - this template is included via shortcode
 | ||
| ?>
 |