<?php
/**
 * Inbox admin page.
 *
 * @package SubmissionInbox
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Renders the leads inbox.
 */
class SUBIN_InboxPage {
	const PER_PAGE = 20;
	const DETAIL_FIELD_LIMIT = 12;
	const HIGHLIGHT_FIELD_LIMIT = 2;
	const MAX_BULK_IDS = 100;

	/**
	 * Lead repository.
	 *
	 * @var \SUBIN\Leads\LeadRepository
	 */
	private $leads;

	/**
	 * Field repository.
	 *
	 * @var \SUBIN\Leads\LeadFieldRepository
	 */
	private $fields;

	/**
	 * Mail event repository.
	 *
	 * @var \SUBIN\MailLog\MailEventRepository
	 */
	private $mail_events;

	/**
	 * Settings repository.
	 *
	 * @var \SUBIN_SettingsRepository
	 */
	private $settings;

	/**
	 * Form repository.
	 *
	 * @var \SUBIN\Forms\FormRepository
	 */
	private $forms;

	/**
	 * Auto-reply template repository.
	 *
	 * @var \SUBIN\AutoReplies\AutoReplyTemplateRepository
	 */
	private $auto_reply_templates;

	/**
	 * Auto-reply rule repository.
	 *
	 * @var \SUBIN\AutoReplies\AutoReplyRuleRepository
	 */
	private $auto_reply_rules;

	/**
	 * Lead workflow repository.
	 *
	 * @var \SUBIN\Leads\LeadWorkflowRepository
	 */
	private $workflow;

	/**
	 * Lead activity repository.
	 *
	 * @var \SUBIN\Leads\LeadActivityRepository
	 */
	private $activity;

	/**
	 * Provider registry.
	 *
	 * @var \SUBIN\Integrations\ProviderRegistry
	 */
	private $providers;

	/**
	 * Form submission context.
	 *
	 * @var \SUBIN\Forms\FormSubmissionContext|null
	 */
	private $form_context;

	/**
	 * Email error classifier.
	 *
	 * @var \SUBIN\Mail\EmailErrorClassifier
	 */
	private $error_classifier;

	/**
	 * Whether state-changing request handling has already run.
	 *
	 * @var bool
	 */
	private $request_handled = false;

	/**
	 * Rejected stale note edit kept only for the current response.
	 *
	 * @var array<string,mixed>|null
	 */
	private $note_conflict_draft = null;

	/**
	 * Cached users who can both open and manage the Inbox.
	 *
	 * @var array<int,object>|null
	 */
	private $assignable_user_cache = null;

	/**
	 * Constructor.
	 *
	 * @param \SUBIN\Forms\FormSubmissionContext|null $form_context Form submission context.
	 */
	public function __construct( $form_context = null ) {
		$this->leads                = new \SUBIN\Leads\LeadRepository();
		$this->fields               = new \SUBIN\Leads\LeadFieldRepository();
		$this->mail_events          = new \SUBIN\MailLog\MailEventRepository();
		$this->settings             = new \SUBIN_SettingsRepository();
		$this->forms                = new \SUBIN\Forms\FormRepository();
		$this->auto_reply_templates = new \SUBIN\AutoReplies\AutoReplyTemplateRepository();
		$this->auto_reply_rules     = new \SUBIN\AutoReplies\AutoReplyRuleRepository();
		$this->activity             = new \SUBIN\Leads\LeadActivityRepository();
		$this->workflow             = new \SUBIN\Leads\LeadWorkflowRepository( $this->activity );
		$this->providers            = new \SUBIN\Integrations\ProviderRegistry();
		$this->form_context         = $form_context;
		$this->error_classifier     = new \SUBIN\Mail\EmailErrorClassifier();
	}

	/**
	 * Render page.
	 *
	 * @return void
	 */
	public function render() {
		if ( ! \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::VIEW_LEADS ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this action.', 'submission-inbox' ) );
		}

		$this->handle_request();

		$action  = SUBIN_Request::get_key( 'action' );
		$lead_id = SUBIN_Request::get_absint( 'lead_id' );

		if ( 'view' === $action && $lead_id > 0 ) {
			$this->render_detail( $lead_id );
			return;
		}

		$this->render_list();
	}

	/**
	 * Handle redirects and state changes before admin HTML is sent.
	 *
	 * @return void
	 */
	public function handle_request() {
		if ( $this->request_handled ) {
			return;
		}

		$this->request_handled = true;

		if ( ! \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::VIEW_LEADS ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this action.', 'submission-inbox' ) );
		}

		$this->maybe_handle_action();
	}

	/**
	 * Handle CSV export before the admin page sends HTML.
	 *
	 * @return void
	 */
	public function handle_export_request() {
		if ( 'leads' !== sanitize_key( wp_unslash( $_GET['subin_export'] ?? '' ) ) ) {
			return;
		}
		if ( ! \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::EXPORT_LEADS ) ) {
			wp_die( esc_html__( 'You do not have permission to export leads.', 'submission-inbox' ) );
		}

		$this->maybe_export( $this->get_filters_from_request() );
	}

	/**
	 * Render inbox list.
	 *
	 * @return void
	 */
	private function render_list() {
		$filters     = $this->get_filters_from_request();
		$paged       = max( 1, SUBIN_Request::get_absint( 'paged', 1 ) );
		$total       = $this->leads->count_leads( $filters );
		$total_pages = max( 1, (int) ceil( $total / self::PER_PAGE ) );
		$paged       = min( $paged, $total_pages );

		$leads = $this->leads->get_paginated(
			array_merge(
				$filters,
				array(
					'per_page' => self::PER_PAGE,
					'offset'   => ( $paged - 1 ) * self::PER_PAGE,
				)
			)
		);

		$header_args = array(
			'secondary_actions' => array(),
			'wrapper_class'     => 'subin-inbox-page',
		);
		if ( $this->can_access_legacy_admin_page( \SUBIN\Security\Capabilities::VIEW_EMAIL_HEALTH ) ) {
			$header_args['primary_action'] = array(
				'label' => __( 'View Email Health', 'submission-inbox' ),
				'url'   => admin_url( 'admin.php?page=subin-email-status' ),
			);
		}
		if ( \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::EXPORT_LEADS ) ) {
			$header_args['secondary_actions'][] = array(
				'label' => __( 'Export CSV', 'submission-inbox' ),
				'url'   => $this->export_url( $filters ),
			);
		}

		SUBIN_View::page_header(
			__( 'Inbox', 'submission-inbox' ),
			__( 'Review saved form submissions and quickly spot notification issues.', 'submission-inbox' ),
			$header_args
		);

		$this->maybe_add_list_notice();
		SUBIN_AdminNotices::render_settings_errors( 'subin_messages' );
		?>
		<div class="si-page">
			<?php $this->render_summary_cards( $filters ); ?>
			<?php $this->render_filters( $filters ); ?>
			<?php $this->render_lead_cards( $leads, $total, $filters ); ?>
			<?php $this->render_pagination( $total, $paged, $filters ); ?>
		</div>
		<?php

		SUBIN_View::page_footer();
	}

	/**
	 * Render summary stats in the mockup card style.
	 *
	 * @param array<string,mixed> $filters Active filters.
	 * @return void
	 */
	private function render_inbox_stats( array $filters ) {
		$counts         = $this->leads->get_summary_counts();
		$new_lead_hours = $this->get_new_lead_hours();
		$new_threshold  = gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) - ( $new_lead_hours * HOUR_IN_SECONDS ) );
		$stats          = array(
			array(
				'label' => __( 'Total leads', 'submission-inbox' ),
				'value' => $counts['total'] ?? 0,
				'url'   => $this->list_url( array(), 1 ),
				'key'   => 'total',
				'icon'  => 'dashicons-groups',
			),
			array(
				'label' => __( 'New leads', 'submission-inbox' ),
				'value' => $this->leads->count( array( 'submitted_after' => $new_threshold ) ),
				'key'   => 'new',
				'icon'  => 'dashicons-star-filled',
			),
			array(
				'label' => __( 'Unseen leads', 'submission-inbox' ),
				'value' => $this->leads->count( array( 'status' => \SUBIN\Leads\LeadRepository::STATUS_NEW ) ),
				'url'   => $this->list_url( $this->filters_for_quick_link( $filters, array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_UNSEEN ) ), 1 ),
				'key'   => 'unread',
				'icon'  => 'dashicons-email-alt',
			),
			array(
				'label' => __( 'Delivery issues', 'submission-inbox' ),
				'value' => $this->leads->count( array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES ) ),
				'url'   => $this->list_url( $this->filters_for_quick_link( $filters, array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES ) ), 1 ),
				'key'   => 'needs_attention',
				'icon'  => 'dashicons-warning',
			),
			array(
				'label' => __( 'Archived', 'submission-inbox' ),
				'value' => $this->leads->count( array( 'status' => \SUBIN\Leads\LeadRepository::STATUS_ARCHIVED ) ),
				'url'   => $this->list_url( $this->filters_for_quick_link( $filters, array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_ARCHIVED ) ), 1 ),
				'key'   => 'archived',
				'icon'  => 'dashicons-archive',
			),
		);
		?>
		<div class="si-stats">
			<?php foreach ( $stats as $stat ) : ?>
				<?php $icon_class = 'subin-status-icon subin-status-icon--' . sanitize_html_class( $stat['key'] ) . ' dashicons ' . $stat['icon']; ?>
				<?php if ( ! empty( $stat['url'] ) ) : ?>
					<a class="si-stat" href="<?php echo esc_url( $stat['url'] ); ?>">
						<span class="<?php echo esc_attr( $icon_class ); ?>" aria-hidden="true"></span>
						<span class="si-stat-copy">
							<strong><?php echo esc_html( (string) $stat['value'] ); ?></strong>
							<span><?php echo esc_html( $stat['label'] ); ?></span>
						</span>
					</a>
				<?php else : ?>
					<div class="si-stat">
						<span class="<?php echo esc_attr( $icon_class ); ?>" aria-hidden="true"></span>
						<span class="si-stat-copy">
							<strong><?php echo esc_html( (string) $stat['value'] ); ?></strong>
							<span><?php echo esc_html( $stat['label'] ); ?></span>
						</span>
					</div>
				<?php endif; ?>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Render filters in the supplied toolbar layout.
	 *
	 * @param array<string,mixed> $filters Active filters.
	 * @return void
	 */
	private function render_inbox_toolbar( array $filters ) {
		$form_options = $this->leads->get_form_filter_options();
		?>
		<div class="si-toolbar">
			<nav class="si-tabs" aria-label="<?php esc_attr_e( 'Quick lead filters', 'submission-inbox' ); ?>">
				<?php foreach ( $this->quick_filter_chips( $filters ) as $chip ) : ?>
					<a class="si-tab<?php echo ! empty( $chip['active'] ) ? ' is-active' : ''; ?>" href="<?php echo esc_url( $chip['url'] ); ?>" title="<?php echo esc_attr( $chip['description'] ?? '' ); ?>"<?php echo ! empty( $chip['active'] ) ? ' aria-current="page"' : ''; ?>>
						<?php echo esc_html( $chip['label'] ); ?>
					</a>
				<?php endforeach; ?>
			</nav>
			<form method="get" action="">
				<input type="hidden" name="page" value="<?php echo esc_attr( SUBIN_AdminMenu::MENU_SLUG ); ?>" />
				<div class="si-filters">
					<input class="si-input" type="search" name="s" value="<?php echo esc_attr( $filters['search'] ); ?>" placeholder="<?php esc_attr_e( 'Search leads by name, email, message, form...', 'submission-inbox' ); ?>" />
					<select class="si-select" name="subin_email_status" aria-label="<?php esc_attr_e( 'Team notification', 'submission-inbox' ); ?>">
						<option value=""><?php esc_html_e( 'Team: All', 'submission-inbox' ); ?></option>
						<option value="failed" <?php selected( $filters['email_status'], 'failed' ); ?>><?php esc_html_e( 'Failed', 'submission-inbox' ); ?></option>
						<option value="sent" title="<?php esc_attr_e( 'WordPress accepted the sending attempt; delivery to the recipient is not verified.', 'submission-inbox' ); ?>" <?php selected( $filters['email_status'], 'sent' ); ?>><?php esc_html_e( 'Accepted by WordPress', 'submission-inbox' ); ?></option>
						<option value="no_email_generated" <?php selected( $filters['email_status'], 'no_email_generated' ); ?>><?php esc_html_e( 'No email generated', 'submission-inbox' ); ?></option>
						<option value="unknown" <?php selected( $filters['email_status'], 'unknown' ); ?>><?php esc_html_e( 'Unknown', 'submission-inbox' ); ?></option>
					</select>
					<select class="si-select" name="subin_provider" aria-label="<?php esc_attr_e( 'Source', 'submission-inbox' ); ?>">
						<option value=""><?php esc_html_e( 'Source: All', 'submission-inbox' ); ?></option>
						<?php foreach ( $this->providers->active_keys() as $provider_key ) : ?>
							<option value="<?php echo esc_attr( $provider_key ); ?>" <?php selected( $filters['provider'], $provider_key ); ?>><?php echo esc_html( $this->providers->label( $provider_key ) ); ?></option>
						<?php endforeach; ?>
					</select>
					<select class="si-select" name="subin_form_id" aria-label="<?php esc_attr_e( 'Form', 'submission-inbox' ); ?>">
						<option value=""><?php esc_html_e( 'Form: All', 'submission-inbox' ); ?></option>
						<?php foreach ( $form_options as $form_option ) : ?>
							<?php
							$form_value = $form_option['provider'] . '|' . $form_option['form_id'];
							$form_label = $form_option['form_name'] ? $form_option['form_name'] : $form_option['form_id'];
							?>
							<option value="<?php echo esc_attr( $form_value ); ?>" <?php selected( $filters['form_filter'], $form_value ); ?>>
								<?php echo esc_html( $form_label ); ?>
							</option>
						<?php endforeach; ?>
					</select>
					<button class="si-btn-light" type="submit"><?php esc_html_e( 'Filter', 'submission-inbox' ); ?></button>
				</div>
			</form>
		</div>
		<?php
	}

	/**
	 * Render summary cards.
	 *
	 * @return void
	 */
	private function render_summary_cards( array $filters = array() ) {
		$counts              = $this->leads->get_summary_counts();
		$new_lead_hours      = $this->get_new_lead_hours();
		$new_lead_threshold  = gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) - ( $new_lead_hours * HOUR_IN_SECONDS ) );
		$current_new         = $this->leads->count(
			array(
				'submitted_after' => $new_lead_threshold,
			)
		);
		$total_unseen        = $this->leads->count( array( 'status' => \SUBIN\Leads\LeadRepository::STATUS_NEW ) );
		$mark_all_seen_label = \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS )
			? __( 'Mark all seen', 'submission-inbox' )
			: '';
		$mark_all_seen_through_id = $total_unseen > 0 && '' !== $mark_all_seen_label
			? $this->leads->max_id_for_status( \SUBIN\Leads\LeadRepository::STATUS_NEW )
			: 0;
		$cards               = array(
			'total'           => array(
				'label'  => __( 'Total leads', 'submission-inbox' ),
				'value'  => $counts['total'] ?? 0,
				'icon'   => 'dashicons-groups',
				'url'    => $this->list_url( array(), 1 ),
				'active' => ! $this->has_active_filters( $filters ),
			),
			'new'             => array(
				'label'       => __( 'New leads', 'submission-inbox' ),
				'value'       => $current_new,
				'icon'        => 'dashicons-star-filled',
				'description' => sprintf(
					/* translators: %d: number of hours. */
					__( 'Last %d hours', 'submission-inbox' ),
					$new_lead_hours
				),
			),
			'unread'          => array(
				'label'  => __( 'Unseen leads', 'submission-inbox' ),
				'value'  => $total_unseen,
				'icon'   => 'dashicons-email-alt',
				'url'    => $this->list_url( array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_UNSEEN ), 1 ),
				'active' => \SUBIN\Leads\LeadRepository::QUEUE_UNSEEN === ( $filters['queue'] ?? '' ),
				'action' => $total_unseen > 0 ? $mark_all_seen_label : '',
			),
			'needs_attention' => array(
				'label'  => __( 'Delivery issues', 'submission-inbox' ),
				'value'  => $this->leads->count( array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES ) ),
				'icon'   => 'dashicons-warning',
				'url'    => $this->list_url( array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES ), 1 ),
				'active' => \SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES === ( $filters['queue'] ?? '' ),
			),
			'archived'        => array(
				'label'  => __( 'Archived', 'submission-inbox' ),
				'value'  => $this->leads->count( array( 'status' => \SUBIN\Leads\LeadRepository::STATUS_ARCHIVED ) ),
				'icon'   => 'dashicons-archive',
				'url'    => $this->list_url( array( 'queue' => \SUBIN\Leads\LeadRepository::QUEUE_ARCHIVED ), 1 ),
				'active' => \SUBIN\Leads\LeadRepository::QUEUE_ARCHIVED === ( $filters['queue'] ?? '' ),
			),
		);
		?>
		<div class="subin-summary-grid subin-inbox-summary">
			<?php foreach ( $cards as $key => $card ) : ?>
				<div class="subin-summary-card<?php echo ! empty( $card['active'] ) ? ' is-active' : ''; ?>">
					<span class="<?php echo esc_attr( 'subin-status-icon subin-status-icon--' . sanitize_html_class( $key ) . ' dashicons ' . $card['icon'] ); ?>" aria-hidden="true"></span>
					<?php if ( ! empty( $card['url'] ) ) : ?>
						<a class="subin-summary-card__link" href="<?php echo esc_url( $card['url'] ); ?>"<?php echo ! empty( $card['active'] ) ? ' aria-current="page"' : ''; ?>>
							<span class="subin-summary-value"><?php echo esc_html( (string) $card['value'] ); ?></span>
							<span class="subin-summary-label"><?php echo esc_html( $card['label'] ); ?></span>
						</a>
					<?php else : ?>
						<span class="subin-summary-value"><?php echo esc_html( (string) $card['value'] ); ?></span>
						<span class="subin-summary-label"><?php echo esc_html( $card['label'] ); ?></span>
					<?php endif; ?>
					<?php if ( ! empty( $card['description'] ) ) : ?>
						<span class="subin-summary-description"><?php echo esc_html( $card['description'] ); ?></span>
					<?php endif; ?>
					<?php if ( ! empty( $card['action'] ) ) : ?>
						<form method="post" action="" class="subin-summary-action">
							<input type="hidden" name="subin_lead_action" value="mark_all_seen" />
							<input type="hidden" name="status_high_water_id" value="<?php echo esc_attr( (string) $mark_all_seen_through_id ); ?>" />
							<?php wp_nonce_field( 'subin_mark_all_seen_' . $mark_all_seen_through_id, 'subin_lead_nonce' ); ?>
							<button type="submit" class="subin-btn subin-btn-secondary subin-btn-small"><?php echo esc_html( $card['action'] ); ?></button>
						</form>
					<?php endif; ?>
				</div>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Render filters and search.
	 *
	 * @param array<string,mixed> $filters Active filters.
	 * @return void
	 */
	private function render_filters( array $filters ) {
		$form_options = $this->leads->get_form_filter_options();
		$users        = $this->assignable_users();
		?>
		<div class="subin-filter-panel">
			<nav class="subin-filter-chips" aria-label="<?php esc_attr_e( 'Quick lead filters', 'submission-inbox' ); ?>">
				<?php foreach ( $this->quick_filter_chips( $filters ) as $chip ) : ?>
					<a class="subin-filter-chip<?php echo ! empty( $chip['active'] ) ? ' is-active' : ''; ?>" href="<?php echo esc_url( $chip['url'] ); ?>" title="<?php echo esc_attr( $chip['description'] ?? '' ); ?>"<?php echo ! empty( $chip['active'] ) ? ' aria-current="page"' : ''; ?>>
						<?php echo esc_html( $chip['label'] ); ?>
					</a>
				<?php endforeach; ?>
			</nav>
			<form method="get" action="" class="subin-filter-form">
				<input type="hidden" name="page" value="<?php echo esc_attr( SUBIN_AdminMenu::MENU_SLUG ); ?>" />
				<div class="subin-filter-main subin-filter-main--inbox">
					<div class="subin-filter-field subin-filter-field--search">
						<label for="subin-inbox-search"><?php esc_html_e( 'Search', 'submission-inbox' ); ?></label>
						<input type="search" id="subin-inbox-search" name="s" value="<?php echo esc_attr( $filters['search'] ); ?>" placeholder="<?php esc_attr_e( 'Lead ID, contact, form, message, or field', 'submission-inbox' ); ?>" />
					</div>
					<div class="subin-filter-field">
						<label for="subin-status"><?php esc_html_e( 'Visibility', 'submission-inbox' ); ?></label>
						<select id="subin-status" name="subin_status">
							<option value=""><?php esc_html_e( 'All', 'submission-inbox' ); ?></option>
							<option value="new" <?php selected( $filters['status'], 'new' ); ?>><?php esc_html_e( 'Unseen', 'submission-inbox' ); ?></option>
							<option value="seen" <?php selected( $filters['status'], 'seen' ); ?>><?php esc_html_e( 'Seen', 'submission-inbox' ); ?></option>
							<option value="archived" <?php selected( $filters['status'], 'archived' ); ?>><?php esc_html_e( 'Archived', 'submission-inbox' ); ?></option>
						</select>
					</div>
					<div class="subin-filter-field">
						<label for="subin-provider"><?php esc_html_e( 'Source', 'submission-inbox' ); ?></label>
						<select id="subin-provider" name="subin_provider">
							<option value=""><?php esc_html_e( 'All sources', 'submission-inbox' ); ?></option>
							<?php foreach ( $this->providers->active_keys() as $provider_key ) : ?>
								<option value="<?php echo esc_attr( $provider_key ); ?>" <?php selected( $filters['provider'], $provider_key ); ?>><?php echo esc_html( $this->providers->label( $provider_key ) ); ?></option>
							<?php endforeach; ?>
						</select>
					</div>
					<div class="subin-filter-field">
						<label for="subin-form"><?php esc_html_e( 'Form', 'submission-inbox' ); ?></label>
						<select id="subin-form" name="subin_form_id">
							<option value=""><?php esc_html_e( 'All forms', 'submission-inbox' ); ?></option>
							<?php foreach ( $form_options as $form_option ) : ?>
								<?php
								$form_value = $form_option['provider'] . '|' . $form_option['form_id'];
								$form_label = $form_option['form_name'] ? $form_option['form_name'] : $form_option['form_id'];
								?>
								<option value="<?php echo esc_attr( $form_value ); ?>" <?php selected( $filters['form_filter'], $form_value ); ?>>
									<?php echo esc_html( $form_label . ' - ' . $this->provider_label( $form_option['provider'] ) ); ?>
								</option>
							<?php endforeach; ?>
						</select>
					</div>
					<div class="subin-filter-field">
						<label for="subin-sort"><?php esc_html_e( 'Sort by', 'submission-inbox' ); ?></label>
						<select id="subin-sort" name="subin_sort">
							<option value="newest" <?php selected( $filters['sort'], 'newest' ); ?>><?php esc_html_e( 'Newest first', 'submission-inbox' ); ?></option>
							<option value="oldest" <?php selected( $filters['sort'], 'oldest' ); ?>><?php esc_html_e( 'Oldest first', 'submission-inbox' ); ?></option>
							<option value="recently_updated" <?php selected( $filters['sort'], 'recently_updated' ); ?>><?php esc_html_e( 'Recently updated', 'submission-inbox' ); ?></option>
							<option value="follow_up" <?php selected( $filters['sort'], 'follow_up' ); ?>><?php esc_html_e( 'Follow-up due', 'submission-inbox' ); ?></option>
							<option value="priority" <?php selected( $filters['sort'], 'priority' ); ?>><?php esc_html_e( 'Priority', 'submission-inbox' ); ?></option>
						</select>
					</div>
					<div class="subin-filter-actions">
						<button type="submit" class="button subin-btn subin-btn-primary"><?php esc_html_e( 'Filter', 'submission-inbox' ); ?></button>
						<a class="button subin-btn" href="<?php echo esc_url( $this->list_url( array(), 1 ) ); ?>"><?php esc_html_e( 'Reset', 'submission-inbox' ); ?></a>
					</div>
				</div>
				<details class="subin-advanced-filters subin-filter-advanced"<?php echo ( ! empty( $filters['email_status'] ) || ! empty( $filters['auto_reply_status'] ) || ! empty( $filters['workflow_status'] ) || ! empty( $filters['priority'] ) || '' !== (string) ( $filters['assigned_user_id'] ?? '' ) || ! empty( $filters['date_from'] ) || ! empty( $filters['date_to'] ) ) ? ' open' : ''; ?>>
					<summary class="subin-filter-toggle"><?php esc_html_e( 'Advanced filters', 'submission-inbox' ); ?></summary>
					<div class="subin-advanced-filters__grid">
						<div class="subin-filter-field">
							<label for="subin-email-status"><?php esc_html_e( 'Team notification', 'submission-inbox' ); ?></label>
							<select id="subin-email-status" name="subin_email_status">
								<option value=""><?php esc_html_e( 'All', 'submission-inbox' ); ?></option>
								<option value="sent" title="<?php esc_attr_e( 'WordPress accepted the sending attempt; delivery to the recipient is not verified.', 'submission-inbox' ); ?>" <?php selected( $filters['email_status'], 'sent' ); ?>><?php esc_html_e( 'Accepted by WordPress', 'submission-inbox' ); ?></option>
								<option value="failed" <?php selected( $filters['email_status'], 'failed' ); ?>><?php esc_html_e( 'Failed', 'submission-inbox' ); ?></option>
								<option value="no_email_generated" <?php selected( $filters['email_status'], 'no_email_generated' ); ?>><?php esc_html_e( 'No email generated', 'submission-inbox' ); ?></option>
								<option value="unknown" <?php selected( $filters['email_status'], 'unknown' ); ?>><?php esc_html_e( 'Unknown', 'submission-inbox' ); ?></option>
							</select>
						</div>
						<div class="subin-filter-field">
							<label for="subin-auto-reply-status"><?php esc_html_e( 'Visitor confirmation', 'submission-inbox' ); ?></label>
							<select id="subin-auto-reply-status" name="subin_auto_reply_status">
								<option value=""><?php esc_html_e( 'All', 'submission-inbox' ); ?></option>
								<option value="sent" title="<?php esc_attr_e( 'WordPress accepted the sending attempt; delivery to the recipient is not verified.', 'submission-inbox' ); ?>" <?php selected( $filters['auto_reply_status'], 'sent' ); ?>><?php esc_html_e( 'Accepted by WordPress', 'submission-inbox' ); ?></option>
								<option value="scheduled" <?php selected( $filters['auto_reply_status'], 'scheduled' ); ?>><?php esc_html_e( 'Scheduled', 'submission-inbox' ); ?></option>
								<option value="failed" <?php selected( $filters['auto_reply_status'], 'failed' ); ?>><?php esc_html_e( 'Failed', 'submission-inbox' ); ?></option>
								<option value="no_email_generated" <?php selected( $filters['auto_reply_status'], 'no_email_generated' ); ?>><?php esc_html_e( 'Not sent', 'submission-inbox' ); ?></option>
								<option value="not_configured" <?php selected( $filters['auto_reply_status'], 'not_configured' ); ?>><?php esc_html_e( 'Not configured', 'submission-inbox' ); ?></option>
								<option value="unknown" <?php selected( $filters['auto_reply_status'], 'unknown' ); ?>><?php esc_html_e( 'Unknown', 'submission-inbox' ); ?></option>
							</select>
						</div>
						<div class="subin-filter-field">
							<label for="subin-workflow-status"><?php esc_html_e( 'Workflow', 'submission-inbox' ); ?></label>
							<select id="subin-workflow-status" name="subin_workflow_status">
								<option value=""><?php esc_html_e( 'All', 'submission-inbox' ); ?></option>
								<?php foreach ( $this->workflow->workflow_statuses() as $key => $label ) : ?>
									<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $filters['workflow_status'], $key ); ?>><?php echo esc_html( $label ); ?></option>
								<?php endforeach; ?>
							</select>
						</div>
						<div class="subin-filter-field">
							<label for="subin-priority"><?php esc_html_e( 'Priority', 'submission-inbox' ); ?></label>
							<select id="subin-priority" name="subin_priority">
								<option value=""><?php esc_html_e( 'All', 'submission-inbox' ); ?></option>
								<?php foreach ( $this->workflow->priorities() as $key => $label ) : ?>
									<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $filters['priority'], $key ); ?>><?php echo esc_html( $label ); ?></option>
								<?php endforeach; ?>
							</select>
						</div>
						<div class="subin-filter-field">
							<label for="subin-assigned-user"><?php esc_html_e( 'Assignee', 'submission-inbox' ); ?></label>
							<select id="subin-assigned-user" name="subin_assigned_user_id">
								<option value=""><?php esc_html_e( 'All', 'submission-inbox' ); ?></option>
								<option value="0" <?php selected( (string) $filters['assigned_user_id'], '0' ); ?>><?php esc_html_e( 'Unassigned', 'submission-inbox' ); ?></option>
								<?php foreach ( $users as $user ) : ?>
									<option value="<?php echo esc_attr( (string) $user->ID ); ?>" <?php selected( absint( $filters['assigned_user_id'] ), absint( $user->ID ) ); ?>><?php echo esc_html( $user->display_name ); ?></option>
								<?php endforeach; ?>
							</select>
						</div>
						<div class="subin-filter-field">
							<label for="subin-work-queue"><?php esc_html_e( 'Work queue', 'submission-inbox' ); ?></label>
							<select id="subin-work-queue" name="subin_queue">
								<option value=""><?php esc_html_e( 'All leads', 'submission-inbox' ); ?></option>
								<?php foreach ( $this->queue_definitions() as $queue_key => $queue ) : ?>
									<?php if ( \SUBIN\Leads\LeadRepository::QUEUE_MY_LEADS === $queue_key && get_current_user_id() <= 0 ) : ?>
										<?php continue; ?>
									<?php endif; ?>
									<option value="<?php echo esc_attr( $queue_key ); ?>" <?php selected( $filters['queue'], $queue_key ); ?>><?php echo esc_html( $queue['label'] ); ?></option>
								<?php endforeach; ?>
							</select>
							<p class="description"><?php esc_html_e( 'A work queue replaces visibility, workflow, assignee, and delivery-status filters.', 'submission-inbox' ); ?></p>
						</div>
						<div class="subin-filter-field">
							<label for="subin-date-from"><?php esc_html_e( 'From date', 'submission-inbox' ); ?></label>
							<input type="date" id="subin-date-from" name="subin_date_from" value="<?php echo esc_attr( $filters['date_from'] ); ?>" />
						</div>
						<div class="subin-filter-field">
							<label for="subin-date-to"><?php esc_html_e( 'To date', 'submission-inbox' ); ?></label>
							<input type="date" id="subin-date-to" name="subin_date_to" value="<?php echo esc_attr( $filters['date_to'] ); ?>" />
						</div>
					</div>
				</details>
			</form>
		</div>
		<?php
	}

	/**
	 * Build quick filter chips that reuse the server-side inbox filters.
	 *
	 * @param array<string,mixed> $filters Active filters.
	 * @return array<int,array{label:string,url:string,active:bool,description:string}>
	 */
	private function quick_filter_chips( array $filters ) {
		$current_user_id = get_current_user_id();
		$chips           = array(
			array(
				'label'  => __( 'All leads', 'submission-inbox' ),
				'url'    => $this->list_url( array(), 1 ),
				'active' => ! $this->has_active_filters( $filters ),
				'description' => __( 'Every saved lead.', 'submission-inbox' ),
			),
		);

		foreach ( $this->queue_definitions() as $queue_key => $queue ) {
			if ( \SUBIN\Leads\LeadRepository::QUEUE_MY_LEADS === $queue_key && $current_user_id <= 0 ) {
				continue;
			}

			$queue_filters = $this->filters_for_quick_link(
				$filters,
				array(
					'queue'         => $queue_key,
					'queue_user_id' => \SUBIN\Leads\LeadRepository::QUEUE_MY_LEADS === $queue_key ? $current_user_id : 0,
				)
			);
			$chips[] = array(
				'label'       => $queue['label'],
				'url'         => $this->list_url( $queue_filters, 1 ),
				'active'      => $queue_key === ( $filters['queue'] ?? '' ),
				'description' => $queue['description'],
			);
		}

		return $chips;
	}

	/**
	 * Labels and truthful semantics for the mutually exclusive daily-work queues.
	 *
	 * @return array<string,array{label:string,description:string}>
	 */
	private function queue_definitions() {
		return array(
			\SUBIN\Leads\LeadRepository::QUEUE_UNSEEN => array(
				'label'       => __( 'Unseen', 'submission-inbox' ),
				'description' => __( 'Saved leads that have not been marked as seen.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_AWAITING_ACTION => array(
				'label'       => __( 'Awaiting action', 'submission-inbox' ),
				'description' => __( 'Active leads that are still in the Open workflow stage.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_UNASSIGNED => array(
				'label'       => __( 'Unassigned', 'submission-inbox' ),
				'description' => __( 'Active leads without an owner.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_MY_LEADS => array(
				'label'       => __( 'My leads', 'submission-inbox' ),
				'description' => __( 'Active leads assigned to you.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_DUE_TODAY => array(
				'label'       => __( 'Due today', 'submission-inbox' ),
				'description' => __( 'Open follow-ups due during the current WordPress calendar day.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_OVERDUE => array(
				'label'       => __( 'Overdue', 'submission-inbox' ),
				'description' => __( 'Open follow-ups due before today.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES => array(
				'label'       => __( 'Delivery issues', 'submission-inbox' ),
				'description' => __( 'Active leads with a failed or missing team notification or visitor confirmation.', 'submission-inbox' ),
			),
			\SUBIN\Leads\LeadRepository::QUEUE_ARCHIVED => array(
				'label'       => __( 'Archived', 'submission-inbox' ),
				'description' => __( 'Leads removed from the active work queues.', 'submission-inbox' ),
			),
		);
	}

	/**
	 * Keep low-risk filters while switching primary status chips.
	 *
	 * @param array<string,mixed> $filters Active filters.
	 * @param array<string,mixed> $overrides Filter overrides.
	 * @return array<string,mixed>
	 */
	private function filters_for_quick_link( array $filters, array $overrides ) {
		return array_merge(
			$filters,
			array(
				'queue'              => '',
				'queue_user_id'      => 0,
				'status'             => '',
				'email_status'       => '',
				'auto_reply_status'  => '',
				'needs_attention'    => '',
				'workflow_status'    => '',
				'assigned_user_id'  => '',
				'overdue_follow_up' => '',
			),
			$overrides
		);
	}

	/**
	 * Render lead cards.
	 *
	 * @param array<int,object>  $leads Leads.
	 * @param int                $total Total.
	 * @param array<string,mixed> $filters Filters.
	 * @return void
	 */
	private function render_lead_cards( array $leads, $total, array $filters ) {
		if ( empty( $leads ) ) {
			if ( $this->has_active_filters( $filters ) || $total > 0 ) {
				SUBIN_View::empty_state( __( 'No leads match the current filters', 'submission-inbox' ), __( 'Try changing your filters or clearing the search.', 'submission-inbox' ) );
			} else {
				SUBIN_View::empty_state( __( 'No leads yet', 'submission-inbox' ), __( 'Saved form submissions will appear here after visitors submit your forms.', 'submission-inbox' ) );
			}
			return;
		}
		$items             = $this->build_lead_list_items( $leads );
		$queue_description = $this->active_queue_description( $filters );
		$can_manage        = \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS );
		$assignable_users  = $can_manage ? $this->assignable_users() : array();
		?>
		<div class="subin-inbox-results-bar">
			<div class="subin-inbox-results-count">
				<strong>
					<?php
					echo esc_html(
						sprintf(
							/* translators: %s: total number of matching leads. */
							_n( '%s matching lead', '%s matching leads', absint( $total ), 'submission-inbox' ),
							number_format_i18n( absint( $total ) )
						)
					);
					?>
				</strong>
				<span>
					<?php
					echo esc_html(
						'' !== $queue_description
							? $queue_description
							: ( $can_manage ? __( 'Select leads on this page to update them together.', 'submission-inbox' ) : __( 'Read-only access to saved leads.', 'submission-inbox' ) )
					);
					?>
				</span>
			</div>
			<?php if ( $can_manage ) : ?>
			<form method="post" action="" id="subin-inbox-bulk-form" class="subin-inbox-bulk-form" data-subin-confirm-archive="<?php esc_attr_e( 'Archive the selected leads?', 'submission-inbox' ); ?>" data-subin-confirm-clear-follow-up="<?php esc_attr_e( 'Clear follow-up dates for the selected leads?', 'submission-inbox' ); ?>">
				<input type="hidden" name="subin_lead_action" value="bulk_update" />
				<?php wp_nonce_field( 'subin_bulk_update_leads', 'subin_bulk_nonce' ); ?>
				<label class="screen-reader-text" for="subin-bulk-action"><?php esc_html_e( 'Bulk action', 'submission-inbox' ); ?></label>
				<select id="subin-bulk-action" name="bulk_action" data-subin-bulk-action required>
					<option value=""><?php esc_html_e( 'Bulk action...', 'submission-inbox' ); ?></option>
					<option value="seen"><?php esc_html_e( 'Mark as seen', 'submission-inbox' ); ?></option>
					<option value="new"><?php esc_html_e( 'Mark as unseen / restore', 'submission-inbox' ); ?></option>
					<option value="archived"><?php esc_html_e( 'Archive', 'submission-inbox' ); ?></option>
					<option value="set_workflow"><?php esc_html_e( 'Set workflow stage', 'submission-inbox' ); ?></option>
					<option value="set_priority"><?php esc_html_e( 'Set priority', 'submission-inbox' ); ?></option>
					<option value="assign"><?php esc_html_e( 'Assign / unassign', 'submission-inbox' ); ?></option>
					<option value="set_follow_up"><?php esc_html_e( 'Set follow-up date', 'submission-inbox' ); ?></option>
					<option value="clear_follow_up"><?php esc_html_e( 'Clear follow-up date', 'submission-inbox' ); ?></option>
				</select>
				<span class="subin-inbox-bulk-target" data-subin-bulk-target="set_workflow" hidden>
					<label class="screen-reader-text" for="subin-bulk-workflow"><?php esc_html_e( 'Workflow stage', 'submission-inbox' ); ?></label>
					<select id="subin-bulk-workflow" name="bulk_workflow_status" disabled>
						<?php foreach ( $this->workflow->workflow_statuses() as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</select>
				</span>
				<span class="subin-inbox-bulk-target" data-subin-bulk-target="set_priority" hidden>
					<label class="screen-reader-text" for="subin-bulk-priority"><?php esc_html_e( 'Priority', 'submission-inbox' ); ?></label>
					<select id="subin-bulk-priority" name="bulk_priority" disabled>
						<?php foreach ( $this->workflow->priorities() as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</select>
				</span>
				<span class="subin-inbox-bulk-target" data-subin-bulk-target="assign" hidden>
					<label class="screen-reader-text" for="subin-bulk-assignee"><?php esc_html_e( 'Assignee', 'submission-inbox' ); ?></label>
					<select id="subin-bulk-assignee" name="bulk_assigned_user_id" disabled>
						<option value="0"><?php esc_html_e( 'Unassigned', 'submission-inbox' ); ?></option>
						<?php foreach ( $assignable_users as $user ) : ?>
							<option value="<?php echo esc_attr( (string) $user->ID ); ?>"><?php echo esc_html( $user->display_name ); ?></option>
						<?php endforeach; ?>
					</select>
				</span>
				<span class="subin-inbox-bulk-target" data-subin-bulk-target="set_follow_up" hidden>
					<label class="screen-reader-text" for="subin-bulk-follow-up"><?php esc_html_e( 'Follow-up date', 'submission-inbox' ); ?></label>
					<input id="subin-bulk-follow-up" type="date" name="bulk_follow_up_at" data-subin-bulk-required disabled />
					</span>
					<button type="submit" class="button subin-btn subin-btn-primary" data-subin-bulk-submit disabled aria-disabled="true"><?php esc_html_e( 'Apply', 'submission-inbox' ); ?></button>
					<?php /* translators: %d: number of selected leads. */ ?>
					<span class="subin-inbox-selected-count" data-subin-selected-count data-subin-selected-one="<?php esc_attr_e( '1 selected', 'submission-inbox' ); ?>" data-subin-selected-many="<?php esc_attr_e( '%d selected', 'submission-inbox' ); ?>" aria-live="polite"><?php esc_html_e( '0 selected', 'submission-inbox' ); ?></span>
			</form>
			<?php endif; ?>
		</div>
		<div class="si-list">
			<div class="si-list-head">
				<div class="si-check">
					<?php if ( $can_manage ) : ?>
						<input type="checkbox" data-subin-select-all aria-label="<?php esc_attr_e( 'Select all leads on this page', 'submission-inbox' ); ?>" />
					<?php endif; ?>
				</div>
				<div><?php esc_html_e( 'Lead', 'submission-inbox' ); ?></div>
				<div><?php esc_html_e( 'Message', 'submission-inbox' ); ?></div>
				<div><?php esc_html_e( 'Form', 'submission-inbox' ); ?></div>
				<div><?php esc_html_e( 'Assurance', 'submission-inbox' ); ?></div>
				<div></div>
			</div>
			<?php foreach ( $items as $item ) : ?>
				<?php $this->render_lead_mockup_row( $item, false, $assignable_users ); ?>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Explain the selected work queue next to its result count.
	 *
	 * @param array<string,mixed> $filters Filters.
	 * @return string
	 */
	private function active_queue_description( array $filters ) {
		$queue  = sanitize_key( (string) ( $filters['queue'] ?? '' ) );
		$queues = $this->queue_definitions();
		if ( ! empty( $filters['needs_attention'] ) && ! empty( $filters['overdue_follow_up'] ) ) {
			return __( 'Legacy combined view: delivery issues with follow-up overdue before today.', 'submission-inbox' );
		}
		if ( ! empty( $filters['needs_attention'] ) ) {
			return __( 'Legacy filtered view: delivery issues combined with the selected filters.', 'submission-inbox' );
		}
		if ( ! empty( $filters['overdue_follow_up'] ) ) {
			return __( 'Legacy filtered view: overdue follow-ups combined with the selected filters.', 'submission-inbox' );
		}

		return isset( $queues[ $queue ] ) ? $queues[ $queue ]['description'] : '';
	}

	/**
	 * Build display-ready lead list items.
	 *
	 * @param array<int,object> $leads Leads.
	 * @return array<int,array<string,mixed>>
	 */
	private function build_lead_list_items( array $leads ) {
		$lead_ids          = array_map( 'absint', wp_list_pluck( $leads, 'id' ) );
		$fields_by_lead_id = $this->fields->get_grouped_by_lead_ids( $lead_ids );
		$events_by_lead_id = $this->mail_events->get_grouped_by_lead_ids( $lead_ids );
		$items             = array();

		foreach ( $leads as $lead ) {
			$lead_id  = absint( $lead->id ?? 0 );
			$fields   = $fields_by_lead_id[ $lead_id ] ?? array();
			$events   = $events_by_lead_id[ $lead_id ] ?? array();
			$items[]  = $this->build_lead_list_item( $lead, $fields, $events );
		}

		return $items;
	}

	/**
	 * Build one display-ready lead list item.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $fields Saved lead fields.
	 * @param array<int,object> $events Related email events.
	 * @return array<string,mixed>
	 */
	private function build_lead_list_item( $lead, array $fields, array $events ) {
		$lead_id           = absint( $lead->id );
		$title             = '' !== trim( (string) ( $lead->customer_name ?? '' ) ) ? $lead->customer_name : __( 'No name provided', 'submission-inbox' );
		$preview           = $this->lead_card_preview( $lead, $fields );
		$email_status      = sanitize_key( $lead->email_status ?? 'unknown' );
		$notification      = $this->lead_notification_context( $lead, $events, $email_status );
		$auto_reply_status = $this->normalize_auto_reply_status( $lead->auto_reply_status ?? 'not_configured' );
		$needs_attention   = in_array( $email_status, array( 'failed', 'no_email_generated' ), true )
			|| in_array( $auto_reply_status, array( 'failed', 'no_email_generated' ), true );
		$status_message    = $this->lead_card_email_status_message( $email_status, $notification['recipient'] );
		$form_settings_url = $this->form_settings_url_for_lead( $lead );
		$email_log_url     = $this->email_log_url_for_outcome( $notification['event'], $email_status, sanitize_key( $lead->provider ?? '' ) );
		$auto_reply_url    = $this->auto_reply_url_for_lead( $lead );
		$follow_up_at      = sanitize_text_field( $lead->follow_up_at ?? '' );
		$is_overdue        = '' !== $follow_up_at && strtotime( $follow_up_at ) && strtotime( $follow_up_at ) < current_time( 'timestamp' );
		$visible_fields    = $this->visible_lead_fields( $fields );

		return array(
			'lead'              => $lead,
			'lead_id'           => $lead_id,
			'email_status'      => $email_status,
			'auto_reply_status' => $auto_reply_status,
			'needs_attention'   => $needs_attention,
			'title'             => $title,
			'preview'           => $preview,
			'status_message'    => $status_message,
			'form_settings_url' => $form_settings_url,
			'email_log_url'     => $email_log_url,
			'auto_reply_url'    => $auto_reply_url,
			'follow_up_at'      => $follow_up_at,
			'is_overdue'        => $is_overdue,
			'fields'            => $visible_fields,
			'field_highlights'  => $this->lead_field_highlights( $visible_fields ),
			'primary_meta'      => $this->lead_primary_meta( $lead ),
			'workflow_meta'     => $this->lead_workflow_meta( $lead, $follow_up_at, $is_overdue ),
			'detail_meta'       => $this->lead_detail_meta( $lead, $status_message ),
		);
	}

	/**
	 * Render one lead row using the supplied mockup structure.
	 *
	 * @param array<string,mixed> $item Display-ready lead item.
	 * @param bool                $open Whether row starts expanded.
	 * @param array<int,object>   $assignable_users Users available for assignment.
	 * @return void
	 */
	private function render_lead_mockup_row( array $item, $open = false, array $assignable_users = array() ) {
		$lead              = $item['lead'];
		$lead_id           = absint( $item['lead_id'] );
		$email_status      = sanitize_key( $item['email_status'] );
		$auto_reply_status = $this->normalize_auto_reply_status( $item['auto_reply_status'] ?? 'not_configured' );
		$needs_attention   = ! empty( $item['needs_attention'] );
		$title             = (string) $item['title'];
		$preview           = (string) $item['preview'];
		$status_message    = (string) $item['status_message'];
		$form_settings_url = (string) $item['form_settings_url'];
		$email_log_url     = (string) $item['email_log_url'];
		$auto_reply_url    = (string) $item['auto_reply_url'];
		$contact           = $this->lead_contact_line( $lead );
		$form_name         = $lead->form_name ? sanitize_text_field( $lead->form_name ) : __( 'Unknown form', 'submission-inbox' );
		?>
		<details class="si-row<?php echo $needs_attention ? ' needs-attention' : ''; ?>"<?php echo $open ? ' open' : ''; ?>>
			<summary class="si-row-main">
				<div class="si-check">
					<?php if ( \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS ) ) : ?>
						<?php /* translators: %1$d: lead ID, %2$s: lead title. */ ?>
						<input type="checkbox" name="lead_ids[]" value="<?php echo esc_attr( (string) $lead_id ); ?>" form="subin-inbox-bulk-form" data-subin-lead-select aria-label="<?php echo esc_attr( sprintf( __( 'Select lead #%1$d, %2$s', 'submission-inbox' ), $lead_id, $title ) ); ?>" onclick="event.stopPropagation();" />
						<input type="hidden" name="status_versions[<?php echo esc_attr( (string) $lead_id ); ?>]" value="<?php echo esc_attr( (string) max( 1, absint( $lead->status_version ?? 1 ) ) ); ?>" form="subin-inbox-bulk-form" />
						<input type="hidden" name="workflow_versions[<?php echo esc_attr( (string) $lead_id ); ?>]" value="<?php echo esc_attr( (string) max( 1, absint( $lead->workflow_version ?? 1 ) ) ); ?>" form="subin-inbox-bulk-form" />
					<?php endif; ?>
				</div>

				<div class="si-person">
					<div class="si-name-line">
						<?php if ( 'new' === sanitize_key( $lead->status ?? '' ) ) : ?>
							<span class="si-dot" aria-hidden="true"></span>
							<span class="screen-reader-text"><?php esc_html_e( 'Unseen lead.', 'submission-inbox' ); ?></span>
						<?php endif; ?>
						<span class="si-name"><?php echo esc_html( $title ); ?></span>
					</div>
					<div class="si-email"><?php echo esc_html( '' !== $contact ? $contact : __( 'No contact details', 'submission-inbox' ) ); ?></div>
				</div>

				<div class="si-message">
					<div class="si-subject"><?php echo esc_html( '' !== $preview ? wp_trim_words( $preview, 16 ) : __( 'No message provided.', 'submission-inbox' ) ); ?></div>
					<div class="si-preview"><?php echo esc_html( $this->lead_preview_meta_line( $item ) ); ?></div>
				</div>

				<div class="si-meta">
					<strong><?php echo esc_html( $form_name ); ?></strong>
					<?php echo esc_html( $this->format_date( $lead->submitted_at ) ); ?>
				</div>

				<div class="si-status">
					<?php $this->render_submission_assurance( $email_status, $auto_reply_status, true ); ?>
				</div>

				<div class="si-actions">
					<span class="si-icon-btn">
						<span class="screen-reader-text"><?php esc_html_e( 'Toggle lead preview', 'submission-inbox' ); ?></span>
						<span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
					</span>
				</div>
			</summary>

			<div class="si-row-extra">
				<div class="si-extra-card">
					<div>
						<?php $submitted_fields = $this->compact_submitted_fields( is_array( $item['fields'] ?? null ) ? $item['fields'] : array() ); ?>
						<?php if ( ! empty( $submitted_fields ) ) : ?>
							<h3 class="si-extra-title"><?php esc_html_e( 'Submitted fields', 'submission-inbox' ); ?></h3>
							<?php $this->render_si_field_rows( $submitted_fields ); ?>
						<?php else : ?>
							<h3 class="si-extra-title"><?php esc_html_e( 'Submitted fields', 'submission-inbox' ); ?></h3>
							<div class="si-message-box"><?php esc_html_e( 'No submitted fields to show.', 'submission-inbox' ); ?></div>
						<?php endif; ?>
					</div>

					<div>
						<h3 class="si-extra-title"><?php esc_html_e( 'Details', 'submission-inbox' ); ?></h3>
						<?php $this->render_si_field_rows( $item['detail_meta'] ); ?>
					</div>

					<div class="si-extra-actions">
						<a class="si-small-btn primary" href="<?php echo esc_url( $this->detail_url( $lead_id ) ); ?>"><?php esc_html_e( 'Open lead', 'submission-inbox' ); ?></a>
						<?php if ( 'failed' === $email_status && '' !== $email_log_url ) : ?>
							<a class="si-small-btn" href="<?php echo esc_url( $email_log_url ); ?>"><?php esc_html_e( 'View email error', 'submission-inbox' ); ?></a>
						<?php elseif ( 'no_email_generated' === $email_status && '' !== $form_settings_url ) : ?>
							<a class="si-small-btn" href="<?php echo esc_url( $form_settings_url ); ?>"><?php esc_html_e( 'Check form settings', 'submission-inbox' ); ?></a>
						<?php endif; ?>
						<?php if ( in_array( $auto_reply_status, array( 'failed', 'no_email_generated' ), true ) && '' !== $auto_reply_url ) : ?>
							<a class="si-small-btn" href="<?php echo esc_url( $auto_reply_url ); ?>"><?php esc_html_e( 'Review visitor confirmation', 'submission-inbox' ); ?></a>
						<?php endif; ?>
						<?php if ( 'new' === sanitize_key( $lead->status ?? '' ) ) : ?>
							<?php $this->render_status_action_button( $lead_id, 'seen', __( 'Mark seen', 'submission-inbox' ), 'si-small-btn', '', 'inbox', $lead->status_version ?? 1 ); ?>
						<?php elseif ( 'archived' === sanitize_key( $lead->status ?? '' ) ) : ?>
							<?php $this->render_status_action_button( $lead_id, 'new', __( 'Restore', 'submission-inbox' ), 'si-small-btn', '', 'inbox', $lead->status_version ?? 1 ); ?>
						<?php else : ?>
							<?php $this->render_status_action_button( $lead_id, 'new', __( 'Mark unseen', 'submission-inbox' ), 'si-small-btn', '', 'inbox', $lead->status_version ?? 1 ); ?>
						<?php endif; ?>
						<?php if ( 'archived' !== sanitize_key( $lead->status ?? '' ) ) : ?>
							<?php $this->render_status_action_button( $lead_id, 'archived', __( 'Archive', 'submission-inbox' ), 'si-small-btn', '', 'inbox', $lead->status_version ?? 1 ); ?>
						<?php endif; ?>
					</div>
					<?php if ( \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS ) ) : ?>
						<?php $this->render_inline_workflow_form( $lead, $assignable_users ); ?>
					<?php endif; ?>
				</div>
			</div>
		</details>
		<?php
	}

	/**
	 * Render a compact workflow editor inside an expanded Inbox row.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $users Assignable users.
	 * @return void
	 */
	private function render_inline_workflow_form( $lead, array $users ) {
		$lead_id         = absint( $lead->id ?? 0 );
		$control_prefix  = 'subin-inline-' . $lead_id . '-';
		$follow_up_value = $this->follow_up_date_value( $lead->follow_up_at ?? '' );
		$users           = $this->include_legacy_assignee( $users, absint( $lead->assigned_user_id ?? 0 ) );
		?>
		<div class="si-inline-workflow" data-subin-inline-workflow>
			<h3 class="si-extra-title"><?php esc_html_e( 'Update workflow', 'submission-inbox' ); ?></h3>
			<form method="post" action="" class="si-inline-workflow__form">
				<input type="hidden" name="subin_lead_action" value="update_workflow" />
				<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
				<input type="hidden" name="workflow_version" value="<?php echo esc_attr( (string) max( 1, absint( $lead->workflow_version ?? 1 ) ) ); ?>" />
				<input type="hidden" name="subin_return_to" value="inbox" />
				<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
				<label for="<?php echo esc_attr( $control_prefix . 'workflow' ); ?>">
					<span><?php esc_html_e( 'Stage', 'submission-inbox' ); ?></span>
					<select id="<?php echo esc_attr( $control_prefix . 'workflow' ); ?>" name="workflow_status">
						<?php foreach ( $this->workflow->workflow_statuses() as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>" <?php selected( sanitize_key( $lead->workflow_status ?? 'open' ), $key ); ?>><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</select>
				</label>
				<label for="<?php echo esc_attr( $control_prefix . 'priority' ); ?>">
					<span><?php esc_html_e( 'Priority', 'submission-inbox' ); ?></span>
					<select id="<?php echo esc_attr( $control_prefix . 'priority' ); ?>" name="priority">
						<?php foreach ( $this->workflow->priorities() as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>" <?php selected( sanitize_key( $lead->priority ?? 'normal' ), $key ); ?>><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</select>
				</label>
				<label for="<?php echo esc_attr( $control_prefix . 'assignee' ); ?>">
					<span><?php esc_html_e( 'Assignee', 'submission-inbox' ); ?></span>
					<select id="<?php echo esc_attr( $control_prefix . 'assignee' ); ?>" name="assigned_user_id">
						<option value="0" <?php selected( absint( $lead->assigned_user_id ?? 0 ), 0 ); ?>><?php esc_html_e( 'Unassigned', 'submission-inbox' ); ?></option>
						<?php foreach ( $users as $user ) : ?>
							<option value="<?php echo esc_attr( (string) $user->ID ); ?>" <?php selected( absint( $lead->assigned_user_id ?? 0 ), absint( $user->ID ) ); ?>><?php echo esc_html( $user->display_name ); ?></option>
						<?php endforeach; ?>
					</select>
				</label>
				<label for="<?php echo esc_attr( $control_prefix . 'follow-up' ); ?>">
					<span><?php esc_html_e( 'Follow-up', 'submission-inbox' ); ?></span>
					<input id="<?php echo esc_attr( $control_prefix . 'follow-up' ); ?>" type="date" name="follow_up_at" value="<?php echo esc_attr( $follow_up_value ); ?>" />
				</label>
				<button type="submit" class="button subin-btn subin-btn-primary"><?php esc_html_e( 'Save', 'submission-inbox' ); ?></button>
			</form>
		</div>
		<?php
	}

	/**
	 * Build compact contact line for the mockup row.
	 *
	 * @param object $lead Lead row.
	 * @return string
	 */
	private function lead_contact_line( $lead ) {
		$parts = array();

		if ( ! empty( $lead->customer_email ) ) {
			$parts[] = sanitize_text_field( (string) $lead->customer_email );
		}

		if ( ! empty( $lead->customer_phone ) ) {
			$parts[] = sanitize_text_field( (string) $lead->customer_phone );
		}

		return implode( ' · ', $parts );
	}

	/**
	 * Build the second line of compact lead metadata.
	 *
	 * @param array<string,mixed> $item Display-ready lead item.
	 * @return string
	 */
	private function lead_preview_meta_line( array $item ) {
		$parts = array();

		foreach ( is_array( $item['field_highlights'] ?? null ) ? $item['field_highlights'] : array() as $field ) {
			$parts[] = sanitize_text_field( $field['label'] ?? '' ) . ': ' . sanitize_text_field( $field['value'] ?? '' );
		}

		foreach ( is_array( $item['workflow_meta'] ?? null ) ? $item['workflow_meta'] : array() as $meta ) {
			$parts[] = sanitize_text_field( $meta['label'] ?? '' ) . ': ' . sanitize_text_field( $meta['value'] ?? '' );
		}

		return implode( ' · ', array_filter( $parts ) );
	}

	/**
	 * Render the three independent submission assurance signals.
	 *
	 * @param string $admin_status Team notification status.
	 * @param string $auto_reply_status Visitor confirmation status.
	 * @param bool   $compact Whether to use the compact Inbox-row layout.
	 * @return void
	 */
	private function render_submission_assurance( $admin_status, $auto_reply_status, $compact = false ) {
		$admin_status      = sanitize_key( (string) $admin_status );
		$auto_reply_status = $this->normalize_auto_reply_status( $auto_reply_status );
		$signals           = array(
			array(
				'label'  => __( 'Submission', 'submission-inbox' ),
				'value'  => __( 'Saved', 'submission-inbox' ),
				'status' => 'sent',
			),
			array(
				'label'  => __( 'Team', 'submission-inbox' ),
				'value'  => $this->email_status_label( $admin_status, true ),
				'status' => $admin_status,
			),
			array(
				'label'  => __( 'Visitor', 'submission-inbox' ),
				'value'  => $this->auto_reply_status_label( $auto_reply_status, true ),
				'status' => $auto_reply_status,
			),
		);
		?>
		<div class="subin-assurance<?php echo $compact ? ' subin-assurance--compact' : ''; ?>" role="list" aria-label="<?php esc_attr_e( 'Submission assurance', 'submission-inbox' ); ?>">
			<?php foreach ( $signals as $signal ) : ?>
				<div class="subin-assurance__signal is-<?php echo esc_attr( $this->assurance_tone( $signal['status'] ) ); ?>" role="listitem" aria-label="<?php echo esc_attr( $signal['label'] . ': ' . $signal['value'] ); ?>">
					<span class="subin-assurance__dot" aria-hidden="true"></span>
					<span class="subin-assurance__label"><?php echo esc_html( $signal['label'] ); ?></span>
					<strong><?php echo esc_html( $signal['value'] ); ?></strong>
				</div>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Render compact label/value rows in the expanded mockup card.
	 *
	 * @param array<int,array{label:string,value:string}> $rows Rows.
	 * @return void
	 */
	private function render_si_field_rows( array $rows ) {
		foreach ( $rows as $row ) {
			if ( empty( $row['label'] ) || '' === trim( (string) ( $row['value'] ?? '' ) ) ) {
				continue;
			}
			?>
			<div class="si-field">
				<span><?php echo esc_html( $row['label'] ); ?></span>
				<strong><?php echo esc_html( $row['value'] ); ?></strong>
			</div>
			<?php
		}
	}

	/**
	 * Prepare submitted fields for the compact expanded inbox card.
	 *
	 * @param array<int,array{label:string,value:string}> $fields Visible submitted fields.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function compact_submitted_fields( array $fields ) {
		$regular = array();
		$message = array();

		foreach ( $fields as $field ) {
			$label = sanitize_text_field( $field['label'] ?? '' );
			$value = trim( sanitize_textarea_field( (string) ( $field['value'] ?? '' ) ) );
			if ( '' === $label || '' === $value ) {
				continue;
			}

			$row = array(
				'label' => $label,
				'value' => wp_trim_words( $value, 12, '...' ),
			);

			if ( 'message' === strtolower( $label ) ) {
				$message[] = $row;
			} else {
				$regular[] = $row;
			}
		}

		$rows = array_merge( $regular, $message );

		return array_slice( $rows, 0, self::DETAIL_FIELD_LIMIT );
	}

	/**
	 * Render compact expandable lead details for a table row.
	 *
	 * @param array<string,mixed> $item Display-ready lead item.
	 * @return void
	 */
	private function render_lead_row_details( array $item ) {
		$fields          = is_array( $item['fields'] ?? null ) ? $item['fields'] : array();
		$display_fields  = array_slice( $fields, 0, self::DETAIL_FIELD_LIMIT );
		$remaining_count = max( 0, count( $fields ) - count( $display_fields ) );
		?>
		<details class="subin-status-block">
			<summary class="subin-action-chip"><?php esc_html_e( 'Details', 'submission-inbox' ); ?></summary>
			<?php $this->render_lead_meta_list( $item['detail_meta'] ); ?>
			<?php if ( ! empty( $display_fields ) ) : ?>
				<?php $this->render_lead_meta_list( $display_fields ); ?>
			<?php endif; ?>
			<?php if ( $remaining_count > 0 ) : ?>
				<?php
				$this->render_lead_meta_list(
					array(
						array(
							'label' => __( 'More fields', 'submission-inbox' ),
							'value' => sprintf(
								/* translators: %d: number of hidden submitted fields. */
								_n( '%d additional field is available in the lead detail.', '%d additional fields are available in the lead detail.', $remaining_count, 'submission-inbox' ),
								$remaining_count
							),
						),
					)
				);
				?>
			<?php endif; ?>
		</details>
		<?php
	}

	/**
	 * Render a compact meta list.
	 *
	 * @param array<int,array{label:string,value:string,class?:string}> $items Meta items.
	 * @param string                                                    $extra_class Optional class.
	 * @return void
	 */
	private function render_lead_meta_list( array $items, $extra_class = '' ) {
		if ( empty( $items ) ) {
			return;
		}

		$class = 'subin-lead-card__meta';
		if ( '' !== $extra_class ) {
			$class .= ' ' . sanitize_html_class( $extra_class );
		}
		?>
		<div class="<?php echo esc_attr( $class ); ?>">
			<?php foreach ( $items as $item ) : ?>
				<?php
				$item_class = ! empty( $item['class'] ) ? ' class="' . esc_attr( sanitize_html_class( $item['class'] ) ) . '"' : '';
				?>
				<span<?php echo $item_class; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built from sanitize_html_class() above. ?>>
					<?php echo esc_html( $item['label'] ); ?>:
					<strong><?php echo esc_html( $item['value'] ); ?></strong>
				</span>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Get the best compact lead preview for the inbox card.
	 *
	 * @param object $lead Lead row.
	 * @param array<int,object>|null $fields Saved lead fields.
	 * @return string
	 */
	private function lead_card_preview( $lead, ?array $fields = null ) {
		if ( '' !== trim( (string) ( $lead->message_preview ?? '' ) ) ) {
			return sanitize_textarea_field( (string) $lead->message_preview );
		}

		if ( null === $fields ) {
			$fields = $this->fields->get_by_lead_id( absint( $lead->id ?? 0 ) );
		}

		foreach ( $fields as $field ) {
			if ( ! empty( $field->is_sensitive ) || '' === trim( (string) $field->field_value ) || $this->is_internal_field( $field ) || $this->is_contact_preview_field( $field ) ) {
				continue;
			}

			return sanitize_textarea_field( (string) $field->field_value );
		}

		return '';
	}

	/**
	 * Filter saved fields for compact inbox details.
	 *
	 * @param array<int,object> $fields Saved lead fields.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function visible_lead_fields( array $fields ) {
		$visible = array();

		foreach ( $fields as $field ) {
			if ( ! empty( $field->is_sensitive ) || '' === trim( (string) $field->field_value ) || $this->is_internal_field( $field ) ) {
				continue;
			}

			$label     = '' !== trim( (string) ( $field->field_label ?? '' ) ) ? (string) $field->field_label : (string) ( $field->field_key ?? '' );
			$visible[] = array(
				'label' => sanitize_text_field( $label ),
				'value' => sanitize_textarea_field( (string) $field->field_value ),
			);
		}

		return $visible;
	}

	/**
	 * Pick a small set of submitted fields for the inbox card summary.
	 *
	 * @param array<int,array{label:string,value:string}> $fields Visible fields.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function lead_field_highlights( array $fields ) {
		$highlights = array();

		foreach ( $fields as $field ) {
			$label = strtolower( (string) ( $field['label'] ?? '' ) );
			if ( $this->compact_field_is_identity_or_message( $label ) ) {
				continue;
			}

			$highlights[] = array(
				'label' => sanitize_text_field( $field['label'] ),
				'value' => wp_trim_words( sanitize_text_field( $field['value'] ), 8 ),
			);

			if ( count( $highlights ) >= self::HIGHLIGHT_FIELD_LIMIT ) {
				break;
			}
		}

		return $highlights;
	}

	/**
	 * Build stable primary meta for a lead card.
	 *
	 * @param object $lead Lead row.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function lead_primary_meta( $lead ) {
		return array(
			array(
				'label' => __( 'Form', 'submission-inbox' ),
				'value' => $lead->form_name ? sanitize_text_field( $lead->form_name ) : __( 'Unknown form', 'submission-inbox' ),
			),
			array(
				'label' => __( 'Submitted', 'submission-inbox' ),
				'value' => $this->format_date( $lead->submitted_at ),
			),
		);
	}

	/**
	 * Build workflow meta for a lead card.
	 *
	 * @param object $lead Lead row.
	 * @param string $follow_up_at Follow-up datetime.
	 * @param bool   $is_overdue Whether follow-up is overdue.
	 * @return array<int,array{label:string,value:string,class?:string}>
	 */
	private function lead_workflow_meta( $lead, $follow_up_at, $is_overdue ) {
		$items = array(
			array(
				'label' => __( 'Workflow', 'submission-inbox' ),
				'value' => $this->workflow_status_label( $lead->workflow_status ?? 'open' ),
			),
			array(
				'label' => __( 'Priority', 'submission-inbox' ),
				'value' => $this->priority_label( $lead->priority ?? 'normal' ),
			),
			array(
				'label' => __( 'Assignee', 'submission-inbox' ),
				'value' => $this->assignee_label( $lead->assigned_user_id ?? 0 ),
			),
		);

		if ( '' !== $follow_up_at ) {
			$items[] = array(
				'label' => __( 'Follow-up', 'submission-inbox' ),
				'value' => $this->format_date( $follow_up_at ),
				'class' => $is_overdue ? 'subin-workflow-overdue' : '',
			);
		}

		return $items;
	}

	/**
	 * Build expandable detail meta for a lead card.
	 *
	 * @param object $lead Lead row.
	 * @param string $status_message Email health summary.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function lead_detail_meta( $lead, $status_message ) {
		return array(
			array(
				'label' => __( 'Source', 'submission-inbox' ),
				'value' => $this->provider_label( $lead->provider ?? '' ),
			),
			array(
				'label' => __( 'Workflow', 'submission-inbox' ),
				'value' => $this->workflow_status_label( $lead->workflow_status ?? 'open' ),
			),
			array(
				'label' => __( 'Priority', 'submission-inbox' ),
				'value' => $this->priority_label( $lead->priority ?? 'normal' ),
			),
			array(
				'label' => __( 'Assignee', 'submission-inbox' ),
				'value' => $this->assignee_label( $lead->assigned_user_id ?? 0 ),
			),
			array(
				'label' => __( 'Email health', 'submission-inbox' ),
				'value' => sanitize_text_field( $status_message ),
			),
		);
	}

	/**
	 * Whether a compact field duplicates identity/contact/message content.
	 *
	 * @param string $label Field label.
	 * @return bool
	 */
	private function compact_field_is_identity_or_message( $label ) {
		foreach ( array( 'name', 'email', 'e-mail', 'phone', 'tel', 'mobile', 'message', 'comments', 'comment', 'enquiry', 'inquiry', 'details' ) as $needle ) {
			if ( false !== strpos( $label, $needle ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Whether a field duplicates the card identity/contact line.
	 *
	 * @param object $field Field row.
	 * @return bool
	 */
	private function is_contact_preview_field( $field ) {
		$key   = strtolower( (string) ( $field->field_key ?? '' ) );
		$label = strtolower( (string) ( $field->field_label ?? '' ) );
		$text  = $key . ' ' . $label;

		foreach ( array( 'name', 'email', 'e-mail', 'phone', 'tel', 'mobile' ) as $needle ) {
			if ( false !== strpos( $text, $needle ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Get compact notification context for an inbox card.
	 *
	 * @param object                 $lead Lead row.
	 * @param array<int,object>|null $events Related email events.
	 * @param string                 $preferred_status Projected status to match.
	 * @return array{event:object|null,recipient:string}
	 */
	private function lead_notification_context( $lead, ?array $events = null, $preferred_status = '' ) {
		if ( null === $events ) {
			$events = $this->mail_events->get_by_lead_id( absint( $lead->id ?? 0 ) );
		}

		$event  = $this->latest_admin_notification_event( $events, $preferred_status );
		if ( $event && '' !== trim( (string) ( $event->to_email ?? '' ) ) ) {
			return array(
				'event'     => $event,
				'recipient' => sanitize_text_field( (string) $event->to_email ),
			);
		}

		$recipient = $this->notification_recipient_details_for_lead( $lead );
		return array(
			'event'     => $event,
			'recipient' => sanitize_text_field( $recipient['email'] ?? '' ),
		);
	}

	/**
	 * Message for the compact email status card.
	 *
	 * @param string $status Email status.
	 * @param string $recipient Recipient email.
	 * @return string
	 */
	private function lead_card_email_status_message( $status, $recipient = '' ) {
		$status    = sanitize_key( $status );
		$recipient = sanitize_text_field( $recipient );

		if ( 'sent' === $status && '' !== $recipient ) {
			return sprintf(
				/* translators: %s: notification recipient email. */
				__( 'WordPress accepted the admin notification to %s; delivery is not verified.', 'submission-inbox' ),
				$recipient
			);
		}

		$messages = array(
			'sent'               => __( 'Admin notification accepted by WordPress; delivery is not verified.', 'submission-inbox' ),
			'failed'             => __( 'Lead is saved, but the notification email failed.', 'submission-inbox' ),
			'no_email_generated' => __( 'This form submission was saved, but no notification email was created.', 'submission-inbox' ),
			'unknown'            => __( 'Lead is saved, but the notification status is unknown.', 'submission-inbox' ),
			'pending'            => __( 'Lead is saved, but the notification status is unknown.', 'submission-inbox' ),
		);

		return $messages[ $status ] ?? $messages['unknown'];
	}

	/**
	 * Render pagination.
	 *
	 * @param int    $total Total rows.
	 * @param int    $paged Current page.
	 * @param array<string,mixed> $filters Filters.
	 * @return void
	 */
	private function render_pagination( $total, $paged, array $filters ) {
		$total_pages = max( 1, (int) ceil( $total / self::PER_PAGE ) );

		if ( $total_pages <= 1 ) {
			return;
		}

		echo '<nav class="subin-pagination" aria-label="' . esc_attr__( 'Inbox pagination', 'submission-inbox' ) . '">';
		/* translators: 1: current page number, 2: total page count. */
		echo '<span class="subin-pagination__info">' . esc_html( sprintf( __( 'Page %1$d of %2$d', 'submission-inbox' ), absint( $paged ), absint( $total_pages ) ) ) . '</span>';
		echo '<div class="subin-pagination__links">';
		echo wp_kses_post(
			paginate_links(
				array(
					'base'      => add_query_arg( 'paged', '%#%', $this->list_url( $filters, 1 ) ),
					'format'    => '',
					'current'   => $paged,
					'total'     => $total_pages,
					'prev_text' => __( 'Previous', 'submission-inbox' ),
					'next_text' => __( 'Next', 'submission-inbox' ),
				)
			)
		);
		echo '</div></nav>';
	}

	/**
	 * Find adjacent leads in the current filtered and sorted list context.
	 *
	 * @param int $lead_id Current lead id.
	 * @return array{previous:int,next:int}
	 */
	private function contextual_lead_navigation( $lead_id ) {
		$filters = $this->get_filters_from_request();
		$paged   = max( 1, SUBIN_Request::get_absint( 'paged', 1 ) );
		$offset  = ( $paged - 1 ) * self::PER_PAGE;
		$leads   = $this->leads->get_paginated(
			array_merge(
				$filters,
				array(
					'per_page' => self::PER_PAGE,
					'offset'   => $offset,
				)
			)
		);
		$lead_ids = array_map( 'absint', wp_list_pluck( $leads, 'id' ) );
		$position = array_search( absint( $lead_id ), $lead_ids, true );
		$result   = array(
			'previous' => 0,
			'next'     => 0,
		);

		if ( false === $position ) {
			return $result;
		}

		if ( $position > 0 ) {
			$result['previous'] = $lead_ids[ $position - 1 ];
		} elseif ( $offset > 0 ) {
			$previous = $this->leads->get_paginated(
				array_merge(
					$filters,
					array(
						'per_page' => 1,
						'offset'   => $offset - 1,
					)
				)
			);
			$result['previous'] = ! empty( $previous ) ? absint( $previous[0]->id ?? 0 ) : 0;
		}

		if ( $position < count( $lead_ids ) - 1 ) {
			$result['next'] = $lead_ids[ $position + 1 ];
		} elseif ( count( $lead_ids ) === self::PER_PAGE ) {
			$next = $this->leads->get_paginated(
				array_merge(
					$filters,
					array(
						'per_page' => 1,
						'offset'   => $offset + self::PER_PAGE,
					)
				)
			);
			$result['next'] = ! empty( $next ) ? absint( $next[0]->id ?? 0 ) : 0;
		}

		return $result;
	}

	/**
	 * Render lead detail.
	 *
	 * @param int $lead_id Lead id.
	 * @return void
	 */
	private function render_detail( $lead_id ) {
		$lead = $this->leads->get( $lead_id );

		if ( ! $lead ) {
			wp_die( esc_html__( 'Lead not found.', 'submission-inbox' ) );
		}

		$fields            = $this->fields->get_by_lead_id( $lead_id );
		$events            = $this->mail_events->get_related_events_for_lead( $lead_id, 10 );
		$notes             = $this->activity->get_notes( $lead_id );
		$timeline          = $this->activity->get_timeline( $lead_id, 50 );
		$back_url          = $this->current_list_url();
		$email_display     = sanitize_text_field( (string) ( $lead->customer_email ?? '' ) );
		$email_address     = sanitize_email( $email_display );
		$phone_display     = sanitize_text_field( (string) ( $lead->customer_phone ?? '' ) );
		$phone_number      = preg_replace( '/[^0-9+]/', '', $phone_display );
		$navigation        = $this->contextual_lead_navigation( $lead_id );
		$admin_status      = sanitize_key( $lead->email_status ?? 'unknown' );
		$auto_reply_status = $this->normalize_auto_reply_status( $lead->auto_reply_status ?? 'not_configured' );
		$secondary_actions = array(
			array(
				'label' => __( 'Back to Inbox', 'submission-inbox' ),
				'url'   => $back_url,
			),
		);
		if ( ! empty( $navigation['previous'] ) ) {
			$secondary_actions[] = array(
				'label' => __( 'Previous lead', 'submission-inbox' ),
				'url'   => $this->detail_url( $navigation['previous'] ),
			);
		}
		if ( ! empty( $navigation['next'] ) ) {
			$secondary_actions[] = array(
				'label' => __( 'Next lead', 'submission-inbox' ),
				'url'   => $this->detail_url( $navigation['next'] ),
			);
		}

		SUBIN_View::page_header(
			__( 'Lead detail', 'submission-inbox' ),
			__( 'Review the saved form submission, contact details, and related WordPress email events.', 'submission-inbox' ),
			array(
				'secondary_actions' => $secondary_actions,
				'wrapper_class'     => 'subin-lead-detail-page',
			)
		);

		$this->maybe_add_detail_notice();
		SUBIN_AdminNotices::render_settings_errors( 'subin_messages' );
		?>
		<section class="subin-lead-summary-card">
			<div class="subin-lead-summary-main">
				<div>
					<div class="subin-lead-summary-badges">
						<?php $this->status_badge( $lead->status, $this->lead_status_label( $lead->status ) ); ?>
					</div>
					<h2><?php echo esc_html( $lead->customer_name ? $lead->customer_name : __( 'No name provided', 'submission-inbox' ) ); ?></h2>
					<div class="subin-lead-card__contact">
						<?php if ( is_email( $email_address ) ) : ?>
							<a class="subin-contact-action" href="mailto:<?php echo esc_attr( $email_address ); ?>">
								<span class="dashicons dashicons-email-alt" aria-hidden="true"></span>
								<?php echo esc_html( $email_display ); ?>
							</a>
						<?php elseif ( '' !== $email_display ) : ?>
							<span><?php echo esc_html( $email_display ); ?></span>
						<?php endif; ?>
						<?php if ( '' !== $phone_display && '' !== $phone_number ) : ?>
							<a class="subin-contact-action" href="tel:<?php echo esc_attr( $phone_number ); ?>">
								<span class="dashicons dashicons-phone" aria-hidden="true"></span>
								<?php echo esc_html( $phone_display ); ?>
							</a>
						<?php endif; ?>
					</div>
					<div class="subin-lead-summary-context">
						<?php /* translators: %d: lead ID. */ ?>
						<?php echo esc_html( sprintf( __( 'Lead #%d', 'submission-inbox' ), $lead_id ) ); ?>
						<span aria-hidden="true">·</span>
						<?php echo esc_html( $lead->form_name ? $lead->form_name : __( 'Unknown form', 'submission-inbox' ) ); ?>
						<span aria-hidden="true">·</span>
						<?php echo esc_html( $this->format_date( $lead->submitted_at ) ); ?>
					</div>
				</div>
				<?php if ( \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS ) ) : ?>
				<div class="subin-lead-summary-actions">
					<?php if ( 'new' === sanitize_key( $lead->status ?? '' ) ) : ?>
						<?php $this->render_status_action_button( $lead_id, 'seen', __( 'Mark as seen', 'submission-inbox' ), 'subin-action-chip', '', '', $lead->status_version ?? 1 ); ?>
					<?php elseif ( 'archived' === sanitize_key( $lead->status ?? '' ) ) : ?>
						<?php $this->render_status_action_button( $lead_id, 'new', __( 'Restore lead', 'submission-inbox' ), 'subin-action-chip', '', '', $lead->status_version ?? 1 ); ?>
					<?php else : ?>
						<?php $this->render_status_action_button( $lead_id, 'new', __( 'Mark as unseen', 'submission-inbox' ), 'subin-action-chip', '', '', $lead->status_version ?? 1 ); ?>
					<?php endif; ?>
					<?php if ( 'archived' !== sanitize_key( $lead->status ?? '' ) ) : ?>
						<?php $this->render_status_action_button( $lead_id, 'archived', __( 'Archive', 'submission-inbox' ), 'subin-action-chip', '', '', $lead->status_version ?? 1 ); ?>
					<?php endif; ?>
				</div>
				<?php endif; ?>
			</div>
			<?php $this->render_submission_assurance( $admin_status, $auto_reply_status ); ?>
		</section>
		<div class="subin-lead-detail-layout subin-lead-detail-layout--focused">
			<div class="subin-lead-detail-main">
				<section class="subin-detail-panel subin-submitted-details subin-submitted-details--primary">
					<h2>
						<?php esc_html_e( 'Submitted details', 'submission-inbox' ); ?>
						<?php SUBIN_View::help_tooltip( __( 'Fields submitted by the visitor. Sensitive/internal fields are not shown.', 'submission-inbox' ) ); ?>
					</h2>
					<?php $this->render_fields( $fields ); ?>
				</section>
				<section class="subin-detail-panel subin-notes-panel">
					<h2>
						<?php esc_html_e( 'Internal note', 'submission-inbox' ); ?>
						<?php SUBIN_View::help_tooltip( __( 'Private admin notes for this lead. Visitors do not see these notes.', 'submission-inbox' ) ); ?>
					</h2>
					<?php $this->render_notes_panel( $lead, $notes ); ?>
				</section>
			</div>
			<aside class="subin-lead-detail-sidebar">
				<section class="subin-detail-panel subin-lead-details-panel">
					<h2>
						<?php esc_html_e( 'Lead details', 'submission-inbox' ); ?>
						<?php SUBIN_View::help_tooltip( __( 'Current workflow, priority, assignee, and source for this lead.', 'submission-inbox' ) ); ?>
					</h2>
					<?php $this->render_lead_details_summary( $lead ); ?>
				</section>
				<details class="subin-detail-panel subin-detail-drawer subin-email-summary" open>
					<summary>
						<span class="subin-detail-drawer__label">
							<span><?php esc_html_e( 'Email summary', 'submission-inbox' ); ?></span>
						</span>
					</summary>
					<?php $this->render_email_summary( $lead, $events ); ?>
				</details>
				<?php if ( \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS ) ) : ?>
				<details class="subin-detail-panel subin-detail-drawer subin-workflow-panel">
					<summary>
						<span class="subin-detail-drawer__label">
							<span><?php esc_html_e( 'Edit workflow', 'submission-inbox' ); ?></span>
						</span>
					</summary>
					<?php $this->render_workflow_panel( $lead ); ?>
				</details>
				<?php endif; ?>
				<details class="subin-detail-panel subin-detail-drawer subin-source-card">
					<summary>
						<span class="subin-detail-drawer__label">
							<span><?php esc_html_e( 'Source', 'submission-inbox' ); ?></span>
						</span>
					</summary>
					<?php $this->render_source_details( $lead, $fields ); ?>
				</details>
				<details class="subin-detail-panel subin-detail-drawer">
					<summary>
						<span class="subin-detail-drawer__label">
							<span><?php esc_html_e( 'Email activity', 'submission-inbox' ); ?></span>
						</span>
					</summary>
					<?php $this->render_email_activity( $lead, $events ); ?>
				</details>
				<details class="subin-detail-panel subin-detail-drawer">
					<summary>
						<span class="subin-detail-drawer__label">
							<span><?php esc_html_e( 'Activity', 'submission-inbox' ); ?></span>
						</span>
					</summary>
					<?php $this->render_timeline_panel( $timeline ); ?>
				</details>
			</aside>
		</div>
		<?php
		SUBIN_View::page_footer();
	}

	/**
	 * Render compact lead metadata for the detail sidebar.
	 *
	 * @param object $lead Lead row.
	 * @return void
	 */
	private function render_lead_details_summary( $lead ) {
		$assigned_user_id = absint( $lead->assigned_user_id ?? 0 );
		$assigned_label   = __( 'Unassigned', 'submission-inbox' );
		$follow_up_at     = sanitize_text_field( $lead->follow_up_at ?? '' );
		$follow_up_time   = '' !== $follow_up_at ? strtotime( $follow_up_at ) : false;
		$is_overdue       = false !== $follow_up_time && $follow_up_time < current_time( 'timestamp' );
		$follow_up_label  = false !== $follow_up_time ? $this->format_date( $follow_up_at ) : __( 'Not scheduled', 'submission-inbox' );
		if ( $assigned_user_id > 0 ) {
			$user = get_userdata( $assigned_user_id );
			if ( $user ) {
				$assigned_label = $user->display_name;
			}
		}
		?>
		<div class="subin-side-list">
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Workflow', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $this->workflow->workflow_statuses()[ sanitize_key( $lead->workflow_status ?? 'open' ) ] ?? __( 'Open', 'submission-inbox' ) ); ?></strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Priority', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $this->workflow->priorities()[ sanitize_key( $lead->priority ?? 'normal' ) ] ?? __( 'Normal', 'submission-inbox' ) ); ?></strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Assignee', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $assigned_label ); ?></strong>
			</div>
			<div class="subin-side-list__item<?php echo $is_overdue ? ' is-overdue' : ''; ?>">
				<span><?php esc_html_e( 'Follow-up', 'submission-inbox' ); ?></span>
				<strong>
					<?php echo esc_html( $follow_up_label ); ?>
					<?php if ( $is_overdue ) : ?>
						<small><?php esc_html_e( 'Overdue', 'submission-inbox' ); ?></small>
					<?php endif; ?>
				</strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Source', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $this->provider_label( $lead->provider ) ); ?></strong>
			</div>
		</div>
		<?php
	}

	/**
	 * Render fields list.
	 *
	 * @param array<int,object> $fields Fields.
	 * @return void
	 */
	private function render_fields( array $fields ) {
		if ( empty( $fields ) ) {
			SUBIN_View::empty_state( __( 'No submitted fields', 'submission-inbox' ), __( 'This lead was saved without visible form field values.', 'submission-inbox' ) );
			return;
		}
		$display_fields = array();
		$message_fields = array();

		foreach ( $fields as $field ) {
			if ( ! empty( $field->is_sensitive ) || '' === trim( (string) $field->field_value ) || $this->is_internal_field( $field ) ) {
				continue;
			}

			if ( $this->field_looks_like_message( $field ) ) {
				$message_fields[] = $field;
			} else {
				$display_fields[] = $field;
			}
		}

		$display_fields = array_merge( $display_fields, $message_fields );

		if ( empty( $display_fields ) ) {
			SUBIN_View::empty_state( __( 'No submitted fields', 'submission-inbox' ), __( 'This lead was saved without visible form field values.', 'submission-inbox' ) );
			return;
		}
		?>
		<div class="subin-submitted-details__table" role="table" aria-label="<?php esc_attr_e( 'Submitted details', 'submission-inbox' ); ?>">
			<?php foreach ( $display_fields as $field ) : ?>
				<?php
				$value       = (string) $field->field_value;
				$field_label = '' !== trim( (string) $field->field_label ) ? $field->field_label : $field->field_key;
				?>
				<div class="subin-submitted-details__row" role="row">
					<div class="subin-submitted-details__label" role="rowheader"><?php echo esc_html( $field_label ); ?></div>
					<div class="subin-submitted-details__value" role="cell"><?php echo nl2br( esc_html( $value ) ); ?></div>
				</div>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Render compact source details.
	 *
	 * @param object $lead Lead row.
	 * @return void
	 */
	private function render_source_details( $lead, array $fields = array() ) {
		$provider       = sanitize_key( $lead->provider ?? '' );
		$entry_id       = '';
		$form_id_label  = __( 'Form ID', 'submission-inbox' );
		$entry_id_label = '';

		if ( 'wpforms' === $provider ) {
			$entry_id       = $this->external_entry_id_from_fields( $fields, '_wpforms_entry_id' );
			$form_id_label  = __( 'WPForms form ID', 'submission-inbox' );
			$entry_id_label = __( 'WPForms entry ID', 'submission-inbox' );
		} elseif ( 'fluent_forms' === $provider ) {
			$entry_id       = $this->external_entry_id_from_fields( $fields, '_fluent_forms_submission_id' );
			$form_id_label  = __( 'Fluent Forms form ID', 'submission-inbox' );
			$entry_id_label = __( 'Fluent Forms submission ID', 'submission-inbox' );
		} elseif ( 'forminator' === $provider ) {
			$entry_id       = $this->external_entry_id_from_fields( $fields, '_forminator_entry_id' );
			$form_id_label  = __( 'Forminator form ID', 'submission-inbox' );
			$entry_id_label = __( 'Forminator entry ID', 'submission-inbox' );
		} elseif ( 'ninja_forms' === $provider ) {
			$entry_id       = $this->external_entry_id_from_fields( $fields, '_ninja_forms_submission_id' );
			$form_id_label  = __( 'Ninja Forms form ID', 'submission-inbox' );
			$entry_id_label = __( 'Ninja Forms submission ID', 'submission-inbox' );
		}
		?>
		<div class="subin-side-list">
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Source type', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $this->provider_label( $lead->provider ) ); ?></strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Form name', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $lead->form_name ? $lead->form_name : __( 'Unknown form', 'submission-inbox' ) ); ?></strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php echo esc_html( $form_id_label ); ?></span>
				<strong><?php echo esc_html( (string) $lead->form_id ); ?></strong>
			</div>
			<?php if ( '' !== $entry_id ) : ?>
				<div class="subin-side-list__item">
					<span><?php echo esc_html( $entry_id_label ); ?></span>
					<strong><?php echo esc_html( $entry_id ); ?></strong>
				</div>
			<?php endif; ?>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Submitted date', 'submission-inbox' ); ?></span>
				<strong><?php echo esc_html( $this->format_date( $lead->submitted_at ) ); ?></strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Source URL', 'submission-inbox' ); ?></span>
				<strong><?php $this->source_link( $lead->source_url ); ?></strong>
			</div>
			<div class="subin-side-list__item">
				<span><?php esc_html_e( 'Referrer', 'submission-inbox' ); ?></span>
				<strong><?php $this->source_link( $lead->referrer ); ?></strong>
			</div>
		</div>
		<?php
	}

	/**
	 * Render workflow controls.
	 *
	 * @param object $lead Lead row.
	 * @return void
	 */
	private function render_workflow_panel( $lead ) {
		$lead_id         = absint( $lead->id ?? 0 );
		$users           = $this->assignable_users( absint( $lead->assigned_user_id ?? 0 ) );
		$follow_up_value = $this->follow_up_date_value( $lead->follow_up_at ?? '' );
		?>
		<form method="post" action="" class="subin-workflow-form">
			<input type="hidden" name="subin_lead_action" value="update_workflow" />
			<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
			<input type="hidden" name="workflow_version" value="<?php echo esc_attr( (string) max( 1, absint( $lead->workflow_version ?? 1 ) ) ); ?>" />
			<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
			<div class="subin-side-list">
				<div class="subin-side-list__item">
					<label for="subin-workflow-status-detail"><?php esc_html_e( 'Workflow status', 'submission-inbox' ); ?></label>
					<select id="subin-workflow-status-detail" name="workflow_status">
						<?php foreach ( $this->workflow->workflow_statuses() as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>" <?php selected( sanitize_key( $lead->workflow_status ?? 'open' ), $key ); ?>><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</select>
				</div>
				<div class="subin-side-list__item">
					<label for="subin-priority-detail"><?php esc_html_e( 'Priority', 'submission-inbox' ); ?></label>
					<select id="subin-priority-detail" name="priority">
						<?php foreach ( $this->workflow->priorities() as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>" <?php selected( sanitize_key( $lead->priority ?? 'normal' ), $key ); ?>><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</select>
				</div>
				<div class="subin-side-list__item">
					<label for="subin-assignee-detail"><?php esc_html_e( 'Assignee', 'submission-inbox' ); ?></label>
					<select id="subin-assignee-detail" name="assigned_user_id">
						<option value="0"><?php esc_html_e( 'Unassigned', 'submission-inbox' ); ?></option>
						<?php foreach ( $users as $user ) : ?>
							<option value="<?php echo esc_attr( (string) $user->ID ); ?>" <?php selected( absint( $lead->assigned_user_id ?? 0 ), absint( $user->ID ) ); ?>><?php echo esc_html( $user->display_name ); ?></option>
						<?php endforeach; ?>
					</select>
				</div>
				<div class="subin-side-list__item">
					<label for="subin-follow-up-detail"><?php esc_html_e( 'Follow-up date', 'submission-inbox' ); ?></label>
					<input id="subin-follow-up-detail" type="date" name="follow_up_at" value="<?php echo esc_attr( $follow_up_value ); ?>" />
				</div>
			</div>
			<p><button type="submit" class="button button-primary subin-btn subin-btn-primary"><?php esc_html_e( 'Save workflow', 'submission-inbox' ); ?></button></p>
		</form>
		<?php
	}

	/**
	 * Load users who can currently manage leads, preserving a legacy assignee.
	 *
	 * @param int $legacy_assignee_id Existing assignee id.
	 * @return array<int,object>
	 */
	private function assignable_users( $legacy_assignee_id = 0 ) {
		if ( null === $this->assignable_user_cache ) {
			$users_by_id = array();
			foreach ( array( \SUBIN\Security\Capabilities::MANAGE_LEADS, SUBIN_AdminMenu::CAPABILITY ) as $capability ) {
				$candidates = get_users(
					array(
						'capability' => $capability,
						'fields'     => array( 'ID', 'display_name' ),
						'number'     => 200,
						'orderby'    => 'display_name',
					)
				);
				foreach ( $candidates as $candidate ) {
					$user_id = absint( $candidate->ID ?? 0 );
					if (
						$user_id > 0
						&& \SUBIN\Security\Capabilities::user_can( $user_id, \SUBIN\Security\Capabilities::VIEW_LEADS )
						&& \SUBIN\Security\Capabilities::user_can( $user_id, \SUBIN\Security\Capabilities::MANAGE_LEADS )
					) {
						$users_by_id[ $user_id ] = $candidate;
					}
				}
			}

			$this->assignable_user_cache = array_values( $users_by_id );
			usort(
				$this->assignable_user_cache,
				static function ( $left, $right ) {
					return strcasecmp( (string) $left->display_name, (string) $right->display_name );
				}
			);
		}

		return $this->include_legacy_assignee( $this->assignable_user_cache, $legacy_assignee_id );
	}

	/**
	 * Add only a lead's current legacy assignee to its own editor.
	 *
	 * @param array<int,object> $users Eligible assignees.
	 * @param int               $legacy_assignee_id Existing assignee id.
	 * @return array<int,object>
	 */
	private function include_legacy_assignee( array $users, $legacy_assignee_id ) {
		$legacy_assignee_id = absint( $legacy_assignee_id );
		if ( $legacy_assignee_id <= 0 || in_array( $legacy_assignee_id, array_map( 'absint', wp_list_pluck( $users, 'ID' ) ), true ) ) {
			return $users;
		}

		$legacy_assignee = get_userdata( $legacy_assignee_id );
		if ( $legacy_assignee ) {
			$users[] = $legacy_assignee;
		}

		return $users;
	}

	/**
	 * Format a stored follow-up timestamp for a date control.
	 *
	 * @param mixed $value Stored date.
	 * @return string
	 */
	private function follow_up_date_value( $value ) {
		$value = sanitize_text_field( (string) $value );
		if ( '' === $value || ! strtotime( $value ) ) {
			return '';
		}

		return substr( $value, 0, 10 );
	}

	/**
	 * Render lead notes and note form.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $notes Notes.
	 * @return void
	 */
	private function render_notes_panel( $lead, array $notes ) {
		$lead_id    = absint( $lead->id ?? 0 );
		$can_manage = \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::MANAGE_LEADS );
		$conflict   = is_array( $this->note_conflict_draft ) && $lead_id === absint( $this->note_conflict_draft['lead_id'] ?? 0 )
			? $this->note_conflict_draft
			: array();
		$conflict_note_id = absint( $conflict['note_id'] ?? 0 );
		$conflict_draft   = is_string( $conflict['note'] ?? null ) ? (string) $conflict['note'] : '';
		$conflict_note_exists = false;
		foreach ( $notes as $note ) {
			if ( $conflict_note_id > 0 && $conflict_note_id === absint( $note->id ?? 0 ) ) {
				$conflict_note_exists = true;
				break;
			}
		}
		?>
		<?php if ( ! empty( $conflict ) ) : ?>
			<div class="subin-note-conflict" role="alert">
				<strong><?php esc_html_e( 'Your edit was not saved because this note changed.', 'submission-inbox' ); ?></strong>
				<?php if ( $conflict_note_exists ) : ?>
					<p><?php esc_html_e( 'The current note is shown below. Your unsaved draft is still in the open editor so you can compare, merge, and save again.', 'submission-inbox' ); ?></p>
				<?php else : ?>
					<p><?php esc_html_e( 'The note was deleted while you were editing it. Your unsaved draft is preserved below and can be saved as a new note.', 'submission-inbox' ); ?></p>
				<?php endif; ?>
			</div>
		<?php endif; ?>
		<?php if ( ! empty( $conflict ) && ! $conflict_note_exists && $can_manage ) : ?>
			<form method="post" action="" class="subin-note-form subin-note-recovery-form">
				<input type="hidden" name="subin_lead_action" value="add_note" />
				<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
				<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
				<label for="subin-lead-note-recovery"><strong><?php esc_html_e( 'Recovered draft', 'submission-inbox' ); ?></strong></label>
				<textarea id="subin-lead-note-recovery" name="lead_note" rows="3" class="large-text" maxlength="5000" required><?php echo esc_textarea( $conflict_draft ); ?></textarea>
				<button type="submit" class="button button-primary subin-btn subin-btn-primary"><?php esc_html_e( 'Save as new note', 'submission-inbox' ); ?></button>
			</form>
		<?php endif; ?>
		<?php if ( $can_manage ) : ?>
		<form method="post" action="" class="subin-note-form">
			<input type="hidden" name="subin_lead_action" value="add_note" />
			<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
			<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
			<div class="subin-note-form__row">
				<label class="screen-reader-text" for="subin-lead-note"><?php esc_html_e( 'Add note', 'submission-inbox' ); ?></label>
				<textarea id="subin-lead-note" name="lead_note" rows="1" class="large-text" maxlength="5000" required placeholder="<?php esc_attr_e( 'Add a short note, for example: called, no answer...', 'submission-inbox' ); ?>"></textarea>
				<button type="submit" class="button button-primary subin-btn subin-btn-primary"><?php esc_html_e( 'Add note', 'submission-inbox' ); ?></button>
			</div>
		</form>
		<?php endif; ?>

		<?php if ( ! empty( $notes ) ) : ?>
			<div class="subin-workflow-list">
					<?php foreach ( $notes as $note ) : ?>
							<?php $is_conflicted_note = $conflict_note_id > 0 && $conflict_note_id === absint( $note->id ?? 0 ); ?>
							<article class="subin-workflow-item<?php echo $is_conflicted_note ? ' subin-workflow-item--conflicted' : ''; ?>">
							<strong><?php echo esc_html( $note->display_name ? $note->display_name : __( 'WordPress user', 'submission-inbox' ) ); ?></strong>
							<p><?php echo nl2br( esc_html( $note->note ) ); ?></p>
							<span>
								<?php echo esc_html( $this->format_date( $note->created_at ) ); ?>
							<?php if ( ! empty( $note->updated_at ) && ( max( 1, absint( $note->note_version ?? 1 ) ) > 1 || (string) $note->updated_at !== (string) $note->created_at ) ) : ?>
								<?php /* translators: %s: date and time when the note was edited. */ ?>
								· <?php echo esc_html( sprintf( __( 'Edited %s', 'submission-inbox' ), $this->format_date( $note->updated_at ) ) ); ?>
								<?php endif; ?>
							</span>
							<?php if ( $can_manage ) : ?>
						<div class="subin-note-actions">
								<details class="subin-note-edit"<?php echo $is_conflicted_note ? ' open' : ''; ?>>
								<summary><?php esc_html_e( 'Edit', 'submission-inbox' ); ?></summary>
								<?php $note_form_id = 'subin-lead-note-edit-form-' . absint( $note->id ?? 0 ); ?>
								<form id="<?php echo esc_attr( $note_form_id ); ?>" method="post" action="" class="subin-note-edit-form">
									<input type="hidden" name="subin_lead_action" value="update_note" />
									<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
									<input type="hidden" name="note_id" value="<?php echo esc_attr( (string) absint( $note->id ?? 0 ) ); ?>" />
									<input type="hidden" name="note_version" value="<?php echo esc_attr( (string) max( 1, absint( $note->note_version ?? 1 ) ) ); ?>" />
									<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
									<label class="screen-reader-text" for="<?php echo esc_attr( 'subin-lead-note-edit-' . absint( $note->id ?? 0 ) ); ?>"><?php esc_html_e( 'Edit note', 'submission-inbox' ); ?></label>
									<textarea id="<?php echo esc_attr( 'subin-lead-note-edit-' . absint( $note->id ?? 0 ) ); ?>" name="lead_note" rows="3" maxlength="5000" required><?php echo esc_textarea( $is_conflicted_note ? $conflict_draft : (string) $note->note ); ?></textarea>
								</form>
								<div class="subin-note-edit-actions">
									<button type="submit" form="<?php echo esc_attr( $note_form_id ); ?>" class="button button-primary subin-btn subin-btn-primary"><?php esc_html_e( 'Save note', 'submission-inbox' ); ?></button>
									<form method="post" action="" class="subin-note-delete-form">
										<input type="hidden" name="subin_lead_action" value="delete_note" />
										<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
										<input type="hidden" name="note_id" value="<?php echo esc_attr( (string) absint( $note->id ?? 0 ) ); ?>" />
										<input type="hidden" name="note_version" value="<?php echo esc_attr( (string) max( 1, absint( $note->note_version ?? 1 ) ) ); ?>" />
										<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
										<button type="submit" class="button subin-action-chip subin-action-chip-danger" onclick="return window.confirm('<?php echo esc_js( __( 'Delete this note? It will be removed from this site. A connected sync service may apply its own retention policy.', 'submission-inbox' ) ); ?>');"><?php esc_html_e( 'Delete', 'submission-inbox' ); ?></button>
									</form>
								</div>
							</details>
						</div>
						<?php endif; ?>
					</article>
					<?php endforeach; ?>
				</div>
			<?php else : ?>
				<div class="subin-empty-state">
					<strong><?php esc_html_e( 'No notes yet', 'submission-inbox' ); ?></strong>
					<p><?php esc_html_e( 'Add the first note to keep follow-up context with this lead.', 'submission-inbox' ); ?></p>
				</div>
			<?php endif; ?>
		<?php
	}

	/**
	 * Render lead activity timeline.
	 *
	 * @param array<int,object> $timeline Timeline.
	 * @return void
	 */
	private function render_timeline_panel( array $timeline ) {
		?>
		<?php if ( ! empty( $timeline ) ) : ?>
			<div class="subin-workflow-list">
				<?php foreach ( $timeline as $activity ) : ?>
					<?php $actor = ! empty( $activity->display_name ) ? sanitize_text_field( $activity->display_name ) : __( 'System', 'submission-inbox' ); ?>
					<article class="subin-workflow-item">
						<strong><?php echo esc_html( $activity->activity_message ); ?></strong>
						<span>
							<?php
							echo esc_html(
								sprintf(
									/* translators: 1: activity actor, 2: activity date. */
									__( '%1$s · %2$s', 'submission-inbox' ),
									$actor,
									$this->format_date( $activity->created_at )
								)
							);
							?>
						</span>
					</article>
				<?php endforeach; ?>
			</div>
		<?php else : ?>
			<?php SUBIN_View::empty_state( __( 'No activity yet', 'submission-inbox' ), __( 'Workflow changes and notes will appear here.', 'submission-inbox' ) ); ?>
		<?php endif; ?>
		<?php
	}

	/**
	 * Render sidebar email summary.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $events Events.
	 * @return void
	 */
	private function render_email_summary( $lead, array $events ) {
		$admin_status = sanitize_key( $lead->email_status ?? 'unknown' );
		$admin_event  = $this->latest_admin_notification_event( $events, $admin_status );
		$admin_to     = $admin_event ? sanitize_text_field( $admin_event->to_email ?? '' ) : $this->notification_recipient_details_for_lead( $lead )['email'];
		$auto_reply   = $this->auto_reply_details_for_lead( $lead, $events );
		?>
		<div class="subin-email-summary__list">
			<div class="subin-email-summary__item">
				<div>
					<strong><?php esc_html_e( 'Team notification', 'submission-inbox' ); ?></strong>
					<?php if ( '' !== $admin_to ) : ?>
						<span><?php echo esc_html( $admin_to ); ?></span>
					<?php endif; ?>
				</div>
				<?php $this->status_badge( $admin_status, $this->email_status_label( $admin_status ) ); ?>
			</div>
			<?php $this->render_admin_notification_followup_actions( $lead, $admin_event, $admin_status ); ?>
			<div class="subin-email-summary__item">
				<div>
					<strong><?php esc_html_e( 'Visitor confirmation', 'submission-inbox' ); ?></strong>
					<?php if ( 'not_configured' === $auto_reply['status'] ) : ?>
						<span><?php esc_html_e( 'No auto-reply was configured when this lead was received.', 'submission-inbox' ); ?></span>
					<?php elseif ( '' !== $auto_reply['rule_name'] ) : ?>
						<span><?php echo esc_html( $auto_reply['rule_name'] ); ?></span>
					<?php endif; ?>
					<?php if ( '' !== $auto_reply['recipient'] ) : ?>
						<span><?php echo esc_html( $auto_reply['recipient'] ); ?></span>
					<?php endif; ?>
				</div>
				<?php $this->status_badge( $auto_reply['status'], $this->auto_reply_status_label( $auto_reply['status'] ) ); ?>
			</div>
			<?php $this->render_visitor_confirmation_followup_actions( $lead, $auto_reply ); ?>
		</div>
		<?php
	}

	/**
	 * Render existing admin notification follow-up actions in the new layout.
	 *
	 * @param object      $lead Lead row.
	 * @param object|null $event Related admin notification event.
	 * @param string      $status Notification status.
	 * @return void
	 */
	private function render_admin_notification_followup_actions( $lead, $event, $status ) {
		$status        = sanitize_key( $status );
		$form_settings = $this->form_settings_url_for_lead( $lead );
		$can_retry     = 'submission_inbox_form' === sanitize_key( $lead->provider ?? '' )
			&& \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::RETRY_NOTIFICATIONS );

		if ( 'failed' !== $status && ( 'no_email_generated' !== $status || '' === $form_settings ) ) {
			return;
		}

		$email_log_url      = $this->email_log_url_for_outcome( $event, $status, sanitize_key( $lead->provider ?? '' ) );
		$issue_action_url   = $this->can_access_legacy_admin_page( \SUBIN\Security\Capabilities::MANAGE_SENDING )
			? admin_url( 'admin.php?page=subin-settings&subin_settings_tab=sending' )
			: '';
		$issue_action_label = __( 'Open Sending settings', 'submission-inbox' );
		$issue              = ( $event && ! empty( $event->error_message ) ) ? $this->error_classifier->classify( (string) $event->error_message ) : array();

		if ( ! empty( $issue ) && 'form_notification_settings' === ( $issue['action_type'] ?? '' ) && '' !== $form_settings ) {
			$issue_action_url   = $form_settings;
			$issue_action_label = __( 'Check form settings', 'submission-inbox' );
		}
		if ( 'failed' === $status && '' === $email_log_url && '' === $issue_action_url && ! $can_retry ) {
			return;
		}
		?>
		<div class="subin-email-summary__actions">
			<?php if ( 'failed' === $status ) : ?>
				<?php if ( '' !== $email_log_url ) : ?>
					<a class="subin-action-chip" href="<?php echo esc_url( $email_log_url ); ?>"><?php esc_html_e( 'View Email Log', 'submission-inbox' ); ?></a>
				<?php endif; ?>
				<?php if ( '' !== $issue_action_url ) : ?>
					<a class="subin-action-chip" href="<?php echo esc_url( $issue_action_url ); ?>"><?php echo esc_html( $issue_action_label ); ?></a>
				<?php endif; ?>
				<?php if ( $can_retry ) : ?>
					<?php $this->render_status_action_button( absint( $lead->id ), 'resend_notification', __( 'Retry notification', 'submission-inbox' ), 'subin-action-chip subin-action-chip-warning' ); ?>
				<?php endif; ?>
			<?php else : ?>
				<a class="subin-action-chip" href="<?php echo esc_url( $form_settings ); ?>"><?php esc_html_e( 'Check form settings', 'submission-inbox' ); ?></a>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Link visitor confirmation outcomes to configuration or the detailed Email Log.
	 *
	 * @param object              $lead Lead row.
	 * @param array<string,mixed> $auto_reply Auto-reply details.
	 * @return void
	 */
	private function render_visitor_confirmation_followup_actions( $lead, array $auto_reply ) {
		$status     = $this->normalize_auto_reply_status( $auto_reply['status'] ?? 'not_configured' );
		$manage_url = $this->auto_reply_url_for_lead( $lead );
		$event      = is_object( $auto_reply['event'] ?? null ) ? $auto_reply['event'] : null;
		$email_log_url = in_array( $status, array( 'failed', 'no_email_generated' ), true )
			? $this->email_log_url_for_outcome( $event, $status, \SUBIN\Mail\LeadEmailStatusService::AUTO_REPLY_SOURCE )
			: '';

		if ( '' === $manage_url && '' === $email_log_url ) {
			return;
		}

		$manage_label = 'not_configured' === $status && empty( $auto_reply['enabled'] )
			? __( 'Create visitor confirmation', 'submission-inbox' )
			: __( 'Manage visitor confirmation', 'submission-inbox' );
		?>
		<div class="subin-email-summary__actions">
			<?php if ( '' !== $email_log_url ) : ?>
				<a class="subin-action-chip" href="<?php echo esc_url( $email_log_url ); ?>"><?php esc_html_e( 'View Email Log', 'submission-inbox' ); ?></a>
			<?php endif; ?>
			<?php if ( '' !== $manage_url ) : ?>
				<a class="subin-action-chip" href="<?php echo esc_url( $manage_url ); ?>"><?php echo esc_html( $manage_label ); ?></a>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render combined email activity timeline.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $events Events.
	 * @return void
	 */
	private function render_email_activity( $lead, array $events ) {
		if ( empty( $events ) ) {
			SUBIN_View::empty_state( __( 'No email activity', 'submission-inbox' ), __( 'No related WordPress email events were found for this lead.', 'submission-inbox' ) );
			return;
		}

		$rules_by_id     = array();
		$templates_by_id = array();
		?>
		<div class="subin-email-activity__timeline">
			<?php foreach ( $events as $event ) : ?>
				<?php
				$event_status   = sanitize_key( $event->status ?? 'unknown' );
				$event_source   = sanitize_key( $event->source ?? '' );
				$is_auto_reply  = 'submission_inbox_auto_reply' === $event_source;
				$event_issue    = ( 'failed' === $event_status && ! empty( $event->error_message ) ) ? $this->error_classifier->classify( (string) $event->error_message ) : array();
				$recipient_info = $is_auto_reply ? '' : $this->notification_recipient_details_for_lead( $lead )['source'];
				$event_rule_id  = $is_auto_reply ? absint( $event->rule_id ?? 0 ) : 0;
				if ( $event_rule_id > 0 && ! array_key_exists( $event_rule_id, $rules_by_id ) ) {
					$rules_by_id[ $event_rule_id ] = $this->auto_reply_rules->find( $event_rule_id );
				}
				$event_rule      = $event_rule_id > 0 ? ( $rules_by_id[ $event_rule_id ] ?? null ) : null;
				$event_rule_name = $event_rule ? sanitize_text_field( $event_rule['name'] ?? '' ) : '';
				if ( '' === $event_rule_name && $event_rule_id > 0 ) {
					/* translators: %d: auto-reply rule ID. */
					$event_rule_name = sprintf( __( 'Rule #%d', 'submission-inbox' ), $event_rule_id );
				}
				$event_template_id = $is_auto_reply ? absint( $event->template_id ?? 0 ) : 0;
				if ( $event_template_id > 0 && ! array_key_exists( $event_template_id, $templates_by_id ) ) {
					$templates_by_id[ $event_template_id ] = $this->auto_reply_templates->get( $event_template_id );
				}
				$event_template      = $event_template_id > 0 ? ( $templates_by_id[ $event_template_id ] ?? null ) : null;
				$event_template_name = $event_template ? sanitize_text_field( $event_template['name'] ?? '' ) : '';
				$event_log_url       = $this->email_log_url_for_outcome( $event, $event_status, $event_source );
				?>
				<article class="subin-email-activity__event subin-email-activity__event--<?php echo esc_attr( $this->badge_type_for_status( $event_status ) ); ?>">
					<div class="subin-email-activity__marker" aria-hidden="true"></div>
					<div class="subin-email-activity__content">
						<div class="subin-email-activity__head">
							<div>
								<span class="subin-status-label"><?php echo esc_html( $this->mail_event_source_label( $event->source ?? '' ) ); ?></span>
								<h3><?php echo esc_html( $event->subject ? $event->subject : __( 'Email event', 'submission-inbox' ) ); ?></h3>
							</div>
							<?php $this->status_badge( $event_status, $this->email_status_label( $event_status ) ); ?>
						</div>
						<div class="subin-email-activity__meta">
							<div>
								<span><?php esc_html_e( 'To', 'submission-inbox' ); ?></span>
								<strong><?php echo esc_html( $event->to_email ? $event->to_email : __( 'Unknown', 'submission-inbox' ) ); ?></strong>
							</div>
							<div>
								<span><?php esc_html_e( 'Date', 'submission-inbox' ); ?></span>
								<strong><?php echo esc_html( $this->format_date( $event->created_at ) ); ?></strong>
							</div>
							<?php if ( $is_auto_reply && '' !== $event_rule_name ) : ?>
								<div>
									<span><?php esc_html_e( 'Rule', 'submission-inbox' ); ?></span>
									<strong><?php echo esc_html( $event_rule_name ); ?></strong>
								</div>
							<?php endif; ?>
							<?php if ( $is_auto_reply && '' !== $event_template_name ) : ?>
								<div>
									<span><?php esc_html_e( 'Saved design', 'submission-inbox' ); ?></span>
									<strong><?php echo esc_html( $event_template_name ); ?></strong>
								</div>
							<?php endif; ?>
							<?php if ( $is_auto_reply && absint( $event->rule_version ?? 0 ) > 0 ) : ?>
								<div>
									<span><?php esc_html_e( 'Rule version', 'submission-inbox' ); ?></span>
									<strong><?php echo esc_html( (string) absint( $event->rule_version ) ); ?></strong>
								</div>
							<?php endif; ?>
							<?php if ( $is_auto_reply && '' !== sanitize_key( $event->delivery_mode ?? '' ) ) : ?>
								<div>
									<span><?php esc_html_e( 'Delivery', 'submission-inbox' ); ?></span>
									<strong><?php echo esc_html( 'delay' === sanitize_key( $event->delivery_mode ) ? __( 'Delayed', 'submission-inbox' ) : __( 'Immediate', 'submission-inbox' ) ); ?></strong>
								</div>
							<?php endif; ?>
							<?php if ( '' !== $recipient_info ) : ?>
								<div>
									<span><?php esc_html_e( 'Recipient source', 'submission-inbox' ); ?></span>
									<strong><?php echo esc_html( $recipient_info ); ?></strong>
								</div>
							<?php endif; ?>
						</div>
						<?php if ( ! empty( $event->error_message ) ) : ?>
							<div class="subin-email-activity__note">
								<?php if ( ! empty( $event_issue ) ) : ?>
									<div class="subin-email-activity__guidance-row">
										<span><?php esc_html_e( 'Issue', 'submission-inbox' ); ?></span>
										<strong><?php echo esc_html( $event_issue['title'] ); ?></strong>
									</div>
									<div class="subin-email-activity__guidance-row">
										<span><?php esc_html_e( 'Explanation', 'submission-inbox' ); ?></span>
										<strong><?php echo esc_html( $event_issue['explanation'] ); ?></strong>
									</div>
									<div class="subin-email-activity__guidance-row">
										<span><?php esc_html_e( 'Recommended action', 'submission-inbox' ); ?></span>
										<strong><?php echo esc_html( $event_issue['recommended_action'] ); ?></strong>
									</div>
								<?php endif; ?>
								<div class="subin-email-activity__guidance-row">
									<span><?php esc_html_e( 'Technical error', 'submission-inbox' ); ?></span>
									<strong><?php echo esc_html( ! empty( $event_issue['technical_error'] ) ? $event_issue['technical_error'] : $this->error_classifier->sanitize_technical_error( (string) $event->error_message ) ); ?></strong>
								</div>
							</div>
						<?php endif; ?>
						<?php if ( '' !== $event_log_url ) : ?>
							<div class="subin-status-links">
								<a href="<?php echo esc_url( $event_log_url ); ?>"><?php esc_html_e( 'View related event', 'submission-inbox' ); ?></a>
							</div>
						<?php endif; ?>
					</div>
				</article>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Get an external entry id from saved hidden metadata fields.
	 *
	 * @param array<int,object> $fields Saved fields.
	 * @return string
	 */
	private function external_entry_id_from_fields( array $fields, $field_key ) {
		$field_key = sanitize_key( $field_key );
		foreach ( $fields as $field ) {
			if ( $field_key === sanitize_key( $field->field_key ?? '' ) ) {
				return sanitize_text_field( (string) ( $field->field_value ?? '' ) );
			}
		}

		return '';
	}

	/**
	 * Render email notification status card.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $events Events.
	 * @return void
	 */
	private function render_notification_card( $lead, array $events ) {
		$status          = sanitize_key( $lead->email_status ?? 'unknown' );
		$event           = $this->latest_admin_notification_event( $events, $status );
		$form_settings   = $this->form_settings_url_for_lead( $lead );
		$card_class      = in_array( $status, array( 'failed', 'no_email_generated' ), true ) ? ( 'failed' === $status ? ' subin-notification-card--failed' : ' subin-notification-card--warning' ) : '';
		$email_log_url   = $this->email_log_url_for_outcome( $event, $status, sanitize_key( $lead->provider ?? '' ) );
		$message         = $this->notification_status_message( $status );
		$issue           = ( $event && ! empty( $event->error_message ) ) ? $this->error_classifier->classify( (string) $event->error_message ) : array();
		$issue_action_url = $this->can_access_legacy_admin_page( \SUBIN\Security\Capabilities::MANAGE_SENDING )
			? admin_url( 'admin.php?page=subin-settings&subin_settings_tab=sending' )
			: '';
		$issue_action_label = __( 'Open Sending settings', 'submission-inbox' );
		$recipient_details = $this->notification_recipient_details_for_lead( $lead );

		if ( ! empty( $issue ) && 'form_notification_settings' === ( $issue['action_type'] ?? '' ) && '' !== $form_settings ) {
			$issue_action_url   = $form_settings;
			$issue_action_label = __( 'Check form settings', 'submission-inbox' );
		}
		$can_retry = \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::RETRY_NOTIFICATIONS );
		?>
		<section class="subin-notification-card<?php echo esc_attr( $card_class ); ?>">
			<div class="subin-notification-card__head">
				<div>
					<h2><?php esc_html_e( 'Email notification', 'submission-inbox' ); ?></h2>
					<?php SUBIN_View::help_tooltip( __( 'This explains what happened when WordPress tried to send the notification email.', 'submission-inbox' ) ); ?>
					<p><?php echo esc_html( $message ); ?></p>
				</div>
				<?php $this->status_badge( $status, $this->email_status_label( $status ) ); ?>
			</div>
			<?php if ( $event && ( ! empty( $event->to_email ) || ! empty( $event->subject ) || ! empty( $event->error_message ) ) ) : ?>
				<div class="subin-notification-card__details">
					<?php if ( ! empty( $event->to_email ) ) : ?>
						<div class="subin-detail-item">
							<span><?php esc_html_e( 'Recipient', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( $event->to_email ); ?></strong>
						</div>
					<?php endif; ?>
					<?php if ( '' !== $recipient_details['source'] ) : ?>
						<div class="subin-detail-item">
							<span><?php esc_html_e( 'Recipient source', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( $recipient_details['source'] ); ?></strong>
						</div>
					<?php endif; ?>
					<?php if ( ! empty( $event->subject ) ) : ?>
						<div class="subin-detail-item">
							<span><?php esc_html_e( 'Subject', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( $event->subject ); ?></strong>
						</div>
					<?php endif; ?>
					<?php if ( ! empty( $event->error_message ) ) : ?>
						<?php if ( ! empty( $issue ) ) : ?>
							<div class="subin-detail-item">
								<span><?php esc_html_e( 'Issue', 'submission-inbox' ); ?></span>
								<strong><?php echo esc_html( $issue['title'] ); ?></strong>
							</div>
							<div class="subin-detail-item subin-detail-message">
								<span><?php esc_html_e( 'Explanation', 'submission-inbox' ); ?></span>
								<strong><?php echo esc_html( $issue['explanation'] ); ?></strong>
							</div>
							<div class="subin-detail-item subin-detail-message">
								<span><?php esc_html_e( 'Recommended action', 'submission-inbox' ); ?></span>
								<strong><?php echo esc_html( $issue['recommended_action'] ); ?></strong>
							</div>
						<?php endif; ?>
						<div class="subin-detail-item subin-detail-message">
							<span><?php esc_html_e( 'Technical error', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( ! empty( $issue['technical_error'] ) ? $issue['technical_error'] : $this->error_classifier->sanitize_technical_error( (string) $event->error_message ) ); ?></strong>
						</div>
					<?php endif; ?>
				</div>
			<?php endif; ?>
			<?php if ( ( 'failed' === $status && ( '' !== $email_log_url || '' !== $issue_action_url || $can_retry ) ) || ( 'no_email_generated' === $status && '' !== $form_settings ) ) : ?>
				<div class="subin-notification-card__actions">
					<?php if ( 'failed' === $status ) : ?>
						<?php if ( '' !== $email_log_url ) : ?>
							<a class="subin-action-chip" href="<?php echo esc_url( $email_log_url ); ?>"><?php esc_html_e( 'View Email Log', 'submission-inbox' ); ?></a>
						<?php endif; ?>
						<?php if ( '' !== $issue_action_url ) : ?>
							<a class="subin-action-chip" href="<?php echo esc_url( $issue_action_url ); ?>"><?php echo esc_html( $issue_action_label ); ?></a>
						<?php endif; ?>
						<?php if ( $can_retry ) : ?>
							<?php $this->render_status_action_button( absint( $lead->id ), 'resend_notification', __( 'Retry notification', 'submission-inbox' ), 'subin-action-chip subin-action-chip-warning' ); ?>
							<?php SUBIN_View::help_tooltip( __( 'Attempts to send the notification email again. The lead is already saved.', 'submission-inbox' ) ); ?>
						<?php endif; ?>
					<?php else : ?>
						<a class="subin-action-chip" href="<?php echo esc_url( $form_settings ); ?>"><?php esc_html_e( 'Check form settings', 'submission-inbox' ); ?></a>
					<?php endif; ?>
				</div>
			<?php endif; ?>
		</section>
		<?php
	}

	/**
	 * Render auto-reply summary for a lead.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $events Related mail events.
	 * @return void
	 */
	private function render_auto_reply_summary( $lead, array $events ) {
		$auto_reply_event = null;
		foreach ( $events as $event ) {
			if ( 'submission_inbox_auto_reply' === sanitize_key( $event->source ?? '' ) ) {
				$auto_reply_event = $event;
				break;
			}
		}

		$template_name = '';
		$recipient     = '';
		$status        = 'no_email_generated';
		$reason        = __( 'Auto-reply disabled.', 'submission-inbox' );
		$form          = ( 'submission_inbox_form' === sanitize_key( $lead->provider ?? '' ) && ! empty( $lead->form_id ) ) ? $this->forms->get( absint( $lead->form_id ) ) : null;

		if ( $form ) {
			$settings   = $this->forms->decode_settings( $form );
			$config     = is_array( $settings['auto_reply'] ?? null ) ? $settings['auto_reply'] : array();
			$template_id = absint( $config['template_id'] ?? 0 );
			$template   = $template_id > 0 ? $this->auto_reply_templates->get( $template_id ) : null;
			if ( $template ) {
				$template_name = sanitize_text_field( $template['name'] ?? '' );
			}
			if ( ! empty( $config['enabled'] ) ) {
				$reason = __( 'No auto-reply email event was found for this lead.', 'submission-inbox' );
			}
		}

		if ( $auto_reply_event ) {
			$recipient = sanitize_text_field( $auto_reply_event->to_email ?? '' );
			$status    = sanitize_key( $auto_reply_event->status ?? 'unknown' );
			$reason    = $this->error_classifier->sanitize_technical_error( (string) ( $auto_reply_event->error_message ?? '' ) );
		}

		?>
		<div class="subin-auto-reply-card__content">
			<div class="subin-auto-reply-card__status">
				<span class="subin-status-label"><?php esc_html_e( 'Visitor auto-reply', 'submission-inbox' ); ?></span>
				<?php $this->status_badge( $status, $this->email_status_label( $status ) ); ?>
				<?php if ( ! $auto_reply_event ) : ?>
					<p><?php esc_html_e( 'No visitor auto-reply was generated for this lead.', 'submission-inbox' ); ?></p>
				<?php elseif ( '' !== $reason ) : ?>
					<p><?php echo esc_html( $reason ); ?></p>
				<?php endif; ?>
			</div>
			<div class="subin-source-card__grid">
				<div class="subin-detail-item">
					<span><?php esc_html_e( 'Template', 'submission-inbox' ); ?></span>
					<strong><?php echo esc_html( '' !== $template_name ? $template_name : __( 'Unknown', 'submission-inbox' ) ); ?></strong>
				</div>
				<div class="subin-detail-item">
					<span><?php esc_html_e( 'Recipient', 'submission-inbox' ); ?></span>
					<strong><?php echo esc_html( '' !== $recipient ? $recipient : __( 'Unknown', 'submission-inbox' ) ); ?></strong>
				</div>
			</div>
			<?php $auto_reply_log_url = $auto_reply_event ? $this->email_log_url_for_outcome( $auto_reply_event, $status, \SUBIN\Mail\LeadEmailStatusService::AUTO_REPLY_SOURCE ) : ''; ?>
			<?php if ( '' !== $auto_reply_log_url ) : ?>
				<div class="subin-status-links">
					<a href="<?php echo esc_url( $auto_reply_log_url ); ?>"><?php esc_html_e( 'View related event', 'submission-inbox' ); ?></a>
				</div>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render related email events.
	 *
	 * @param array<int,object> $events Events.
	 * @return void
	 */
	private function render_email_events( array $events ) {
		if ( empty( $events ) ) {
			echo '<p>' . esc_html__( 'No related email events.', 'submission-inbox' ) . '</p>';
			return;
		}
		?>
		<div class="subin-email-events__list">
			<?php foreach ( $events as $event ) : ?>
				<?php
				$event_status = sanitize_key( $event->status ?? 'unknown' );
				$event_issue  = ( 'failed' === $event_status && ! empty( $event->error_message ) ) ? $this->error_classifier->classify( (string) $event->error_message ) : array();
				?>
				<article class="subin-email-event-card">
					<div class="subin-email-event-card__head">
						<div>
							<span class="subin-status-label"><?php echo esc_html( $this->mail_event_source_label( $event->source ?? '' ) ); ?></span>
							<h3><?php echo esc_html( $event->subject ? $event->subject : __( 'Email event', 'submission-inbox' ) ); ?></h3>
						</div>
						<?php $this->status_badge( $event->status, $this->email_status_label( $event->status ) ); ?>
					</div>
					<div class="subin-source-card__grid">
						<div class="subin-detail-item">
							<span><?php esc_html_e( 'To', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( $event->to_email ); ?></strong>
						</div>
						<div class="subin-detail-item">
							<span><?php esc_html_e( 'Date', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( $this->format_date( $event->created_at ) ); ?></strong>
						</div>
					</div>
					<?php if ( ! empty( $event->error_message ) ) : ?>
						<?php if ( ! empty( $event_issue ) ) : ?>
							<div class="subin-detail-item">
								<span><?php esc_html_e( 'Issue', 'submission-inbox' ); ?></span>
								<strong><?php echo esc_html( $event_issue['title'] ); ?></strong>
							</div>
						<?php endif; ?>
						<div class="subin-detail-item subin-detail-message">
							<span><?php esc_html_e( 'Technical error', 'submission-inbox' ); ?></span>
							<strong><?php echo esc_html( ! empty( $event_issue['technical_error'] ) ? $event_issue['technical_error'] : $this->error_classifier->sanitize_technical_error( (string) $event->error_message ) ); ?></strong>
						</div>
					<?php endif; ?>
				</article>
			<?php endforeach; ?>
		</div>
		<?php
	}

	/**
	 * Handle POST actions.
	 *
	 * @return void
	 */
	private function maybe_handle_action() {
		$request_method = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) );
		if ( 'POST' !== strtoupper( $request_method ) ) {
			return;
		}

		$raw_action  = $_POST['subin_lead_action'] ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified before the action is executed.
		$raw_lead_id = $_POST['lead_id'] ?? 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified before the action is executed.
		$action      = is_string( $raw_action ) ? sanitize_key( wp_unslash( $raw_action ) ) : '';
		$lead_id     = is_scalar( $raw_lead_id ) ? absint( wp_unslash( $raw_lead_id ) ) : 0;
		$required_capability = $this->required_capability_for_action( $action );
		if ( '' !== $required_capability && ! \SUBIN\Security\Capabilities::can_current_user( $required_capability ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this lead action.', 'submission-inbox' ) );
		}

		if ( 'mark_all_seen' === $action ) {
			$raw_high_water_id = $_POST['status_high_water_id'] ?? null; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Included in the nonce action verified below.
			if ( ! is_scalar( $raw_high_water_id ) || 1 !== preg_match( '/^[1-9]\d*$/', (string) wp_unslash( $raw_high_water_id ) ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'all_leads_mark_seen_conflict' ) );
			}
			$high_water_id = absint( wp_unslash( $raw_high_water_id ) );
			$raw_nonce = $_POST['subin_lead_nonce'] ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified immediately below.
			if ( ! is_string( $raw_nonce ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $raw_nonce ) ), 'subin_mark_all_seen_' . $high_water_id ) ) {
				add_settings_error(
					'subin_messages',
					'subin_security_check_failed',
					__( 'Security check failed. Please try again.', 'submission-inbox' ),
					'error'
				);
				return;
			}

			$result = $this->leads->update_status_bulk_through_id(
				\SUBIN\Leads\LeadRepository::STATUS_NEW,
				\SUBIN\Leads\LeadRepository::STATUS_SEEN,
				$high_water_id
			);
			if ( false === $result ) {
				$this->safe_admin_redirect( $this->current_list_url( 'all_leads_mark_seen_failed' ) );
			}
			$this->safe_admin_redirect(
				add_query_arg(
					'subin_updated_count',
					absint( $result ),
					$this->current_list_url( 'all_leads_mark_seen' )
				)
			);
		}

		if ( 'bulk_update' === $action ) {
			$raw_nonce = $_POST['subin_bulk_nonce'] ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified immediately below.
			if ( ! is_string( $raw_nonce ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $raw_nonce ) ), 'subin_bulk_update_leads' ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'bulk_security_failed' ) );
			}

			$raw_lead_ids    = isset( $_POST['lead_ids'] ) && is_array( $_POST['lead_ids'] ) ? wp_unslash( $_POST['lead_ids'] ) : array();
			$raw_bulk_action = $_POST['bulk_action'] ?? ( $_POST['bulk_status'] ?? '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above; bulk_status preserves the previous form contract.
			$bulk_action     = is_string( $raw_bulk_action ) ? sanitize_key( wp_unslash( $raw_bulk_action ) ) : '';
			$allowed_statuses = array(
				\SUBIN\Leads\LeadRepository::STATUS_NEW,
				\SUBIN\Leads\LeadRepository::STATUS_SEEN,
				\SUBIN\Leads\LeadRepository::STATUS_ARCHIVED,
			);

			if ( empty( $raw_lead_ids ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'bulk_no_selection' ) );
			}

			$workflow_actions = array( 'set_workflow', 'set_priority', 'assign', 'set_follow_up', 'clear_follow_up' );
			if ( ! in_array( $bulk_action, array_merge( $allowed_statuses, $workflow_actions ), true ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'bulk_invalid_action' ) );
			}

			$lead_ids = array();
			foreach ( $raw_lead_ids as $raw_selected_id ) {
				if ( ! is_scalar( $raw_selected_id ) ) {
					continue;
				}

				$selected_id = absint( $raw_selected_id );
				if ( $selected_id <= 0 || in_array( $selected_id, $lead_ids, true ) ) {
					continue;
				}

				$lead_ids[] = $selected_id;
				if ( count( $lead_ids ) >= self::MAX_BULK_IDS ) {
					break;
				}
			}
			if ( empty( $lead_ids ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'bulk_no_selection' ) );
			}

			$selected_leads = $this->leads->get_by_ids( $lead_ids );

			if ( in_array( $bulk_action, $allowed_statuses, true ) ) {
				$status_versions = $this->bulk_status_versions_from_request( $lead_ids );
				$succeeded       = 0;
				$conflicted      = 0;
				$failed          = 0;
				$unchanged       = 0;
				foreach ( $lead_ids as $changed_id ) {
					$changed_id    = absint( $changed_id );
					$selected_lead = $selected_leads[ $changed_id ] ?? null;
					if ( ! is_object( $selected_lead ) ) {
						++$conflicted;
						continue;
					}

					$change_needed = $bulk_action !== sanitize_key( $selected_lead->status ?? '' );
					$result        = $this->leads->update_status_if_current( $changed_id, $bulk_action, $status_versions[ $changed_id ] ?? null );
					if ( true === $result ) {
						if ( $change_needed ) {
							++$succeeded;
							$this->activity->add_activity(
								$changed_id,
								'lead_status_updated',
								sprintf(
									/* translators: %s: lead visibility status label. */
									__( 'Lead marked as %s in bulk.', 'submission-inbox' ),
									$this->lead_status_label( $bulk_action )
								),
								array( 'status' => $bulk_action ),
								get_current_user_id()
							);
						} else {
							++$unchanged;
						}
					} elseif ( is_wp_error( $result ) && in_array( $result->get_error_code(), array( 'subin_status_conflict', 'subin_status_version_invalid' ), true ) ) {
						++$conflicted;
					} else {
						++$failed;
					}
				}

				if ( 0 === $succeeded && 0 === $conflicted && 0 === $failed ) {
					$this->safe_admin_redirect( $this->current_list_url( 'bulk_no_change' ) );
				}

				if ( $conflicted > 0 || $failed > 0 ) {
					if ( $conflicted > 0 && 0 === $succeeded && 0 === $failed ) {
						$partial_notice = 'bulk_status_conflict';
					} elseif ( $conflicted > 0 || $succeeded > 0 ) {
						$partial_notice = 'bulk_status_partial';
					} else {
						$partial_notice = 'bulk_update_failed';
					}
					$this->safe_admin_redirect(
						add_query_arg(
							array(
								'subin_updated_count'   => $succeeded,
								'subin_conflict_count'  => $conflicted,
								'subin_failed_count'    => $failed,
								'subin_unchanged_count' => $unchanged,
							),
							$this->current_list_url( $partial_notice )
						)
					);
				}

				$this->safe_admin_redirect(
					add_query_arg(
						array(
							'subin_updated_count'   => $succeeded,
							'subin_unchanged_count' => $unchanged,
						),
						$this->current_list_url( 'bulk_status_updated' )
					)
				);
			}

			$workflow_data = $this->bulk_workflow_data_from_request( $bulk_action );
			if ( null === $workflow_data || empty( $workflow_data ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'bulk_invalid_action' ) );
			}

			$changed_leads = array_filter(
				$selected_leads,
				function ( $selected_lead ) use ( $workflow_data ) {
					return $this->workflow_change_needed( $selected_lead, $workflow_data );
				}
			);
			if ( empty( $changed_leads ) ) {
				$this->safe_admin_redirect( $this->current_list_url( 'bulk_no_change' ) );
			}

			$workflow_versions = $this->bulk_workflow_versions_from_request( array_keys( $changed_leads ) );
			$succeeded          = 0;
			$conflicted         = 0;
			$failed             = 0;
			foreach ( $changed_leads as $selected_lead_id => $selected_lead ) {
				unset( $selected_lead );
				$selected_lead_id = absint( $selected_lead_id );
				$result           = $this->workflow->update_workflow_if_current(
					$selected_lead_id,
					$workflow_data,
					$workflow_versions[ $selected_lead_id ] ?? null,
					get_current_user_id()
				);
				if ( true === $result ) {
					++$succeeded;
				} elseif ( is_wp_error( $result ) && in_array( $result->get_error_code(), array( 'subin_workflow_conflict', 'subin_workflow_version_invalid' ), true ) ) {
					++$conflicted;
				} else {
					++$failed;
				}
			}

			if ( $conflicted > 0 || $failed > 0 ) {
				if ( $conflicted > 0 && 0 === $succeeded && 0 === $failed ) {
					$partial_notice = 'bulk_workflow_conflict';
				} elseif ( $conflicted > 0 || $succeeded > 0 ) {
					$partial_notice = 'bulk_workflow_partial';
				} else {
					$partial_notice = 'bulk_update_failed';
				}
				$this->safe_admin_redirect(
					add_query_arg(
						array(
							'subin_updated_count'  => $succeeded,
							'subin_conflict_count' => $conflicted,
							'subin_failed_count'   => $failed,
						),
						$this->current_list_url( $partial_notice )
					)
				);
			}
			$this->safe_admin_redirect( add_query_arg( 'subin_updated_count', $succeeded, $this->current_list_url( 'bulk_workflow_updated' ) ) );
		}

		if ( ! in_array( $action, array( 'new', 'seen', 'archived', 'resend_notification', 'update_workflow', 'add_note', 'update_note', 'delete_note' ), true ) || $lead_id <= 0 ) {
			return;
		}

		$raw_nonce = $_POST['subin_lead_nonce'] ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified immediately below.
		if ( ! is_string( $raw_nonce ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $raw_nonce ) ), 'subin_update_lead_' . $lead_id ) ) {
			add_settings_error(
				'subin_messages',
				'subin_security_check_failed',
				__( 'Security check failed. Please try again.', 'submission-inbox' ),
				'error'
			);
			return;
		}

		if ( 'resend_notification' === $action ) {
			$notice = $this->queue_notification_retry( $lead_id ) ? 'notification_queued' : 'notification_failed';
			$this->safe_admin_redirect( $this->detail_url( $lead_id, true, $notice ) );
		}

		if ( 'update_workflow' === $action ) {
			$workflow_data        = $this->workflow_data_from_request();
			$raw_workflow_version = $_POST['workflow_version'] ?? null; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Action nonce verified above.
			if ( is_scalar( $raw_workflow_version ) ) {
				$raw_workflow_version = wp_unslash( $raw_workflow_version );
			}
			$result = is_array( $workflow_data )
				? $this->workflow->update_workflow_if_current( $lead_id, $workflow_data, $raw_workflow_version, get_current_user_id() )
				: false;
			$raw_return_to = $_POST['subin_return_to'] ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Action nonce verified above.
			$return_to     = is_string( $raw_return_to ) ? sanitize_key( wp_unslash( $raw_return_to ) ) : '';
			if ( true === $result ) {
				$notice = 'workflow_updated';
			} elseif ( is_wp_error( $result ) && in_array( $result->get_error_code(), array( 'subin_workflow_conflict', 'subin_workflow_version_invalid' ), true ) ) {
				$notice = 'workflow_conflict';
			} else {
				$notice = 'workflow_failed';
			}
			$this->safe_admin_redirect( 'inbox' === $return_to ? $this->current_list_url( $notice ) : $this->detail_url( $lead_id, true, $notice ) );
		}

		if ( 'add_note' === $action ) {
			$note_text = $this->note_text_from_request();
			$result    = null !== $note_text
				? $this->activity->add_note_checked( $lead_id, $note_text, get_current_user_id() )
				: new \WP_Error( 'subin_note_value_invalid', __( 'The note text is invalid.', 'submission-inbox' ) );
			$this->redirect_after_note_result( $lead_id, $result, 'note_added', 'note_failed' );
		}

		if ( 'update_note' === $action ) {
			$note_text = $this->note_text_from_request();
			$note_id   = $this->positive_integer_from_request( 'note_id' );
			$result    = null !== $note_text
				? $this->activity->update_note_if_current(
					$note_id,
					$lead_id,
					$note_text,
					$this->positive_integer_from_request( 'note_version' ),
					get_current_user_id()
				)
				: new \WP_Error( 'subin_note_value_invalid', __( 'The note text is invalid.', 'submission-inbox' ) );
			if ( is_wp_error( $result ) && 'subin_note_conflict' === $result->get_error_code() && null !== $note_text && $note_id > 0 ) {
				$this->note_conflict_draft = array(
					'lead_id' => $lead_id,
					'note_id' => $note_id,
					'note'    => $note_text,
				);

				return;
			}
			$this->redirect_after_note_result( $lead_id, $result, 'note_updated', 'note_update_failed' );
		}

		if ( 'delete_note' === $action ) {
			$result = $this->activity->delete_note_if_current(
				$this->positive_integer_from_request( 'note_id' ),
				$lead_id,
				$this->positive_integer_from_request( 'note_version' ),
				get_current_user_id()
			);
			$this->redirect_after_note_result( $lead_id, $result, 'note_deleted', 'note_delete_failed' );
		}

		$raw_return_to = $_POST['subin_return_to'] ?? ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Action nonce verified above.
		$return_to     = is_string( $raw_return_to ) ? sanitize_key( wp_unslash( $raw_return_to ) ) : '';
		$current_lead       = $this->leads->get( $lead_id );
		$raw_status_version = $_POST['status_version'] ?? null; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Action nonce verified above.
		if ( is_scalar( $raw_status_version ) ) {
			$raw_status_version = wp_unslash( $raw_status_version );
		}
		$result = $current_lead ? $this->leads->update_status_if_current( $lead_id, $action, $raw_status_version ) : false;

		if ( true !== $result ) {
			$conflict = is_wp_error( $result ) && in_array( $result->get_error_code(), array( 'subin_status_conflict', 'subin_status_version_invalid' ), true );
			if ( $conflict ) {
				$this->safe_admin_redirect( 'inbox' === $return_to ? $this->current_list_url( 'lead_status_conflict' ) : $this->detail_url( $lead_id, true, 'status_conflict' ) );
			}
			$this->safe_admin_redirect( 'inbox' === $return_to ? $this->current_list_url( 'lead_status_failed' ) : $this->detail_url( $lead_id, true, 'status_failed' ) );
		}

		if ( $action !== sanitize_key( $current_lead->status ?? '' ) ) {
			$this->activity->add_activity(
				$lead_id,
				'lead_status_updated',
				sprintf(
					/* translators: %s: lead visibility status label. */
					__( 'Lead marked as %s.', 'submission-inbox' ),
					$this->lead_status_label( $action )
				),
				array( 'status' => $action ),
				get_current_user_id()
			);
		}

		$this->safe_admin_redirect( 'inbox' === $return_to ? $this->current_list_url( 'lead_status_updated' ) : $this->detail_url( $lead_id, true, $action ) );
	}

	/**
	 * Read a strictly scalar note body from a nonce-verified request.
	 *
	 * @return string|null
	 */
	private function note_text_from_request() {
		$raw_note = $_POST['lead_note'] ?? null; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The lead action nonce is verified before this parser is called.
		if ( ! is_string( $raw_note ) ) {
			return null;
		}

		return wp_unslash( $raw_note );
	}

	/**
	 * Read one positive integer note token without converting malformed values.
	 *
	 * @param string $request_key Request key.
	 * @return int|null
	 */
	private function positive_integer_from_request( $request_key ) {
		$request_key = sanitize_key( (string) $request_key );
		if ( ! in_array( $request_key, array( 'note_id', 'note_version' ), true ) ) {
			return null;
		}

		$raw_value = $_POST[ $request_key ] ?? null; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The lead action nonce is verified before this parser is called.
		if ( ! is_string( $raw_value ) && ! is_int( $raw_value ) ) {
			return null;
		}
		$raw_value = wp_unslash( $raw_value );
		if ( 1 !== preg_match( '/^[1-9]\d*$/', (string) $raw_value ) ) {
			return null;
		}

		return absint( $raw_value );
	}

	/**
	 * Convert a checked note result into a clear, non-destructive UI outcome.
	 *
	 * @param int    $lead_id Lead id.
	 * @param mixed  $result Repository result.
	 * @param string $success_notice Success notice.
	 * @param string $failure_notice Generic failure notice.
	 * @return void
	 */
	private function redirect_after_note_result( $lead_id, $result, $success_notice, $failure_notice ) {
		$lead_id = absint( $lead_id );
		if ( true === $result || ( is_int( $result ) && $result > 0 ) ) {
			$this->safe_admin_redirect( $this->detail_url( $lead_id, true, $success_notice ) );
		}

		if ( is_wp_error( $result ) ) {
			$error_code = $result->get_error_code();
			if ( 'subin_note_lead_missing' === $error_code ) {
				$this->safe_admin_redirect( $this->current_list_url( 'lead_unavailable' ) );
			}
			if ( in_array( $error_code, array( 'subin_note_conflict', 'subin_note_version_invalid' ), true ) ) {
				$this->safe_admin_redirect( $this->detail_url( $lead_id, true, 'note_conflict' ) );
			}
			if ( in_array( $error_code, array( 'subin_note_value_invalid', 'subin_note_too_long', 'subin_note_id_invalid' ), true ) ) {
				$this->safe_admin_redirect( $this->detail_url( $lead_id, true, 'note_invalid' ) );
			}
		}

		$this->safe_admin_redirect( $this->detail_url( $lead_id, true, $failure_notice ) );
	}

	/**
	 * Read only workflow fields explicitly included in the request.
	 *
	 * Keeping omitted fields out of the payload prevents a narrow action from
	 * resetting unrelated workflow state to a default value.
	 *
	 * @return array<string,mixed>|null Null when a supplied value is malformed.
	 */
	private function workflow_data_from_request() {
		$data   = array();
		$fields = array( 'workflow_status', 'priority', 'assigned_user_id', 'follow_up_at' );

		foreach ( $fields as $field ) {
			if ( ! array_key_exists( $field, $_POST ) ) {
				continue;
			}

			$raw_value = $_POST[ $field ]; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The lead action nonce is verified before this parser is called.
			if ( ! is_scalar( $raw_value ) ) {
				return null;
			}

			$raw_value = wp_unslash( $raw_value );
			if ( 'assigned_user_id' === $field ) {
				if ( 1 !== preg_match( '/^\d+$/', (string) $raw_value ) ) {
					return null;
				}
				$data[ $field ] = absint( $raw_value );
			} elseif ( 'follow_up_at' === $field ) {
				$data[ $field ] = sanitize_text_field( $raw_value );
			} else {
				$data[ $field ] = sanitize_key( $raw_value );
			}
		}

		return $data;
	}

	/**
	 * Build and validate one bounded workflow mutation for a bulk action.
	 *
	 * @param string $action Bulk action key.
	 * @return array<string,mixed>|null
	 */
	private function bulk_workflow_data_from_request( $action ) {
		$action = sanitize_key( (string) $action );

		if ( 'clear_follow_up' === $action ) {
			return array( 'follow_up_at' => '' );
		}

		$field_map = array(
			'set_workflow'  => 'bulk_workflow_status',
			'set_priority'  => 'bulk_priority',
			'assign'        => 'bulk_assigned_user_id',
			'set_follow_up' => 'bulk_follow_up_at',
		);
		if ( ! isset( $field_map[ $action ] ) || ! array_key_exists( $field_map[ $action ], $_POST ) ) {
			return null;
		}

		$request_field = $field_map[ $action ];
		$raw_value     = $_POST[ $request_field ]; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The bulk action nonce is verified before this parser is called.
		if ( ! is_scalar( $raw_value ) ) {
			return null;
		}
		$raw_value = wp_unslash( $raw_value );

		if ( 'set_workflow' === $action ) {
			$value = sanitize_key( $raw_value );
			return array_key_exists( $value, $this->workflow->workflow_statuses() ) ? array( 'workflow_status' => $value ) : null;
		}

		if ( 'set_priority' === $action ) {
			$value = sanitize_key( $raw_value );
			return array_key_exists( $value, $this->workflow->priorities() ) ? array( 'priority' => $value ) : null;
		}

		if ( 'assign' === $action ) {
			if ( 1 !== preg_match( '/^\d+$/', (string) $raw_value ) ) {
				return null;
			}
			return array( 'assigned_user_id' => absint( $raw_value ) );
		}

		$value = sanitize_text_field( $raw_value );
		return '' !== $value && null !== $this->workflow->sanitize_follow_up_at( $value ) ? array( 'follow_up_at' => $value ) : null;
	}

	/**
	 * Read the browser-rendered workflow version for each selected lead.
	 *
	 * Missing or malformed values stay null so the strict repository operation
	 * fails closed and reports a conflict instead of using freshly read state.
	 *
	 * @param array<int,int|string> $lead_ids Selected lead ids.
	 * @return array<int,int|null>
	 */
	private function bulk_workflow_versions_from_request( array $lead_ids ) {
		return $this->bulk_versions_from_request( 'workflow_versions', $lead_ids );
	}

	/**
	 * Read browser-rendered visibility versions for selected leads.
	 *
	 * @param array<int,int|string> $lead_ids Selected lead ids.
	 * @return array<int,int|null>
	 */
	private function bulk_status_versions_from_request( array $lead_ids ) {
		return $this->bulk_versions_from_request( 'status_versions', $lead_ids );
	}

	/**
	 * Strictly parse a per-lead version map from a nonce-verified bulk request.
	 *
	 * @param string                $request_key POST map key.
	 * @param array<int,int|string> $lead_ids Selected lead ids.
	 * @return array<int,int|null>
	 */
	private function bulk_versions_from_request( $request_key, array $lead_ids ) {
		$request_key = sanitize_key( (string) $request_key );
		$raw_maps    = array( 'workflow_versions', 'status_versions' );
		if ( ! in_array( $request_key, $raw_maps, true ) ) {
			return array();
		}
		$raw_versions = isset( $_POST[ $request_key ] ) && is_array( $_POST[ $request_key ] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The bulk action nonce is verified before this parser is called.
			? wp_unslash( $_POST[ $request_key ] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
			: array();
		$versions     = array();

		foreach ( array_unique( array_map( 'absint', $lead_ids ) ) as $lead_id ) {
			$raw_version = $raw_versions[ $lead_id ] ?? null;
			$versions[ $lead_id ] = is_scalar( $raw_version ) && 1 === preg_match( '/^[1-9]\d*$/', (string) $raw_version )
				? absint( $raw_version )
				: null;
		}

		return $versions;
	}

	/**
	 * Determine whether a lead differs from a partial workflow payload.
	 *
	 * @param object              $lead Lead row.
	 * @param array<string,mixed> $data Partial workflow payload.
	 * @return bool
	 */
	private function workflow_change_needed( $lead, array $data ) {
		return ! empty( $this->changed_workflow_data( $lead, $data ) );
	}

	/**
	 * Keep only workflow fields whose submitted value differs from a lead.
	 *
	 * Date controls intentionally compare at calendar-day precision so saving a
	 * different field never rewrites a pre-existing follow-up time.
	 *
	 * @param object              $lead Lead row.
	 * @param array<string,mixed> $data Partial workflow payload.
	 * @return array<string,mixed>
	 */
	private function changed_workflow_data( $lead, array $data ) {
		$changed = array();

		foreach ( $data as $field => $value ) {
			if ( 'workflow_status' === $field || 'priority' === $field ) {
				if ( sanitize_key( (string) ( $lead->{$field} ?? '' ) ) !== sanitize_key( (string) $value ) ) {
					$changed[ $field ] = $value;
				}
				continue;
			}

			if ( 'assigned_user_id' === $field ) {
				if ( absint( $lead->assigned_user_id ?? 0 ) !== absint( $value ) ) {
					$changed[ $field ] = $value;
				}
				continue;
			}

			if ( 'follow_up_at' === $field ) {
				$current_date = $this->follow_up_date_value( $lead->follow_up_at ?? '' );
				$desired_date = sanitize_text_field( (string) $value );
				if ( $current_date !== $desired_date ) {
					$changed[ $field ] = $value;
				}
			}
		}

		return $changed;
	}

	/**
	 * Show list notice after list actions.
	 *
	 * @return void
	 */
	private function maybe_add_list_notice() {
		$notice          = SUBIN_Request::get_key( 'notice' );
		$updated_count   = SUBIN_Request::get_absint( 'subin_updated_count' );
		$conflict_count  = SUBIN_Request::get_absint( 'subin_conflict_count' );
		$failed_count    = SUBIN_Request::get_absint( 'subin_failed_count' );
		$unchanged_count = SUBIN_Request::get_absint( 'subin_unchanged_count' );
		$mark_all_message = $updated_count > 0
			? sprintf(
				/* translators: %d: number of leads marked as seen. */
				_n( '%d previously unseen lead marked as seen. Newer leads remain unseen.', '%d previously unseen leads marked as seen. Newer leads remain unseen.', $updated_count, 'submission-inbox' ),
				$updated_count
			)
			: __( 'No previously unseen leads needed an update. Newer leads remain unseen.', 'submission-inbox' );
		$bulk_status_updated_message = $unchanged_count > 0
			? sprintf(
				/* translators: 1: updated leads, 2: already matching leads. */
				__( '%1$d statuses updated; %2$d already matched the requested status.', 'submission-inbox' ),
				$updated_count,
				$unchanged_count
			)
			: sprintf(
				/* translators: %d: number of updated leads. */
				_n( '%d lead status updated.', '%d lead statuses updated.', $updated_count, 'submission-inbox' ),
				$updated_count
			);
		$bulk_status_conflict_message = $unchanged_count > 0
			? sprintf(
				/* translators: 1: conflicting leads, 2: already matching leads. */
				__( '%1$d leads changed or were removed since this page loaded; %2$d already matched the requested status. Conflicting leads were not changed.', 'submission-inbox' ),
				$conflict_count,
				$unchanged_count
			)
			: sprintf(
				/* translators: %d: number of conflicting leads. */
				_n( '%d lead changed or was removed since this page loaded and its status was not updated.', '%d leads changed or were removed since this page loaded and their statuses were not updated.', $conflict_count, 'submission-inbox' ),
				$conflict_count
			);

		if ( ! in_array( $notice, array( 'lead_deleted', 'lead_delete_failed', 'lead_unavailable', 'lead_status_updated', 'lead_status_conflict', 'lead_status_failed', 'workflow_updated', 'workflow_conflict', 'workflow_failed', 'all_leads_mark_seen', 'all_leads_mark_seen_conflict', 'all_leads_mark_seen_failed', 'bulk_updated', 'bulk_status_updated', 'bulk_status_conflict', 'bulk_status_partial', 'bulk_workflow_updated', 'bulk_workflow_conflict', 'bulk_workflow_partial', 'bulk_no_selection', 'bulk_invalid_action', 'bulk_no_change', 'bulk_update_failed', 'bulk_security_failed' ), true ) ) {
			return;
		}

		$messages = array(
			'lead_deleted'               => array( 'updated', __( 'Lead deleted.', 'submission-inbox' ) ),
			'lead_delete_failed'         => array( 'error', __( 'Lead could not be deleted.', 'submission-inbox' ) ),
			'lead_unavailable'           => array( 'warning', __( 'This lead was removed while you were working. The note action was not applied.', 'submission-inbox' ) ),
			'lead_status_updated'        => array( 'updated', __( 'Lead status updated.', 'submission-inbox' ) ),
			'lead_status_conflict'       => array( 'warning', __( 'This lead changed after the page loaded. Its status was not changed. Review the latest value and try again.', 'submission-inbox' ) ),
			'lead_status_failed'         => array( 'error', __( 'Lead status could not be updated.', 'submission-inbox' ) ),
			'workflow_updated'           => array( 'updated', __( 'Lead workflow saved.', 'submission-inbox' ) ),
			'workflow_conflict'          => array( 'warning', __( 'This lead changed after you opened it. Your changes were not saved. Review the latest workflow values and try again.', 'submission-inbox' ) ),
			'workflow_failed'            => array( 'error', __( 'Lead workflow could not be saved.', 'submission-inbox' ) ),
			'all_leads_mark_seen'        => array( 'updated', $mark_all_message ),
			'all_leads_mark_seen_conflict' => array( 'warning', __( 'The Mark all seen action expired. Refresh the Inbox so newer leads are not changed accidentally.', 'submission-inbox' ) ),
			'all_leads_mark_seen_failed' => array( 'error', __( 'Unseen leads could not be marked as seen.', 'submission-inbox' ) ),
			'bulk_updated'                => array( 'updated', __( 'Selected leads updated.', 'submission-inbox' ) ),
			'bulk_status_updated'         => array(
				'updated',
				$bulk_status_updated_message,
			),
			'bulk_status_partial'         => array(
				'warning',
				sprintf(
					/* translators: 1: updated leads, 2: conflicts, 3: failed leads, 4: already matching leads. */
					__( '%1$d statuses updated; %2$d leads changed or were removed since this page loaded; %3$d failed; %4$d already matched. Conflicting leads were not changed.', 'submission-inbox' ),
					$updated_count,
					$conflict_count,
					$failed_count,
					$unchanged_count
				),
			),
			'bulk_status_conflict'        => array(
				'warning',
				$bulk_status_conflict_message,
			),
			'bulk_workflow_updated'       => array(
				'updated',
				sprintf(
					/* translators: %d: number of updated leads. */
					_n( '%d lead workflow updated.', '%d lead workflows updated.', $updated_count, 'submission-inbox' ),
					$updated_count
				),
			),
			'bulk_workflow_partial'       => array(
				'warning',
				sprintf(
					/* translators: 1: updated leads, 2: conflicts, 3: failed leads. */
					__( '%1$d updated; %2$d changed since this page loaded; %3$d failed. Conflicting leads were not changed.', 'submission-inbox' ),
					$updated_count,
					$conflict_count,
					$failed_count
				),
			),
			'bulk_workflow_conflict'      => array(
				'warning',
				sprintf(
					/* translators: %d: number of conflicting leads. */
					_n( '%d lead changed since this page loaded and was not updated. Review it and try again.', '%d leads changed since this page loaded and were not updated. Review them and try again.', $conflict_count, 'submission-inbox' ),
					$conflict_count
				),
			),
			'bulk_no_selection'           => array( 'warning', __( 'Select at least one lead before applying a bulk action.', 'submission-inbox' ) ),
			'bulk_invalid_action'         => array( 'error', __( 'Choose a valid bulk action.', 'submission-inbox' ) ),
			'bulk_no_change'              => array( 'info', __( 'The selected leads already have that value.', 'submission-inbox' ) ),
			'bulk_update_failed'          => array( 'error', __( 'Selected leads could not be updated.', 'submission-inbox' ) ),
			'bulk_security_failed'        => array( 'error', __( 'Security check failed. Refresh the page and try again.', 'submission-inbox' ) ),
		);

		add_settings_error(
			'subin_messages',
			'subin_lead_list_action',
			$messages[ $notice ][1],
			$messages[ $notice ][0]
		);
	}

	/**
	 * Show lead action notice after redirect.
	 *
	 * @return void
	 */
	private function maybe_add_detail_notice() {
		$notice = SUBIN_Request::get_key( 'subin_notice' );

		if ( ! in_array( $notice, array( 'new', 'seen', 'archived', 'status_conflict', 'status_failed', 'notification_sent', 'notification_queued', 'notification_failed', 'workflow_updated', 'workflow_conflict', 'workflow_failed', 'note_added', 'note_failed', 'note_updated', 'note_update_failed', 'note_deleted', 'note_delete_failed', 'note_conflict', 'note_invalid' ), true ) ) {
			return;
		}

		$messages = array(
			'new'                 => array( 'updated', __( 'Lead marked as new.', 'submission-inbox' ) ),
			'seen'                => array( 'updated', __( 'Lead marked as seen.', 'submission-inbox' ) ),
			'archived'            => array( 'updated', __( 'Lead archived.', 'submission-inbox' ) ),
			'status_conflict'     => array( 'warning', __( 'This lead changed after the page loaded. Its status was not changed. Review the latest value and try again.', 'submission-inbox' ) ),
			'status_failed'        => array( 'error', __( 'Lead status could not be updated.', 'submission-inbox' ) ),
			'notification_sent'   => array( 'updated', __( 'Notification was accepted by WordPress; delivery is not verified.', 'submission-inbox' ) ),
			'notification_queued' => array( 'updated', __( 'Notification retry queued.', 'submission-inbox' ) ),
			'notification_failed' => array( 'error', __( 'Notification could not be sent.', 'submission-inbox' ) ),
			'workflow_updated'    => array( 'updated', __( 'Workflow saved.', 'submission-inbox' ) ),
			'workflow_conflict'   => array( 'warning', __( 'This lead changed after you opened it. Your changes were not saved. Review the latest workflow values and try again.', 'submission-inbox' ) ),
			'workflow_failed'     => array( 'error', __( 'Workflow could not be saved.', 'submission-inbox' ) ),
			'note_added'          => array( 'updated', __( 'Note added.', 'submission-inbox' ) ),
			'note_failed'         => array( 'error', __( 'Note could not be added.', 'submission-inbox' ) ),
			'note_updated'        => array( 'updated', __( 'Note updated.', 'submission-inbox' ) ),
			'note_update_failed'  => array( 'error', __( 'Note could not be updated.', 'submission-inbox' ) ),
			'note_deleted'        => array( 'updated', __( 'Note deleted.', 'submission-inbox' ) ),
			'note_delete_failed'  => array( 'error', __( 'Note could not be deleted.', 'submission-inbox' ) ),
			'note_conflict'       => array( 'warning', __( 'This note changed or was removed after you opened it. Your action was not applied. Review the latest notes and try again.', 'submission-inbox' ) ),
			'note_invalid'        => array( 'error', __( 'Enter a note between 1 and 5,000 characters and try again.', 'submission-inbox' ) ),
		);

		add_settings_error(
			'subin_messages',
			'subin_lead_updated',
			$messages[ $notice ][1],
			$messages[ $notice ][0]
		);

	}

	/**
	 * Resolve the permission required by a state-changing Inbox action.
	 *
	 * Unknown actions deliberately resolve to no capability and are ignored by
	 * the dispatcher rather than inheriting a broader permission accidentally.
	 *
	 * @param string $action Sanitized action key.
	 * @return string
	 */
	private function required_capability_for_action( $action ) {
		$action = sanitize_key( (string) $action );
		if ( 'resend_notification' === $action ) {
			return \SUBIN\Security\Capabilities::RETRY_NOTIFICATIONS;
		}

		if ( in_array( $action, array( 'mark_all_seen', 'bulk_update', 'new', 'seen', 'archived', 'update_workflow', 'add_note', 'update_note', 'delete_note' ), true ) ) {
			return \SUBIN\Security\Capabilities::MANAGE_LEADS;
		}

		return '';
	}

	/**
	 * Render a status action button.
	 *
	 * @param int    $lead_id Lead id.
	 * @param string $action Action.
	 * @param string $label Button label.
	 * @param string $button_class Button CSS classes.
	 * @param string $confirm Confirmation prompt.
	 * @param string $return_to Return context.
	 * @param mixed  $status_version Version rendered with a visibility action.
	 * @return void
	 */
	private function render_status_action_button( $lead_id, $action, $label, $button_class = '', $confirm = '', $return_to = '', $status_version = null ) {
		$required_capability = $this->required_capability_for_action( $action );
		if ( '' === $required_capability || ! \SUBIN\Security\Capabilities::can_current_user( $required_capability ) ) {
			return;
		}
		$is_visibility_action = in_array( $action, array( 'new', 'seen', 'archived' ), true );
		if ( $is_visibility_action && ( ! is_scalar( $status_version ) || 1 !== preg_match( '/^[1-9]\d*$/', (string) $status_version ) ) ) {
			return;
		}

		$button_class = '' !== $button_class ? $button_class : 'button button-secondary';
		?>
		<form method="post" action="">
			<input type="hidden" name="subin_lead_action" value="<?php echo esc_attr( $action ); ?>" />
			<input type="hidden" name="lead_id" value="<?php echo esc_attr( (string) $lead_id ); ?>" />
			<?php if ( $is_visibility_action ) : ?>
				<input type="hidden" name="status_version" value="<?php echo esc_attr( (string) absint( $status_version ) ); ?>" />
			<?php endif; ?>
			<?php if ( 'inbox' === $return_to ) : ?>
				<input type="hidden" name="subin_return_to" value="inbox" />
			<?php endif; ?>
			<?php wp_nonce_field( 'subin_update_lead_' . $lead_id, 'subin_lead_nonce' ); ?>
			<button type="submit" class="<?php echo esc_attr( $button_class ); ?>"<?php echo '' !== $confirm ? ' onclick="return confirm(\'' . esc_js( $confirm ) . '\');"' : ''; ?>><?php echo esc_html( $label ); ?></button>
		</form>
		<?php
	}

	/**
	 * Queue a saved lead notification retry.
	 *
	 * @param int $lead_id Lead id.
	 * @return bool
	 */
	private function queue_notification_retry( $lead_id ) {
		if ( ! \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::RETRY_NOTIFICATIONS ) ) {
			return false;
		}

		$lead_id = absint( $lead_id );
		$lead    = $this->leads->get( $lead_id );
		if ( ! $lead || 'submission_inbox_form' !== sanitize_key( $lead->provider ?? '' ) ) {
			return false;
		}

		$jobs   = new \SUBIN\Notifications\NotificationJobRepository();
		$result = $jobs->retry_saved_lead( $lead_id, \SUBIN\Forms\FormSubmissionHandler::EMAIL_TYPE_ADMIN_NOTIFICATION );
		if ( is_wp_error( $result ) || absint( $result ) <= 0 ) {
			return false;
		}

		$projection = $this->leads->mark_email_retry_pending_if_current(
			$lead_id,
			max( 1, absint( $lead->email_status_version ?? 1 ) )
		);
		if ( is_wp_error( $projection ) ) {
			do_action( 'subin_notification_retry_projection_failed', $lead_id, $projection );
		}
		if ( ! \SUBIN\Forms\FormSubmissionHandler::schedule_notification_worker() ) {
			return false;
		}
		$this->activity->add_activity(
			$lead_id,
			'notification_retry_queued',
			__( 'Admin notification retry queued.', 'submission-inbox' ),
			array( 'notification_job_id' => absint( $result ) ),
			get_current_user_id()
		);

		return true;
	}

	/**
	 * Get notification recipient.
	 *
	 * @return string
	 */
	private function notification_recipient() {
		$settings  = $this->settings->all();
		$recipient = sanitize_email( $settings['notification_recipient_email'] ?? '' );
		if ( is_email( $recipient ) ) {
			return $recipient;
		}

		return sanitize_email( get_option( 'admin_email' ) );
	}

	/**
	 * Build resend notification body.
	 *
	 * @param object            $lead Lead.
	 * @param array<int,object> $fields Fields.
	 * @return string
	 */
	private function notification_body( $lead, array $fields ) {
		$rows = '';
		foreach ( $fields as $field ) {
			if ( ! empty( $field->is_sensitive ) || '' === trim( (string) $field->field_value ) ) {
				continue;
			}

			$rows .= '<tr><th align="left" style="padding:6px 10px;border-bottom:1px solid #eee;">' . esc_html( $field->field_label ) . '</th><td style="padding:6px 10px;border-bottom:1px solid #eee;">' . nl2br( esc_html( $field->field_value ) ) . '</td></tr>';
		}

		if ( '' === $rows ) {
			$rows = '<tr><td colspan="2" style="padding:6px 10px;border-bottom:1px solid #eee;">' . esc_html__( 'No saved fields.', 'submission-inbox' ) . '</td></tr>';
		}

		$detail_url = $this->detail_url( absint( $lead->id ) );

		/* translators: %s: form name. */
		return '<h2>' . esc_html( sprintf( __( 'New lead from %s', 'submission-inbox' ), $lead->form_name ) ) . '</h2>'
			. '<p><strong>' . esc_html__( 'Submitted at:', 'submission-inbox' ) . '</strong> ' . esc_html( $this->format_date( $lead->submitted_at ) ) . '</p>'
			. '<p><strong>' . esc_html__( 'Source:', 'submission-inbox' ) . '</strong> ' . esc_html( $this->provider_label( $lead->provider ) ) . '</p>'
			. '<table cellspacing="0" cellpadding="0" style="border-collapse:collapse;width:100%;max-width:680px;">' . $rows . '</table>'
			. '<p><a href="' . esc_url( $detail_url ) . '">' . esc_html__( 'View lead in WordPress', 'submission-inbox' ) . '</a></p>'
			. '<p>' . esc_html__( 'Sent by InboxMend.', 'submission-inbox' ) . '</p>';
	}

	/**
	 * Export URL for current filters.
	 *
	 * @param array<string,mixed> $filters Filters.
	 * @return string
	 */
	private function export_url( array $filters ) {
		return wp_nonce_url(
			add_query_arg( array_merge( $this->url_args_from_filters( $filters ), array( 'subin_export' => 'leads' ) ), admin_url( 'admin.php' ) ),
			'subin_export_leads',
			'subin_export_nonce'
		);
	}

	/**
	 * Export filtered leads when requested.
	 *
	 * @param array<string,mixed> $filters Filters.
	 * @return void
	 */
	private function maybe_export( array $filters ) {
		if ( 'leads' !== sanitize_key( wp_unslash( $_GET['subin_export'] ?? '' ) ) ) {
			return;
		}
		if ( ! \SUBIN\Security\Capabilities::can_current_user( \SUBIN\Security\Capabilities::EXPORT_LEADS ) ) {
			wp_die( esc_html__( 'You do not have permission to export leads.', 'submission-inbox' ) );
		}

		if ( ! isset( $_GET['subin_export_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['subin_export_nonce'] ) ), 'subin_export_leads' ) ) {
			add_settings_error( 'subin_messages', 'subin_export_invalid', __( 'Security check failed. Please try again.', 'submission-inbox' ), 'error' );
			return;
		}

		$limit           = 5000;
		$leads           = $this->leads->get_leads_for_export( $filters, $limit );
		$field_summaries = $this->fields->get_non_sensitive_summaries_by_lead_ids( wp_list_pluck( $leads, 'id' ) );
		$rows            = array(
			array(
				'Lead ID',
				'Status',
				'Workflow Status',
				'Priority',
				'Assigned User',
				'Follow-up At',
				'Team Notification Status',
				'Visitor Confirmation Status',
				'Source',
				'Form ID',
				'Form Name',
				'Customer Name',
				'Customer Email',
				'Customer Phone',
				'Message Preview',
				'Source URL',
				'Referrer',
				'Submitted At',
				'Created At',
				'Fields Summary',
			),
		);

		foreach ( $leads as $lead ) {
			$rows[] = array(
				$lead->id,
				$this->lead_status_label( $lead->status ),
				$this->workflow_status_label( $lead->workflow_status ?? 'open' ),
				$this->priority_label( $lead->priority ?? 'normal' ),
				$this->assignee_label( $lead->assigned_user_id ?? 0 ),
				$lead->follow_up_at ?? '',
				$this->email_status_label( $lead->email_status ),
				$this->auto_reply_status_label( $lead->auto_reply_status ?? 'not_configured' ),
				$this->provider_label( $lead->provider ),
				$lead->form_id,
				$lead->form_name,
				$lead->customer_name,
				$lead->customer_email,
				$lead->customer_phone,
				$lead->message_preview,
				$lead->source_url,
				$lead->referrer,
				$lead->submitted_at,
				$lead->created_at,
				$field_summaries[ absint( $lead->id ) ] ?? '',
			);
		}

		if ( count( $leads ) >= $limit ) {
			$rows[] = array( 'Export limited to 5000 rows.' );
		}

		$exporter = new \SUBIN\Export\CsvExporter();
		$exporter->export_leads( $rows, 'submission-inbox-leads-' . gmdate( 'Y-m-d' ) . '.csv' );
	}

	/**
	 * Get request filters.
	 *
	 * @return array<string,string>
	 */
	private function get_filters_from_request() {
		$status            = SUBIN_Request::get_key( 'subin_status' );
		$email_status      = SUBIN_Request::get_key( 'subin_email_status' );
		$auto_reply_status = SUBIN_Request::get_key( 'subin_auto_reply_status' );
		$queue             = SUBIN_Request::get_key( 'subin_queue' );
		$provider          = SUBIN_Request::get_key( 'subin_provider' );
		$form_filter       = SUBIN_Request::get_text( 'subin_form_id' );
		$attention         = SUBIN_Request::get_key( 'subin_attention' );
		$workflow_status   = SUBIN_Request::get_key( 'subin_workflow_status' );
		$priority          = SUBIN_Request::get_key( 'subin_priority' );
		$assigned_user_id  = SUBIN_Request::get_text( 'subin_assigned_user_id' );
		$overdue_follow_up = SUBIN_Request::get_key( 'subin_overdue_follow_up' );
		$sort               = SUBIN_Request::get_key( 'subin_sort' );
		$form_provider      = '';
		$form_id            = '';
		$allowed_sorts      = array(
			\SUBIN\Leads\LeadRepository::SORT_NEWEST,
			\SUBIN\Leads\LeadRepository::SORT_OLDEST,
			\SUBIN\Leads\LeadRepository::SORT_RECENTLY_UPDATED,
			\SUBIN\Leads\LeadRepository::SORT_FOLLOW_UP,
			\SUBIN\Leads\LeadRepository::SORT_PRIORITY,
		);
		$allowed_queues     = array_keys( $this->queue_definitions() );

		if ( ! in_array( $queue, $allowed_queues, true ) ) {
			$queue = '';
		}
		$legacy_primary_conflict = '' !== $status || '' !== $email_status || '' !== $auto_reply_status || '' !== $workflow_status || '' !== $assigned_user_id;
		if ( '' === $queue && '1' === $attention && '1' !== $overdue_follow_up && ! $legacy_primary_conflict ) {
			$queue = \SUBIN\Leads\LeadRepository::QUEUE_DELIVERY_ISSUES;
		} elseif ( '' === $queue && '1' === $overdue_follow_up && '1' !== $attention && ! $legacy_primary_conflict ) {
			$queue = \SUBIN\Leads\LeadRepository::QUEUE_OVERDUE;
		}

		if ( '' !== $form_filter && false !== strpos( $form_filter, '|' ) ) {
			$form_parts    = explode( '|', $form_filter, 2 );
			$form_provider = sanitize_key( $form_parts[0] ?? '' );
			$form_id       = sanitize_text_field( $form_parts[1] ?? '' );
		} elseif ( '' !== $form_filter ) {
			$form_id = $form_filter;
		}

		$filters = array(
			'queue'              => $queue,
			'queue_user_id'      => \SUBIN\Leads\LeadRepository::QUEUE_MY_LEADS === $queue ? get_current_user_id() : 0,
			'status'             => in_array( $status, array( 'new', 'seen', 'archived' ), true ) ? $status : '',
			'email_status'       => in_array( $email_status, array( 'sent', 'failed', 'no_email_generated', 'unknown' ), true ) ? $email_status : '',
			'auto_reply_status'  => in_array( $auto_reply_status, array( 'not_configured', 'scheduled', 'sent', 'failed', 'no_email_generated', 'unknown' ), true ) ? $auto_reply_status : '',
			'needs_attention'    => '1' === $attention ? '1' : '',
			'provider'           => $this->providers->sanitize( $provider ),
			'form_provider'      => $this->providers->sanitize( $form_provider ),
			'form_id'            => $form_id,
			'form_filter'        => $form_filter,
			'workflow_status'    => array_key_exists( $workflow_status, $this->workflow->workflow_statuses() ) ? $workflow_status : '',
			'priority'           => array_key_exists( $priority, $this->workflow->priorities() ) ? $priority : '',
			'assigned_user_id'   => ( '' === $assigned_user_id || '0' === $assigned_user_id || absint( $assigned_user_id ) > 0 ) ? $assigned_user_id : '',
			'overdue_follow_up'  => '1' === $overdue_follow_up ? '1' : '',
			'form'               => SUBIN_Request::get_text( 'subin_form' ),
			'date_from'          => $this->sanitize_date( SUBIN_Request::get_text( 'subin_date_from' ) ),
			'date_to'            => $this->sanitize_date( SUBIN_Request::get_text( 'subin_date_to' ) ),
			'search'             => SUBIN_Request::get_text( 's' ),
			'sort'               => in_array( $sort, $allowed_sorts, true ) ? $sort : \SUBIN\Leads\LeadRepository::SORT_NEWEST,
		);

		if ( '' !== $queue ) {
			foreach ( array( 'status', 'email_status', 'auto_reply_status', 'needs_attention', 'workflow_status', 'assigned_user_id', 'overdue_follow_up' ) as $conflicting_filter ) {
				$filters[ $conflicting_filter ] = '';
			}
		}

		return $filters;
	}

	/**
	 * Sanitize date input.
	 *
	 * @param mixed $date Date.
	 * @return string
	 */
	private function sanitize_date( $date ) {
		$date = sanitize_text_field( wp_unslash( $date ) );
		if ( 1 !== preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ) {
			return '';
		}

		$parts = explode( '-', $date );

		return checkdate( absint( $parts[1] ), absint( $parts[2] ), absint( $parts[0] ) ) ? $date : '';
	}

	/**
	 * Check active filters.
	 *
	 * @param array<string,mixed> $filters Filters.
	 * @return bool
	 */
	private function has_active_filters( array $filters ) {
		unset( $filters['sort'], $filters['queue_user_id'] );

		return '' !== implode( '', array_map( 'strval', $filters ) );
	}

	/**
	 * Build URL args from filters.
	 *
	 * @param array<string,mixed> $filters Filters.
	 * @return array<string,string>
	 */
	private function url_args_from_filters( array $filters ) {
		return array_filter(
			array(
				'page'                    => SUBIN_AdminMenu::MENU_SLUG,
				'subin_queue'             => $filters['queue'] ?? '',
				'subin_status'            => $filters['status'] ?? '',
				'subin_email_status'      => $filters['email_status'] ?? '',
				'subin_auto_reply_status' => $filters['auto_reply_status'] ?? '',
				'subin_attention'         => $filters['needs_attention'] ?? '',
				'subin_provider'          => $filters['provider'] ?? '',
				'subin_form_id'           => $filters['form_filter'] ?? '',
				'subin_workflow_status'   => $filters['workflow_status'] ?? '',
				'subin_priority'          => $filters['priority'] ?? '',
				'subin_assigned_user_id' => $filters['assigned_user_id'] ?? '',
				'subin_overdue_follow_up' => $filters['overdue_follow_up'] ?? '',
				'subin_form'              => $filters['form'] ?? '',
				'subin_date_from'         => $filters['date_from'] ?? '',
				'subin_date_to'           => $filters['date_to'] ?? '',
				's'                       => $filters['search'] ?? '',
				'subin_sort'              => ( \SUBIN\Leads\LeadRepository::SORT_NEWEST === ( $filters['sort'] ?? \SUBIN\Leads\LeadRepository::SORT_NEWEST ) ) ? '' : $filters['sort'],
			),
			static function ( $value ) {
				return '' !== (string) $value;
			}
		);
	}

	/**
	 * Render status badge.
	 *
	 * @param string $status Status.
	 * @param string $label Label.
	 * @return void
	 */
	private function status_badge( $status, $label ) {
		SUBIN_View::badge( $label, $this->badge_type_for_status( $status ) );
	}

	/**
	 * Badge type for status.
	 *
	 * @param string $status Status.
	 * @return string
	 */
	private function badge_type_for_status( $status ) {
		if ( in_array( $status, array( 'sent', 'detected' ), true ) ) {
			return 'success';
		}

		if ( 'failed' === $status ) {
			return 'danger';
		}

		if ( in_array( $status, array( 'no_email_generated' ), true ) ) {
			return 'warning';
		}

		if ( in_array( $status, array( 'new', 'scheduled' ), true ) ) {
			return 'info';
		}

		return 'muted';
	}

	/**
	 * Summarize related email events for the lead hero.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $events Events.
	 * @return string
	 */
	private function lead_email_result_summary( $lead, array $events ) {
		$sent      = 0;
		$failed    = 0;
		$no_email  = 0;
		$event_num = count( $events );

		foreach ( $events as $event ) {
			$status = sanitize_key( $event->status ?? '' );
			if ( 'sent' === $status ) {
				++$sent;
			} elseif ( 'failed' === $status ) {
				++$failed;
			} elseif ( 'no_email_generated' === $status ) {
				++$no_email;
			}
		}

		if ( $failed > 0 ) {
			return sprintf(
				/* translators: %d: failed email event count. */
				_n( '%d email event failed', '%d email events failed', $failed, 'submission-inbox' ),
				$failed
			);
		}

		if ( $sent > 0 ) {
			return sprintf(
				/* translators: %d: email event count accepted by WordPress. */
				_n( '%d email event accepted by WordPress', '%d email events accepted by WordPress', $sent, 'submission-inbox' ),
				$sent
			);
		}

		if ( $no_email > 0 || 'no_email_generated' === sanitize_key( $lead->email_status ?? '' ) ) {
			return __( 'No email generated', 'submission-inbox' );
		}

		if ( $event_num > 0 ) {
			return sprintf(
				/* translators: %d: email event count. */
				_n( '%d related email event', '%d related email events', $event_num, 'submission-inbox' ),
				$event_num
			);
		}

		return $this->email_status_label( sanitize_key( $lead->email_status ?? 'unknown' ) );
	}

	/**
	 * Lead status label.
	 *
	 * @param string $status Status.
	 * @return string
	 */
	private function lead_status_label( $status ) {
		$labels = array(
			'new'      => __( 'Unseen', 'submission-inbox' ),
			'seen'     => __( 'Seen', 'submission-inbox' ),
			'archived' => __( 'Archived', 'submission-inbox' ),
		);

		return $labels[ $status ] ?? __( 'Unknown', 'submission-inbox' );
	}

	/**
	 * Get the configured unread lead freshness window.
	 *
	 * @return int
	 */
	private function get_new_lead_hours() {
		$settings = $this->settings->all();
		$hours    = absint( $settings['new_lead_hours'] ?? 24 );
		$allowed  = array( 6, 12, 24, 48, 72, 168 );

		return in_array( $hours, $allowed, true ) ? $hours : 24;
	}

	/**
	 * Email status label.
	 *
	 * @param string $status Status.
	 * @param bool   $compact Whether to use the short Inbox label.
	 * @return string
	 */
	private function email_status_label( $status, $compact = false ) {
		$labels = array(
			'sent'               => $compact ? __( 'Accepted', 'submission-inbox' ) : __( 'Accepted by WordPress — delivery not verified', 'submission-inbox' ),
			'failed'             => __( 'Failed', 'submission-inbox' ),
			'no_email_generated' => $compact ? __( 'Not sent', 'submission-inbox' ) : __( 'No email generated', 'submission-inbox' ),
			'unknown'            => __( 'Unknown', 'submission-inbox' ),
			'pending'            => __( 'Unknown', 'submission-inbox' ),
		);

		return $labels[ $status ] ?? __( 'Unknown', 'submission-inbox' );
	}

	/**
	 * Visitor confirmation status label.
	 *
	 * @param string $status Status.
	 * @param bool   $compact Whether to use the short Inbox label.
	 * @return string
	 */
	private function auto_reply_status_label( $status, $compact = false ) {
		$status = $this->normalize_auto_reply_status( $status );
		$labels = array(
			'sent'               => $compact ? __( 'Accepted', 'submission-inbox' ) : __( 'Accepted by WordPress — delivery not verified', 'submission-inbox' ),
			'scheduled'          => __( 'Scheduled', 'submission-inbox' ),
			'failed'             => __( 'Failed', 'submission-inbox' ),
			'no_email_generated' => $compact ? __( 'Not sent', 'submission-inbox' ) : __( 'No confirmation sent', 'submission-inbox' ),
			'not_configured'     => $compact ? __( 'Off', 'submission-inbox' ) : __( 'Not configured', 'submission-inbox' ),
			'unknown'            => __( 'Unknown', 'submission-inbox' ),
		);

		return $labels[ $status ];
	}

	/**
	 * Normalize the persisted visitor confirmation projection.
	 *
	 * @param mixed $status Status.
	 * @return string
	 */
	private function normalize_auto_reply_status( $status ) {
		$status  = sanitize_key( (string) $status );
		$allowed = array( 'not_configured', 'scheduled', 'sent', 'failed', 'no_email_generated', 'unknown' );

		return in_array( $status, $allowed, true ) ? $status : 'unknown';
	}

	/**
	 * Visual tone for one assurance signal.
	 *
	 * @param string $status Status.
	 * @return string
	 */
	private function assurance_tone( $status ) {
		$status = sanitize_key( (string) $status );
		if ( 'sent' === $status ) {
			return 'success';
		}
		if ( 'failed' === $status ) {
			return 'danger';
		}
		if ( 'no_email_generated' === $status ) {
			return 'warning';
		}
		if ( 'scheduled' === $status ) {
			return 'info';
		}

		return 'muted';
	}

	/**
	 * Dashicon class for lead source/provider.
	 *
	 * @param string $provider Provider key.
	 * @return string
	 */
	private function provider_icon( $provider ) {
		return $this->providers->icon( $provider );
	}

	/**
	 * Short explanation for email notification status.
	 *
	 * @param string $status Email status.
	 * @return string
	 */
	private function email_status_explanation( $status ) {
		$messages = array(
			'sent'               => __( 'Lead is saved in Inbox.', 'submission-inbox' ),
			'failed'             => __( 'Lead is saved. Check Email Log for the error.', 'submission-inbox' ),
			'no_email_generated' => __( 'This form submission was saved, but no notification email was created.', 'submission-inbox' ),
			'unknown'            => __( 'Lead is saved, but the notification status is unknown.', 'submission-inbox' ),
			'pending'            => __( 'Lead is saved, but the notification status is unknown.', 'submission-inbox' ),
		);

		return $messages[ $status ] ?? $messages['unknown'];
	}

	/**
	 * Lead detail notification message.
	 *
	 * @param string $status Email status.
	 * @return string
	 */
	private function notification_status_message( $status ) {
		$messages = array(
			'sent'               => __( 'Lead is saved in InboxMend and the notification email was accepted by WordPress; delivery is not verified.', 'submission-inbox' ),
			'failed'             => __( 'Lead is saved in InboxMend, but the admin notification email could not be sent.', 'submission-inbox' ),
			'no_email_generated' => __( 'Lead is saved, but this form did not create a notification email.', 'submission-inbox' ),
			'unknown'            => __( 'Lead is saved, but the notification status is unknown.', 'submission-inbox' ),
			'pending'            => __( 'Lead is saved, but the notification status is unknown.', 'submission-inbox' ),
		);

		return $messages[ $status ] ?? $messages['unknown'];
	}

	/**
	 * Find the latest event from a known team-notification source.
	 *
	 * @param array<int,object> $events Events.
	 * @param string            $preferred_status Projected status to match.
	 * @return object|null
	 */
	private function latest_admin_notification_event( array $events, $preferred_status = '' ) {
		$preferred_status = sanitize_key( (string) $preferred_status );
		foreach ( $events as $event ) {
			$is_admin_source = in_array( sanitize_key( $event->source ?? '' ), \SUBIN\Mail\LeadEmailStatusService::ADMIN_NOTIFICATION_SOURCES, true );
			$status_matches  = '' === $preferred_status || $preferred_status === sanitize_key( $event->status ?? '' );
			if ( $is_admin_source && $status_matches ) {
				return $event;
			}
		}

		return null;
	}

	/**
	 * Get visitor auto-reply details for summary and activity sections.
	 *
	 * @param object            $lead Lead row.
	 * @param array<int,object> $events Events.
	 * @return array{event:object|null,enabled:bool,rule_name:string,rule_version:int,template_name:string,recipient:string,status:string,delivery_mode:string}
	 */
	private function auto_reply_details_for_lead( $lead, array $events ) {
		$projected_status = $this->normalize_auto_reply_status( $lead->auto_reply_status ?? 'not_configured' );
		$event_status     = 'scheduled' === $projected_status ? 'pending' : $projected_status;
		$auto_reply_event = null;
		foreach ( $events as $event ) {
			if ( 'submission_inbox_auto_reply' === sanitize_key( $event->source ?? '' ) && $event_status === sanitize_key( $event->status ?? '' ) ) {
				$auto_reply_event = $event;
				break;
			}
		}

		$provider      = sanitize_key( $lead->provider ?? '' );
		$form_id       = sanitize_text_field( (string) ( $lead->form_id ?? '' ) );
		$rule_id       = $auto_reply_event ? absint( $auto_reply_event->rule_id ?? 0 ) : 0;
		$rule          = $rule_id > 0 ? $this->auto_reply_rules->find( $rule_id ) : null;
		if ( ! $rule && '' !== $provider && '' !== $form_id ) {
			$rule = $this->auto_reply_rules->find_enabled_rule( $provider, $form_id );
		}

		$template_name = '';
		$rule_name     = $rule ? sanitize_text_field( $rule['name'] ?? '' ) : '';
		$rule_version  = $auto_reply_event ? absint( $auto_reply_event->rule_version ?? 0 ) : absint( $rule['rule_version'] ?? 0 );
		$delivery_mode = $auto_reply_event ? sanitize_key( $auto_reply_event->delivery_mode ?? '' ) : sanitize_key( $rule['delivery_mode'] ?? '' );
		$enabled       = $auto_reply_event || ( $rule && ! empty( $rule['enabled'] ) );
		$template_id   = $auto_reply_event ? absint( $auto_reply_event->template_id ?? 0 ) : absint( $rule['template_id'] ?? 0 );
		if ( '' === $rule_name && $rule_id > 0 ) {
			/* translators: %d: auto-reply rule ID. */
			$rule_name = sprintf( __( 'Rule #%d', 'submission-inbox' ), $rule_id );
		}
		if ( $template_id > 0 ) {
			$template = $this->auto_reply_templates->get( $template_id );
			if ( $template ) {
				$template_name = sanitize_text_field( $template['name'] ?? '' );
			}
		}

		$form = ( 'submission_inbox_form' === $provider && ! empty( $lead->form_id ) ) ? $this->forms->get( absint( $lead->form_id ) ) : null;

		if ( ! $rule && $form ) {
			$settings    = $this->forms->decode_settings( $form );
			$config      = is_array( $settings['auto_reply'] ?? null ) ? $settings['auto_reply'] : array();
			$enabled     = ! empty( $config['enabled'] );
			$template_id = absint( $config['template_id'] ?? 0 );
			$template    = $template_id > 0 ? $this->auto_reply_templates->get( $template_id ) : null;
			if ( $template ) {
				$template_name = sanitize_text_field( $template['name'] ?? '' );
			}
		}

		return array(
			'event'         => $auto_reply_event,
			'enabled'       => $enabled,
			'rule_name'     => $rule_name,
			'rule_version'  => $rule_version,
			'template_name' => $template_name,
			'recipient'     => $auto_reply_event ? sanitize_text_field( $auto_reply_event->to_email ?? '' ) : '',
			'status'        => $projected_status,
			'delivery_mode' => $delivery_mode,
		);
	}

	/**
	 * Check a capability whose destination page still uses the legacy gate.
	 *
	 * Keeping both checks prevents granular Inbox users from seeing a CTA that
	 * WordPress will reject until that destination receives its own full audit.
	 *
	 * @param string $capability Registered plugin capability for the destination.
	 * @return bool
	 */
	private function can_access_legacy_admin_page( $capability ) {
		return current_user_can( SUBIN_AdminMenu::CAPABILITY )
			&& \SUBIN\Security\Capabilities::can_current_user( $capability );
	}

	/**
	 * Build the filtered Auto-replies URL for a lead's source form.
	 *
	 * @param object $lead Lead row.
	 * @return string
	 */
	private function auto_reply_url_for_lead( $lead ) {
		if ( ! $this->can_access_legacy_admin_page( \SUBIN\Security\Capabilities::MANAGE_AUTO_REPLIES ) ) {
			return '';
		}

		$provider = sanitize_key( $lead->provider ?? '' );
		$form_id  = sanitize_text_field( (string) ( $lead->form_id ?? '' ) );
		if ( '' === $provider || '' === $form_id ) {
			return '';
		}

		return add_query_arg(
			array(
				'page'                   => 'submission-inbox-auto-replies',
				'subin_auto_replies_tab' => 'rules',
				'provider'               => $provider,
				'form_id'                => $form_id,
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Link to an exact matching event or a channel/status-filtered Email Log.
	 *
	 * @param object|null $event Matching event.
	 * @param string      $status Projected status.
	 * @param string      $source Mail source.
	 * @return string
	 */
	private function email_log_url_for_outcome( $event, $status, $source = '' ) {
		if ( ! $this->can_access_legacy_admin_page( \SUBIN\Security\Capabilities::VIEW_EMAIL_LOG ) ) {
			return '';
		}

		if ( is_object( $event ) && absint( $event->id ?? 0 ) > 0 ) {
			return add_query_arg(
				array(
					'page'     => 'subin-email-log',
					'event_id' => absint( $event->id ),
				),
				admin_url( 'admin.php' )
			);
		}

		return add_query_arg(
			array_filter(
				array(
					'page'         => 'subin-email-log',
					'subin_status' => sanitize_key( (string) $status ),
					'subin_source' => sanitize_key( (string) $source ),
				)
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Form settings URL for a built-in form lead.
	 *
	 * @param object $lead Lead row.
	 * @return string
	 */
	private function form_settings_url_for_lead( $lead ) {
		if ( ! $this->can_access_legacy_admin_page( \SUBIN\Security\Capabilities::MANAGE_FORMS ) ) {
			return '';
		}

		if ( 'submission_inbox_form' !== sanitize_key( $lead->provider ?? '' ) || absint( $lead->form_id ?? 0 ) <= 0 ) {
			return '';
		}

		return add_query_arg(
			array(
				'page'    => 'submission-inbox-forms',
				'view'    => 'edit',
				'form_id' => absint( $lead->form_id ),
				'tab'     => 'settings',
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Explain where the admin notification recipient came from.
	 *
	 * @param object $lead Lead row.
	 * @return array{email:string,source:string}
	 */
	private function notification_recipient_details_for_lead( $lead ) {
		if ( 'submission_inbox_form' !== sanitize_key( $lead->provider ?? '' ) || absint( $lead->form_id ?? 0 ) <= 0 ) {
			return array(
				'email'  => '',
				'source' => '',
			);
		}

		$form = $this->forms->get( absint( $lead->form_id ) );
		if ( ! $form || empty( $form['notification_enabled'] ) ) {
			return array(
				'email'  => '',
				'source' => __( 'Admin notification is disabled for this form.', 'submission-inbox' ),
			);
		}

		$form_settings = $this->forms->decode_settings( $form );
		$notification  = is_array( $form_settings['notification'] ?? null ) ? $form_settings['notification'] : array();
		$mode          = sanitize_key( $notification['recipient_mode'] ?? '' );
		$form_email    = sanitize_email( $form['notification_email'] ?? '' );

		if ( '' === $mode ) {
			$mode = is_email( $form_email ) ? 'custom' : 'default';
		}

		if ( 'custom' === $mode && is_email( $form_email ) ) {
			return array(
				'email'  => $form_email,
				'source' => __( 'Form settings custom recipient.', 'submission-inbox' ),
			);
		}

		$settings = $this->settings->all();
		$default  = sanitize_email( $settings['notification_recipient_email'] ?? '' );
		if ( is_email( $default ) ) {
			return array(
				'email'  => $default,
				'source' => __( 'Default notification recipient from Settings.', 'submission-inbox' ),
			);
		}

		$admin_email = sanitize_email( get_option( 'admin_email' ) );
		return array(
			'email'  => is_email( $admin_email ) ? $admin_email : '',
			'source' => __( 'WordPress admin email fallback.', 'submission-inbox' ),
		);
	}

	/**
	 * Whether a submitted field is internal/security metadata.
	 *
	 * @param object $field Field row.
	 * @return bool
	 */
	private function is_internal_field( $field ) {
		$key   = strtolower( (string) ( $field->field_key ?? '' ) );
		$label = strtolower( (string) ( $field->field_label ?? '' ) );
		$text  = $key . ' ' . $label;

		foreach ( array( 'captcha', 'recaptcha', 'g-recaptcha', 'nonce', '_wpnonce', 'token', 'csrf', 'honeypot', 'security', 'subin_' ) as $needle ) {
			if ( false !== strpos( $text, $needle ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Check whether a saved field appears to be the submitted message field.
	 *
	 * @param object $field Field row.
	 * @return bool
	 */
	private function field_looks_like_message( $field ) {
		$key   = strtolower( (string) ( $field->field_key ?? '' ) );
		$label = strtolower( (string) ( $field->field_label ?? '' ) );
		$text  = $key . ' ' . $label;

		foreach ( array( 'message', 'comments', 'comment', 'enquiry', 'inquiry', 'details' ) as $needle ) {
			if ( false !== strpos( $text, $needle ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Human mail event source label.
	 *
	 * @param string $source Event source.
	 * @return string
	 */
	private function mail_event_source_label( $source ) {
		$labels = array(
			'submission_inbox_form'       => __( 'Admin notification', 'submission-inbox' ),
			'submission_inbox_auto_reply' => __( 'Visitor auto-reply', 'submission-inbox' ),
			'contact_form_7'          => __( 'Contact Form 7', 'submission-inbox' ),
			'wpforms'                 => __( 'WPForms', 'submission-inbox' ),
			'fluent_forms'            => __( 'Fluent Forms', 'submission-inbox' ),
			'forminator'              => __( 'Forminator', 'submission-inbox' ),
			'ninja_forms'             => __( 'Ninja Forms', 'submission-inbox' ),
			'wp_mail'                 => __( 'WordPress email', 'submission-inbox' ),
			'wordpress'               => __( 'WordPress email', 'submission-inbox' ),
			'plugin'                  => __( 'Plugin email', 'submission-inbox' ),
		);

		$source = sanitize_key( $source );
		return $labels[ $source ] ?? __( 'Email event', 'submission-inbox' );
	}

	/**
	 * Human source label.
	 *
	 * @param string $provider Provider.
	 * @return string
	 */
	private function provider_label( $provider ) {
		return $this->providers->label( $provider );
	}

	/**
	 * Workflow status label.
	 *
	 * @param string $status Status.
	 * @return string
	 */
	private function workflow_status_label( $status ) {
		$labels = $this->workflow->workflow_statuses();
		$status = sanitize_key( $status );

		return $labels[ $status ] ?? $labels['open'];
	}

	/**
	 * Priority label.
	 *
	 * @param string $priority Priority.
	 * @return string
	 */
	private function priority_label( $priority ) {
		$labels   = $this->workflow->priorities();
		$priority = sanitize_key( $priority );

		return $labels[ $priority ] ?? $labels['normal'];
	}

	/**
	 * Assignee display label.
	 *
	 * @param int $user_id User id.
	 * @return string
	 */
	private function assignee_label( $user_id ) {
		$user = get_userdata( absint( $user_id ) );

		return $user ? $user->display_name : __( 'Unassigned', 'submission-inbox' );
	}

	/**
	 * Render source link.
	 *
	 * @param string $url URL.
	 * @return void
	 */
	private function source_link( $url ) {
		if ( empty( $url ) ) {
			echo esc_html__( 'Not recorded', 'submission-inbox' );
			return;
		}

		printf(
			'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
			esc_url( $url ),
			esc_html( wp_parse_url( $url, PHP_URL_HOST ) ? wp_parse_url( $url, PHP_URL_HOST ) : $url )
		);
	}

	/**
	 * Format date.
	 *
	 * @param string $date Date.
	 * @return string
	 */
	private function format_date( $date ) {
		if ( empty( $date ) ) {
			return '';
		}

		return mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $date );
	}

	/**
	 * List URL.
	 *
	 * @param array<string,mixed> $filters Filters.
	 * @param int                 $paged Page.
	 * @return string
	 */
	private function list_url( array $filters, $paged ) {
		$args = $this->url_args_from_filters( $filters );
		if ( $paged > 1 ) {
			$args['paged'] = $paged;
		}

		return add_query_arg(
			$args,
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Build a list URL from the current request context.
	 *
	 * @param string $notice Optional list notice key.
	 * @return string
	 */
	private function current_list_url( $notice = '' ) {
		$url = $this->list_url(
			$this->get_filters_from_request(),
			max( 1, SUBIN_Request::get_absint( 'paged', 1 ) )
		);

		if ( '' !== $notice ) {
			$url = add_query_arg( 'notice', sanitize_key( $notice ), $url );
		}

		return $url;
	}

	/**
	 * Detail URL.
	 *
	 * @param int  $lead_id Lead id.
	 * @param bool   $updated Include updated flag.
	 * @param string $notice Notice key.
	 * @return string
	 */
	private function detail_url( $lead_id, $updated = false, $notice = '' ) {
		$args = array_merge(
			$this->url_args_from_filters( $this->get_filters_from_request() ),
			array(
				'page'    => SUBIN_AdminMenu::MENU_SLUG,
				'action'  => 'view',
				'lead_id' => absint( $lead_id ),
			)
		);
		$paged = max( 1, SUBIN_Request::get_absint( 'paged', 1 ) );
		if ( $paged > 1 ) {
			$args['paged'] = $paged;
		}

		if ( $updated ) {
			$args['subin_updated'] = 1;
		}

		if ( '' !== $notice ) {
			$args['subin_notice'] = sanitize_key( $notice );
		}

		return add_query_arg( $args, admin_url( 'admin.php' ) );
	}

	/**
	 * Redirect from an admin page callback without header warnings.
	 *
	 * @param string $url Target URL.
	 * @return void
	 */
	private function safe_admin_redirect( $url ) {
		$url = esc_url_raw( $url );
		if ( ! headers_sent() ) {
			wp_safe_redirect( $url );
			exit;
		}

		printf(
			'<script>window.location.href=%1$s;</script><noscript><meta http-equiv="refresh" content="0;url=%2$s" /></noscript>',
			wp_json_encode( $url ),
			esc_url( $url )
		);
		exit;
	}
}
