<?php

class PeepSoAdminDashboardModern
{
    const PLACEHOLDER_INSTALLER_URL = 'admin.php?page=peepso-installer';

    public static function render()
    {
        $data = self::get_dashboard_data();
        self::render_styles();

        echo '<div class="ps-dashboard-modern">';

        self::render_intro($data);
        self::render_global_range_strip();

        echo '<div class="ps-dashboard-top-row">';
        echo '<div class="ps-dashboard-top-row__stats">';
        self::render_snapshot($data['snapshot']);
        echo '</div>';
        echo '<div class="ps-dashboard-top-row__attention">';
        self::render_needs_attention($data['needs_attention']);
        echo '</div>';
        echo '</div>';

        echo '<div class="ps-dashboard-grid ps-dashboard-grid--main">';
        self::render_activity_overview($data['activity']);
        self::render_member_pulse($data['members']);
        self::render_top_content($data['top_content']);
        self::render_top_contributors($data['contributors']);
        self::render_at_risk_panel($data['at_risk']);
        echo '</div>';

        self::render_addon_section($data['addons']);

        echo '</div>';
    }

    private static function get_dashboard_data()
    {
        return array(
            'snapshot' => self::get_snapshot_data(),
            'needs_attention' => self::get_needs_attention_data(),
            'activity' => self::get_activity_data(),
            'members' => self::get_member_data(),
            'top_content'   => self::get_top_content_data(),
            'contributors'  => self::get_top_contributors_data(),
            'at_risk'       => self::get_at_risk_data(),
            'addons' => self::get_addon_data(),
        );
    }

    private static function render_global_range_strip()
    {
        $ranges = array(
            'this_week'  => __('This week',  'peepso-core'),
            'last_week'  => __('Last week',  'peepso-core'),
            'this_month' => __('This month', 'peepso-core'),
            'last_month' => __('Last month', 'peepso-core'),
        );

        echo '<div class="ps-dashboard-range-strip">';
        echo '<span class="ps-dashboard-range-strip__label">' . esc_html__('Date range', 'peepso-core') . '</span>';
        echo '<div class="ps-chart-ranges">';
        foreach ($ranges as $range => $label) {
            $active = ('this_month' === $range) ? ' ps-chart-range-btn--active' : '';
            echo '<button class="ps-chart-range-btn' . esc_attr($active) . '" data-range="' . esc_attr($range) . '">' . esc_html($label) . '</button>';
        }
        echo '<button class="ps-chart-range-btn" data-range="custom">' . esc_html__('Custom', 'peepso-core') . '</button>';
        echo '</div>';
        echo '<div class="ps-custom-range" id="ps-custom-range">';
        echo '<label for="ps-range-from">' . esc_html__('From', 'peepso-core') . '</label>';
        echo '<input type="date" id="ps-range-from" class="ps-range-input">';
        echo '<span class="ps-custom-range__sep">–</span>';
        echo '<label for="ps-range-to">' . esc_html__('To', 'peepso-core') . '</label>';
        echo '<input type="date" id="ps-range-to" class="ps-range-input">';
        echo '<button id="ps-range-apply" class="ps-range-apply-btn">' . esc_html__('Apply', 'peepso-core') . '</button>';
        echo '</div>';
        echo '</div>';
    }

    private static function get_snapshot_data()
    {
        global $wpdb;

        $users_table = $wpdb->prefix . 'peepso_users';
        $mail_table = $wpdb->prefix . 'peepso_mail_queue';
        $gdpr_table = $wpdb->prefix . 'peepso_gdpr_request_data';
        $report_table = $wpdb->prefix . 'peepso_report';
        $mayfly_table = $wpdb->prefix . 'peepso_mayfly';

        $now          = time();
        $seven_days   = gmdate('Y-m-d H:i:s', $now - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', $now - 14 * DAY_IN_SECONDS);
        $thirty_days  = gmdate('Y-m-d H:i:s', $now - 30 * DAY_IN_SECONDS);
        $sixty_days   = gmdate('Y-m-d H:i:s', $now - 60 * DAY_IN_SECONDS);

        $total_members    = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$users_table}` LEFT JOIN `{$wpdb->users}` ON `{$users_table}`.`usr_id` = `{$wpdb->users}`.`ID` WHERE `{$wpdb->users}`.`user_registered` IS NOT NULL"));
        $new_members_7    = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->users}` WHERE `user_registered` >= %s", $seven_days)));
        $new_members_30   = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->users}` WHERE `user_registered` >= %s", $thirty_days)));
        $pending_approvals = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$users_table}` LEFT JOIN `{$wpdb->users}` ON `{$users_table}`.`usr_id` = `{$wpdb->users}`.`ID` WHERE `{$wpdb->users}`.`user_registered` IS NOT NULL AND `{$users_table}`.`usr_role`='verified'"));
        $online_now       = intval($wpdb->get_var("SELECT COUNT(`id`) FROM `{$mayfly_table}` WHERE `name` LIKE '%peepso_cache_%_online'"));
        $active_recently  = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$users_table}` WHERE `usr_last_activity` >= %s", $seven_days)));
        $open_reports     = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$report_table}` WHERE `rep_status` = 0"));
        $mail_backlog     = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$mail_table}` WHERE `mail_status` IN (0,1,2,5,6)"));
        $gdpr_backlog     = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$gdpr_table}` WHERE `request_status` IN (0,1,2,5,6)"));

        // Prior-period counts for trend indicators
        $prev_new_7  = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM `{$wpdb->users}` WHERE `user_registered` >= %s AND `user_registered` < %s",
            $fourteen_days, $seven_days
        )));
        $prev_new_30 = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM `{$wpdb->users}` WHERE `user_registered` >= %s AND `user_registered` < %s",
            $sixty_days, $thirty_days
        )));
        $prev_active = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM `{$users_table}` WHERE `usr_last_activity` >= %s AND `usr_last_activity` < %s",
            $fourteen_days, $seven_days
        )));

        return array(
            array(
                'label' => __('Total members', 'peepso-core'),
                'value' => $total_members,
                'note'  => __('All PeepSo community accounts.', 'peepso-core'),
            ),
            array(
                'label' => __('New members · 7 days', 'peepso-core'),
                'value' => $new_members_7,
                'note'  => __('Fresh signups in the last week.', 'peepso-core'),
                'trend' => self::calc_trend($new_members_7, $prev_new_7, __('vs prev 7d', 'peepso-core')),
            ),
            array(
                'label' => __('New members · 30 days', 'peepso-core'),
                'value' => $new_members_30,
                'note'  => __('Fresh signups in the last month.', 'peepso-core'),
                'trend' => self::calc_trend($new_members_30, $prev_new_30, __('vs prev 30d', 'peepso-core')),
            ),
            array(
                'label' => __('Pending approvals', 'peepso-core'),
                'value' => $pending_approvals,
                'note'  => __('Members waiting for verification.', 'peepso-core'),
                'url'   => admin_url('users.php?psrole=verified'),
            ),
            array(
                'label' => __('Online now', 'peepso-core'),
                'value' => $online_now,
                'note'  => __('Members currently online.', 'peepso-core'),
            ),
            array(
                'label' => __('Active recently', 'peepso-core'),
                'value' => $active_recently,
                'note'  => __('Members seen in the last 7 days.', 'peepso-core'),
                'trend' => self::calc_trend($active_recently, $prev_active, __('vs prev 7d', 'peepso-core')),
            ),
            array(
                'label' => __('Open reports', 'peepso-core'),
                'value' => $open_reports,
                'note'  => __('Reported content or profiles awaiting review.', 'peepso-core'),
            ),
            array(
                'label' => __('Queued mail', 'peepso-core'),
                'value' => $mail_backlog,
                'note'  => __('Emails waiting in the queue.', 'peepso-core'),
            ),
            array(
                'label' => __('Pending GDPR', 'peepso-core'),
                'value' => $gdpr_backlog,
                'note'  => __('Open export or erasure requests.', 'peepso-core'),
            ),
        );
    }

    private static function calc_trend($current, $previous, $vs_label = '')
    {
        if ($previous === 0 && $current === 0) return null;
        if ($previous === 0) {
            return array('dir' => 'up', 'pct' => null, 'vs' => $vs_label);
        }
        $pct = round(($current - $previous) / $previous * 100);
        return array(
            'dir' => $pct > 0 ? 'up' : ($pct < 0 ? 'down' : 'flat'),
            'pct' => abs($pct),
            'vs'  => $vs_label,
        );
    }

    private static function get_needs_attention_data()
    {
        global $wpdb;

        $users_table = $wpdb->prefix . 'peepso_users';
        $report_table = $wpdb->prefix . 'peepso_report';
        $mail_table = $wpdb->prefix . 'peepso_mail_queue';
        $gdpr_table = $wpdb->prefix . 'peepso_gdpr_request_data';

        $pending_members = $wpdb->get_results(
            "SELECT u.ID, u.user_registered, pu.usr_last_activity
             FROM `{$wpdb->users}` u
             INNER JOIN `{$users_table}` pu ON pu.usr_id = u.ID
             WHERE pu.usr_role = 'verified'
             ORDER BY u.user_registered DESC
             LIMIT 5"
        );

        $open_reports = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$report_table}` WHERE `rep_status` = 0"));
        $oldest_report = $wpdb->get_row("SELECT * FROM `{$report_table}` WHERE `rep_status` = 0 ORDER BY `rep_timestamp` ASC LIMIT 1");

        $mail_backlog = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$mail_table}` WHERE `mail_status` IN (0,1,2,5,6)"));
        $mail_failed = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$mail_table}` WHERE `mail_status` = 3"));

        $gdpr_backlog = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$gdpr_table}` WHERE `request_status` IN (0,1,2,5,6)"));

        return array(
            'pending_total' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$users_table}` LEFT JOIN `{$wpdb->users}` ON `{$users_table}`.`usr_id` = `{$wpdb->users}`.`ID` WHERE `{$wpdb->users}`.`user_registered` IS NOT NULL AND `{$users_table}`.`usr_role`='verified'")),
            'pending_members' => $pending_members,
            'open_reports' => $open_reports,
            'oldest_report' => $oldest_report,
            'mail_backlog' => $mail_backlog,
            'mail_failed' => $mail_failed,
            'gdpr_backlog' => $gdpr_backlog,
            'unanswered_posts' => self::get_unanswered_posts(),
        );
    }

    private static function get_unanswered_posts()
    {
        global $wpdb;

        $posts_table = $wpdb->posts;
        $activities_table = $wpdb->prefix . 'peepso_activities';
        $cutoff = gmdate('Y-m-d H:i:s', time() - DAY_IN_SECONDS);
        $recent_cutoff = gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS);

        $sql = $wpdb->prepare(
            "SELECT p.ID, p.post_author, p.post_date, p.post_content, p.post_title
             FROM `{$posts_table}` p
             LEFT JOIN (
                SELECT `act_comment_object_id` AS root_post_id, COUNT(*) AS comment_count
                FROM `{$activities_table}`
                WHERE `act_comment_object_id` <> 0
                GROUP BY `act_comment_object_id`
             ) c ON c.root_post_id = p.ID
             WHERE p.post_type = 'peepso-post'
               AND p.post_status = 'publish'
               AND p.post_date <= %s
               AND p.post_date >= %s
               AND COALESCE(c.comment_count, 0) = 0
             ORDER BY p.post_date ASC
             LIMIT 5",
            $cutoff,
            $recent_cutoff
        );

        return $wpdb->get_results($sql);
    }

    private static function get_activity_data()
    {
        global $wpdb;

        $seven_days = gmdate('Y-m-d H:i:s', time() - 7 * DAY_IN_SECONDS);
        $thirty_days = gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS);
        $reactions_table = $wpdb->prefix . 'peepso_reactions';

        // Time-domain rule (see PeepSoActivityRankingEngine): WP content is UTC, so compare
        // post_date_gmt vs the gmdate() (UTC) cutoffs; reaction_timestamp is a session-tz
        // TIMESTAMP column, so age it vs NOW() (matches the ranking engine) — never vs a UTC cutoff.
        return array(
            'seven_days' => array(
                'posts' => intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-post' AND `post_status`='publish' AND `post_date_gmt` >= %s", $seven_days))),
                'comments' => intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-comment' AND `post_status`='publish' AND `post_date_gmt` >= %s", $seven_days))),
                'reactions' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$reactions_table}` WHERE `reaction_timestamp` >= (NOW() - INTERVAL 7 DAY)")),
            ),
            'thirty_days' => array(
                'posts' => intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-post' AND `post_status`='publish' AND `post_date_gmt` >= %s", $thirty_days))),
                'comments' => intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-comment' AND `post_status`='publish' AND `post_date_gmt` >= %s", $thirty_days))),
                'reactions' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$reactions_table}` WHERE `reaction_timestamp` >= (NOW() - INTERVAL 30 DAY)")),
            ),
        );
    }

    private static function get_member_data()
    {
        global $wpdb;

        $users_table = $wpdb->prefix . 'peepso_users';
        $active_cutoff = gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS);

        $recent_members = $wpdb->get_results(
            "SELECT u.ID, u.user_registered
             FROM `{$wpdb->users}` u
             INNER JOIN `{$users_table}` pu ON pu.usr_id = u.ID
             ORDER BY u.user_registered DESC
             LIMIT 5"
        );

        $active_members = $wpdb->get_results($wpdb->prepare(
            "SELECT u.ID, pu.usr_last_activity
             FROM `{$wpdb->users}` u
             INNER JOIN `{$users_table}` pu ON pu.usr_id = u.ID
             WHERE pu.usr_last_activity >= %s
             ORDER BY pu.usr_last_activity DESC
             LIMIT 5",
            $active_cutoff
        ));

        return array(
            'recent_members' => $recent_members,
            'active_members' => $active_members,
        );
    }

    private static function get_top_content_data($range = null)
    {
        global $wpdb;

        if (null === $range) {
            $mindate = date('Y-m-01');
            $maxdate = date('Y-m-d');
        } else {
            $mindate = $range['min'];
            $maxdate = $range['max'];
        }

        $posts_table      = $wpdb->posts;
        $activities_table = $wpdb->prefix . 'peepso_activities';
        $reactions_table  = $wpdb->prefix . 'peepso_reactions';
        $likes_table      = $wpdb->prefix . 'peepso_likes';
        $ranking_table    = $wpdb->prefix . 'peepso_activity_ranking';

        // Tab 1 — Top Engagement: posts ranked by comments + reactions in range
        $top_engaged = $wpdb->get_results($wpdb->prepare(
            "SELECT p.ID, p.post_author, p.post_date, p.post_date_gmt, p.post_content, p.post_title,
                    COALESCE(c.comment_count, 0) AS comment_count,
                    COALESCE(r.reaction_count, 0) + COALESCE(l.like_count, 0) AS reaction_count
             FROM `{$posts_table}` p
             INNER JOIN `{$activities_table}` act ON act.act_external_id = p.ID
             LEFT JOIN (
                SELECT x.root_post_id, SUM(x.cc) AS comment_count FROM (
                    SELECT cmt.act_comment_object_id AS root_post_id,
                           COUNT(*) + (SELECT COUNT(*) FROM `{$activities_table}` rep WHERE rep.act_comment_object_id = cmt.act_external_id) AS cc
                    FROM `{$activities_table}` cmt
                    WHERE cmt.act_comment_object_id <> 0
                    GROUP BY cmt.act_comment_object_id, cmt.act_external_id
                ) x GROUP BY x.root_post_id
             ) c ON c.root_post_id = p.ID
             LEFT JOIN (
                SELECT reaction_act_id AS root_post_id, COUNT(*) AS reaction_count
                FROM `{$reactions_table}` GROUP BY reaction_act_id
             ) r ON r.root_post_id = act.act_id
             LEFT JOIN (
                SELECT like_external_id AS root_post_id, COUNT(*) AS like_count
                FROM `{$likes_table}` GROUP BY like_external_id
             ) l ON l.root_post_id = p.ID
             WHERE p.post_type = 'peepso-post' AND p.post_status = 'publish'
               AND (CAST(p.post_date AS DATE) BETWEEN %s AND %s)
             ORDER BY (COALESCE(c.comment_count,0)*2 + COALESCE(r.reaction_count,0) + COALESCE(l.like_count,0)) DESC, p.post_date DESC
             LIMIT 10",
            $mindate, $maxdate
        ));

        // Tab 2 — High Views · Low Interaction: sorted by view-to-engagement ratio
        $high_views = array();
        if (self::table_exists($ranking_table)) {
            $high_views = $wpdb->get_results($wpdb->prepare(
                "SELECT p.ID, p.post_author, p.post_date, p.post_date_gmt, p.post_content, p.post_title,
                        COALESCE(ranking.rank_act_views, 0) AS view_count,
                        COALESCE(ranking.rank_act_comments, 0) + COALESCE(ranking.rank_act_likes, 0) AS interaction_count
                 FROM `{$posts_table}` p
                 INNER JOIN `{$activities_table}` act ON act.act_external_id = p.ID
                 INNER JOIN `{$ranking_table}` ranking ON ranking.rank_act_id = act.act_id
                 WHERE p.post_type = 'peepso-post' AND p.post_status = 'publish'
                   AND (CAST(p.post_date AS DATE) BETWEEN %s AND %s)
                   AND ranking.rank_act_views >= 3
                 ORDER BY (ranking.rank_act_views / (ranking.rank_act_comments + ranking.rank_act_likes + 1)) DESC,
                          ranking.rank_act_views DESC
                 LIMIT 10",
                $mindate, $maxdate
            ));
        }

        // Tab 3 — Trending: highest engagement score in range
        $trending = array();
        if (self::table_exists($ranking_table)) {
            $trending = $wpdb->get_results($wpdb->prepare(
                "SELECT p.ID, p.post_author, p.post_date, p.post_date_gmt, p.post_content, p.post_title,
                        COALESCE(ranking.rank_act_views, 0) AS view_count,
                        COALESCE(ranking.rank_act_comments, 0) AS comment_count,
                        COALESCE(ranking.rank_act_likes, 0) AS reaction_count,
                        COALESCE(ranking.rank_score_30d, 0) AS score
                 FROM `{$posts_table}` p
                 INNER JOIN `{$activities_table}` act ON act.act_external_id = p.ID
                 INNER JOIN `{$ranking_table}` ranking ON ranking.rank_act_id = act.act_id
                 WHERE p.post_type = 'peepso-post' AND p.post_status = 'publish'
                   AND (CAST(p.post_date AS DATE) BETWEEN %s AND %s)
                 ORDER BY ranking.rank_score_30d DESC, p.post_date DESC
                 LIMIT 10",
                $mindate, $maxdate
            ));
        }

        return array(
            'top_engaged' => $top_engaged,
            'high_views'  => $high_views,
            'trending'    => $trending,
        );
    }

    private static function get_top_contributors_data($range = null)
    {
        global $wpdb;

        if (null === $range) {
            $mindate = date('Y-m-01');
            $maxdate = date('Y-m-d');
        } else {
            $mindate = $range['min'];
            $maxdate = $range['max'];
        }

        $users_table = $wpdb->prefix . 'peepso_users';

        return $wpdb->get_results($wpdb->prepare(
            "SELECT p.post_author,
                    COUNT(*) AS total_count,
                    SUM(CASE WHEN p.post_type = 'peepso-post'    THEN 1 ELSE 0 END) AS post_count,
                    SUM(CASE WHEN p.post_type = 'peepso-comment' THEN 1 ELSE 0 END) AS comment_count
             FROM `{$wpdb->posts}` p
             INNER JOIN `{$users_table}` pu ON pu.usr_id = p.post_author
             WHERE p.post_type IN ('peepso-post','peepso-comment')
               AND p.post_status = 'publish'
               AND (CAST(p.post_date AS DATE) BETWEEN %s AND %s)
             GROUP BY p.post_author
             ORDER BY total_count DESC
             LIMIT 10",
            $mindate, $maxdate
        ));
    }

    private static function get_at_risk_data()
    {
        global $wpdb;

        $users_table = $wpdb->prefix . 'peepso_users';
        $cutoff_30   = gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS);
        $cutoff_90   = gmdate('Y-m-d H:i:s', time() - 90 * DAY_IN_SECONDS);

        // New users (last 30 days) who have never posted or commented
        $new_silent = $wpdb->get_results($wpdb->prepare(
            "SELECT u.ID, u.user_registered, u.display_name
             FROM `{$wpdb->users}` u
             INNER JOIN `{$users_table}` pu ON pu.usr_id = u.ID
             WHERE u.user_registered >= %s
               AND NOT EXISTS (
                   SELECT 1 FROM `{$wpdb->posts}` p
                   WHERE p.post_author = u.ID
                     AND p.post_type IN ('peepso-post','peepso-comment')
                     AND p.post_status = 'publish'
               )
             ORDER BY u.user_registered DESC
             LIMIT 10",
            $cutoff_30
        ));

        // Previously active (posted 30–90 days ago) but silent in the last 30 days
        $going_quiet = $wpdb->get_results($wpdb->prepare(
            "SELECT u.ID, u.display_name, pu.usr_last_activity,
                    MAX(p.post_date) AS last_post_date
             FROM `{$wpdb->users}` u
             INNER JOIN `{$users_table}` pu ON pu.usr_id = u.ID
             INNER JOIN `{$wpdb->posts}` p
                 ON p.post_author = u.ID
                AND p.post_type IN ('peepso-post','peepso-comment')
                AND p.post_status = 'publish'
                AND p.post_date >= %s
                AND p.post_date < %s
             WHERE NOT EXISTS (
                 SELECT 1 FROM `{$wpdb->posts}` p2
                 WHERE p2.post_author = u.ID
                   AND p2.post_type IN ('peepso-post','peepso-comment')
                   AND p2.post_status = 'publish'
                   AND p2.post_date >= %s
             )
             GROUP BY u.ID, u.display_name, pu.usr_last_activity
             ORDER BY last_post_date DESC
             LIMIT 10",
            $cutoff_90, $cutoff_30, $cutoff_30
        ));

        return array(
            'new_silent'  => $new_silent,
            'going_quiet' => $going_quiet,
        );
    }

    private static function get_addon_data()
    {
        return array(
            'groups' => self::get_groups_data(),
            'pages' => self::get_pages_data(),
            'friends' => self::get_friends_data(),
            'chat' => self::get_chat_data(),
            'photos' => self::get_photos_data(),
            'videos' => self::get_videos_data(),
            'files' => self::get_files_data(),
            'email_digest' => self::get_email_digest_data(),
        );
    }

    private static function get_groups_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoGroupsPlugin') || !self::table_exists($wpdb->prefix . 'peepso_group_members')) {
            return self::placeholder_data(
                __('Groups health', 'peepso-core'),
                __('Track active groups, pending join requests, dormant spaces, and the biggest communities here.', 'peepso-core')
            );
        }

        $thirty_days = gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS);
        $sixty_days  = gmdate('Y-m-d H:i:s', time() - 60 * DAY_IN_SECONDS);
        $total = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-group' AND `post_status` NOT IN ('trash','auto-draft')"));
        $new_30 = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-group' AND `post_status` NOT IN ('trash','auto-draft') AND `post_date` >= %s", $thirty_days)));
        $prev_new_30 = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-group' AND `post_status` NOT IN ('trash','auto-draft') AND `post_date` >= %s AND `post_date` < %s", $sixty_days, $thirty_days)));
        $active_30 = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.meta_value)
             FROM `{$wpdb->postmeta}` pm
             INNER JOIN `{$wpdb->posts}` p ON p.ID = pm.post_id
             WHERE pm.meta_key = 'peepso_group_id'
               AND p.post_type = 'peepso-post'
               AND p.post_status = 'publish'
               AND p.post_date >= %s",
            $thirty_days
        )));
        $prev_active_30 = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.meta_value)
             FROM `{$wpdb->postmeta}` pm
             INNER JOIN `{$wpdb->posts}` p ON p.ID = pm.post_id
             WHERE pm.meta_key = 'peepso_group_id'
               AND p.post_type = 'peepso-post'
               AND p.post_status = 'publish'
               AND p.post_date >= %s AND p.post_date < %s",
            $sixty_days, $thirty_days
        )));
        $pending = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_group_members` WHERE `gm_user_status` IN ('pending_user','pending_admin')"));
        $largest = $wpdb->get_results(
            "SELECT p.ID, p.post_title, p.post_name, COUNT(gm.gm_id) AS member_count
             FROM `{$wpdb->posts}` p
             LEFT JOIN `{$wpdb->prefix}peepso_group_members` gm ON gm.gm_group_id = p.ID AND gm.gm_user_status IN ('member_readonly','member','member_moderator','member_manager','member_owner')
             LEFT JOIN `{$wpdb->users}` u ON u.ID = gm.gm_user_id
             WHERE u.user_registered IS NOT NULL
             AND p.post_type = 'peepso-group' AND p.post_status NOT IN ('trash','auto-draft')
             GROUP BY p.ID
             ORDER BY member_count DESC, p.post_date DESC
             LIMIT 3"
        );

        return array(
            'active' => true,
            'title' => __('Groups health', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Total groups', 'peepso-core'), 'value' => $total),
                array('label' => __('New · 30 days', 'peepso-core'), 'value' => $new_30, 'trend' => self::calc_trend($new_30, $prev_new_30, __('vs prev 30d', 'peepso-core'))),
                array('label' => __('Active · 30 days', 'peepso-core'), 'value' => $active_30, 'trend' => self::calc_trend($active_30, $prev_active_30, __('vs prev 30d', 'peepso-core'))),
                array('label' => __('Pending joins', 'peepso-core'), 'value' => $pending),
                array('label' => __('Dormant groups', 'peepso-core'), 'value' => max(0, $total - $active_30)),
            ),
            'items' => $largest,
            'item_type' => 'groups',
            'item_label' => __('Largest groups', 'peepso-core'),
            'manage_url' => admin_url('admin.php?page=peepso-manage&tab=groups'),
            'item_url' => PeepSo::get_page('groups', FALSE),
        );
    }

    private static function get_pages_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoPagesPlugin') || !self::table_exists($wpdb->prefix . 'peepso_page_members')) {
            return self::placeholder_data(
                __('Pages health', 'peepso-core'),
                __('Track active pages, pending follows, dormant brands, and the biggest pages here.', 'peepso-core')
            );
        }

        $thirty_days = gmdate('Y-m-d H:i:s', time() - 30 * DAY_IN_SECONDS);
        $sixty_days  = gmdate('Y-m-d H:i:s', time() - 60 * DAY_IN_SECONDS);
        $total = intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-page' AND `post_status` NOT IN ('trash','auto-draft')"));
        $new_30 = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-page' AND `post_status` NOT IN ('trash','auto-draft') AND `post_date` >= %s", $thirty_days)));
        $prev_new_30 = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-page' AND `post_status` NOT IN ('trash','auto-draft') AND `post_date` >= %s AND `post_date` < %s", $sixty_days, $thirty_days)));
        $active_30 = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.meta_value)
             FROM `{$wpdb->postmeta}` pm
             INNER JOIN `{$wpdb->posts}` p ON p.ID = pm.post_id
             WHERE pm.meta_key = 'peepso_page_id'
               AND p.post_type = 'peepso-post'
               AND p.post_status = 'publish'
               AND p.post_date >= %s",
            $thirty_days
        )));
        $prev_active_30 = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT pm.meta_value)
             FROM `{$wpdb->postmeta}` pm
             INNER JOIN `{$wpdb->posts}` p ON p.ID = pm.post_id
             WHERE pm.meta_key = 'peepso_page_id'
               AND p.post_type = 'peepso-post'
               AND p.post_status = 'publish'
               AND p.post_date >= %s AND p.post_date < %s",
            $sixty_days, $thirty_days
        )));
        $largest = $wpdb->get_results(
            "SELECT p.ID, p.post_title, p.post_name, COUNT(pm.pm_id) AS member_count
             FROM `{$wpdb->posts}` p
             LEFT JOIN `{$wpdb->prefix}peepso_page_members` pm ON pm.pm_page_id = p.ID AND pm.pm_user_status IN ('member_readonly','member','member_moderator','member_manager','member_owner')
             LEFT JOIN `{$wpdb->users}` u ON u.ID = pm.pm_user_id
             WHERE u.user_registered IS NOT NULL
             AND p.post_type = 'peepso-page' AND p.post_status NOT IN ('trash','auto-draft')
             GROUP BY p.ID
             ORDER BY member_count DESC, p.post_date DESC
             LIMIT 3"
        );

        return array(
            'active' => true,
            'title' => __('Pages health', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Total pages', 'peepso-core'), 'value' => $total),
                array('label' => __('New · 30 days', 'peepso-core'), 'value' => $new_30, 'trend' => self::calc_trend($new_30, $prev_new_30, __('vs prev 30d', 'peepso-core'))),
                array('label' => __('Active · 30 days', 'peepso-core'), 'value' => $active_30, 'trend' => self::calc_trend($active_30, $prev_active_30, __('vs prev 30d', 'peepso-core'))),
                array('label' => __('Dormant pages', 'peepso-core'), 'value' => max(0, $total - $active_30)),
            ),
            'items' => $largest,
            'item_type' => 'pages',
            'item_label' => __('Largest pages', 'peepso-core'),
            'manage_url' => admin_url('admin.php?page=peepso-manage&tab=pages'),
            'item_url' => PeepSo::get_page('pages', FALSE),
        );
    }

    private static function get_friends_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoFriendsPlugin') || !self::table_exists($wpdb->prefix . 'peepso_friend_requests')) {
            return self::placeholder_data(
                __('Friends activity', 'peepso-core'),
                __('See pending requests and new friendship activity here when Friends is active.', 'peepso-core')
            );
        }

        $seven_days    = gmdate('Y-m-d H:i:s', time() - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', time() - 14 * DAY_IN_SECONDS);

        $new_requests  = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_friend_requests` WHERE `freq_created` >= %s", $seven_days)));
        $prev_requests = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_friend_requests` WHERE `freq_created` >= %s AND `freq_created` < %s", $fourteen_days, $seven_days)));
        $new_friends   = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_friends` WHERE `fnd_created` >= %s", $seven_days)));
        $prev_friends  = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_friends` WHERE `fnd_created` >= %s AND `fnd_created` < %s", $fourteen_days, $seven_days)));

        return array(
            'active' => true,
            'title' => __('Friends activity', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Pending requests', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_friend_requests`"))),
                array('label' => __('New requests · 7 days', 'peepso-core'), 'value' => $new_requests, 'trend' => self::calc_trend($new_requests, $prev_requests, __('vs prev 7d', 'peepso-core'))),
                array('label' => __('New friendships · 7 days', 'peepso-core'), 'value' => $new_friends, 'trend' => self::calc_trend($new_friends, $prev_friends, __('vs prev 7d', 'peepso-core'))),
                array('label' => __('Total friendships', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_friends`"))),
            ),
            'manage_url' => admin_url('admin.php?page=peepso_config&tab=friends'),
        );
    }

    private static function get_chat_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoMessagesPlugin') || !self::table_exists($wpdb->prefix . 'peepso_message_participants')) {
            return self::placeholder_data(
                __('Chat activity', 'peepso-core'),
                __('See private message volume and active conversations here when Chat is active.', 'peepso-core')
            );
        }

        $seven_days    = gmdate('Y-m-d H:i:s', time() - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', time() - 14 * DAY_IN_SECONDS);

        $messages      = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-message' AND `post_status`='publish' AND `post_date` >= %s", $seven_days)));
        $prev_messages = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE `post_type`='peepso-message' AND `post_status`='publish' AND `post_date` >= %s AND `post_date` < %s", $fourteen_days, $seven_days)));
        $active_conv      = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(DISTINCT `mpart_msg_id`) FROM `{$wpdb->prefix}peepso_message_participants` WHERE `mpart_last_activity` >= %s", $seven_days)));
        $prev_active_conv = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(DISTINCT `mpart_msg_id`) FROM `{$wpdb->prefix}peepso_message_participants` WHERE `mpart_last_activity` >= %s AND `mpart_last_activity` < %s", $fourteen_days, $seven_days)));

        return array(
            'active' => true,
            'title' => __('Chat activity', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Messages · 7 days', 'peepso-core'), 'value' => $messages, 'trend' => self::calc_trend($messages, $prev_messages, __('vs prev 7d', 'peepso-core'))),
                array('label' => __('Total conversations', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(DISTINCT `mpart_msg_id`) FROM `{$wpdb->prefix}peepso_message_participants`"))),
                array('label' => __('Active conversations · 7 days', 'peepso-core'), 'value' => $active_conv, 'trend' => self::calc_trend($active_conv, $prev_active_conv, __('vs prev 7d', 'peepso-core'))),
            ),
            'manage_url' => admin_url('admin.php?page=peepso_config&tab=messages'),
        );
    }

    private static function get_photos_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoSharePhotos') || !self::table_exists($wpdb->prefix . 'peepso_photos')) {
            return self::placeholder_data(
                __('Photos', 'peepso-core'),
                __('Track uploads, albums, and photo storage here when Photos is active.', 'peepso-core')
            );
        }

        $seven_days    = gmdate('Y-m-d H:i:s', time() - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', time() - 14 * DAY_IN_SECONDS);
        $total_size    = intval($wpdb->get_var("SELECT COALESCE(SUM(`pho_size`),0) FROM `{$wpdb->prefix}peepso_photos`"));
        $uploads_7     = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_photos` WHERE `pho_created` >= %s", $seven_days)));
        $prev_uploads  = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_photos` WHERE `pho_created` >= %s AND `pho_created` < %s", $fourteen_days, $seven_days)));

        return array(
            'active' => true,
            'title' => __('Photos', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Photos total', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_photos`"))),
                array('label' => __('Uploads · 7 days', 'peepso-core'), 'value' => $uploads_7, 'trend' => self::calc_trend($uploads_7, $prev_uploads, __('vs prev 7d', 'peepso-core'))),
                array('label' => __('Albums', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_photos_album`"))),
                array('label' => __('Storage used', 'peepso-core'), 'value' => size_format($total_size)),
            ),
            'manage_url' => admin_url('admin.php?page=peepso_config&tab=photos'),
        );
    }

    private static function get_videos_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoVideos') || !self::table_exists($wpdb->prefix . 'peepso_videos')) {
            return self::placeholder_data(
                __('Media', 'peepso-core'),
                __('Track uploads, processing backlog, and failed conversions here when Videos is active.', 'peepso-core')
            );
        }

        $seven_days    = gmdate('Y-m-d H:i:s', time() - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', time() - 14 * DAY_IN_SECONDS);
        $uploads_7     = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_videos` WHERE `vid_created` >= %s", $seven_days)));
        $prev_uploads  = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_videos` WHERE `vid_created` >= %s AND `vid_created` < %s", $fourteen_days, $seven_days)));

        return array(
            'active' => true,
            'title' => __('Media', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Media total', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_videos`"))),
                array('label' => __('Uploads · 7 days', 'peepso-core'), 'value' => $uploads_7, 'trend' => self::calc_trend($uploads_7, $prev_uploads, __('vs prev 7d', 'peepso-core'))),
                array('label' => __('Pending conversions', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_videos` WHERE `vid_conversion_status` IN (0,1,6)"))),
                array('label' => __('Failed conversions', 'peepso-core'), 'value' => intval($wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_videos` WHERE `vid_conversion_status` = 3 OR `vid_stored_failed` = 1"))),
            ),
            'manage_url' => admin_url('admin.php?page=peepso_config&tab=videos'),
        );
    }

    private static function get_files_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoFileUploads')) {
            return self::placeholder_data(
                __('Files', 'peepso-core'),
                __('Track attached files shared in activity and comments here when Files is active.', 'peepso-core')
            );
        }

        $seven_days    = gmdate('Y-m-d H:i:s', time() - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', time() - 14 * DAY_IN_SECONDS);
        $query_total = "SELECT COUNT(a.ID)
            FROM `{$wpdb->posts}` a
            INNER JOIN `{$wpdb->posts}` p ON p.ID = a.post_parent
            WHERE a.post_type = 'attachment'
              AND p.post_type IN ('peepso-post','peepso-comment')
              AND a.post_mime_type NOT LIKE 'image/%'
              AND a.post_mime_type NOT LIKE 'video/%'";
        $query_recent = $wpdb->prepare($query_total . " AND a.post_date >= %s", $seven_days);
        $query_prev   = $wpdb->prepare($query_total . " AND a.post_date >= %s AND a.post_date < %s", $fourteen_days, $seven_days);
        $uploads_7    = intval($wpdb->get_var($query_recent));
        $prev_uploads = intval($wpdb->get_var($query_prev));

        return array(
            'active' => true,
            'title' => __('Files', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Files total', 'peepso-core'), 'value' => intval($wpdb->get_var($query_total))),
                array('label' => __('Uploads · 7 days', 'peepso-core'), 'value' => $uploads_7, 'trend' => self::calc_trend($uploads_7, $prev_uploads, __('vs prev 7d', 'peepso-core'))),
            ),
            'manage_url' => admin_url('admin.php?page=peepso_config&tab=fileuploads'),
        );
    }

    private static function get_email_digest_data()
    {
        global $wpdb;

        if (!class_exists('PeepSoEmailDigest') || !self::table_exists($wpdb->prefix . 'peepso_email_digest_log')) {
            return self::placeholder_data(
                __('Email Digest', 'peepso-core'),
                __('Track digest sends and schedule health here when Email Digest is active.', 'peepso-core')
            );
        }

        $seven_days    = gmdate('Y-m-d H:i:s', time() - 7  * DAY_IN_SECONDS);
        $fourteen_days = gmdate('Y-m-d H:i:s', time() - 14 * DAY_IN_SECONDS);
        $schedule_type = PeepSo::get_option('email_digest_schedule_type', 'daily');
        $enabled = intval(PeepSo::get_option('email_digest_enable', 0));
        if (PeepSo::get_option('email_digest_external_cron', 0) === 1) {
            $next_run = PeepSo::get_option('email_digest_schedule_timestamp');
        } else {
            $next_run = ($enabled) ? wp_next_scheduled(PeepSoEmailDigest::CRON_EMAIL_DIGEST_EVENT, array($schedule_type)) : false;
        }

        return array(
            'active' => true,
            'title' => __('Email Digest', 'peepso-core'),
            'metrics' => array(
                array('label' => __('Enabled', 'peepso-core'), 'value' => $enabled ? __('Yes', 'peepso-core') : __('No', 'peepso-core')),
                array('label' => __('Sent · 7 days', 'peepso-core'), 'value' => ($sent_7 = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_email_digest_log` WHERE `edl_sent` >= %s", $seven_days)))), 'trend' => self::calc_trend($sent_7, intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$wpdb->prefix}peepso_email_digest_log` WHERE `edl_sent` >= %s AND `edl_sent` < %s", $fourteen_days, $seven_days))), __('vs prev 7d', 'peepso-core'))),
                array('label' => __('Last sent', 'peepso-core'), 'value' => self::format_datetime($wpdb->get_var("SELECT MAX(`edl_sent`) FROM `{$wpdb->prefix}peepso_email_digest_log`"))),
                array('label' => __('Next run', 'peepso-core'), 'value' => $next_run ? self::format_datetime(gmdate('Y-m-d H:i:s', $next_run)) : __('Not scheduled', 'peepso-core')),
            ),
            'manage_url' => admin_url('admin.php?page=peepso_config&tab=email-digest'),
        );
    }

    private static function render_intro($data)
    {
        echo '<div class="ps-dashboard-card ps-dashboard-card--hero">';
        echo '<div>';
        echo '<div class="ps-dashboard-eyebrow">' . esc_html__('PeepSo Dashboard', 'peepso-core') . '</div>';
        echo '<h2>' . esc_html__('What matters right now', 'peepso-core') . '</h2>';
        echo '<p>' . esc_html__('This dashboard focuses on what you can act on today: community growth, recent activity, moderation pressure, and add-on areas that are active on this site.', 'peepso-core') . '</p>';
        echo '</div>';
        echo '<div class="ps-dashboard-hero-actions">';
        echo '<a class="button button-primary" href="' . esc_url(admin_url('admin.php?page=peepso-installer')) . '">' . esc_html__('See PeepSo Installer', 'peepso-core') . '</a>';
        echo '<a class="button" href="' . esc_url(admin_url('admin.php?page=peepso-getting-started')) . '">' . esc_html__('Getting Started', 'peepso-core') . '</a>';
        echo '</div>';
        echo '</div>';
    }

    private static function render_snapshot($cards)
    {
        $arrows = array('up' => '↑', 'down' => '↓', 'flat' => '→');

        echo '<div class="ps-dashboard-snapshot">';
        foreach ($cards as $card) {
            $tag   = !empty($card['url']) ? 'a' : 'div';
            $attrs = !empty($card['url']) ? ' href="' . esc_url($card['url']) . '"' : '';
            echo '<' . $tag . ' class="ps-dashboard-card ps-dashboard-stat"' . $attrs . '>';
            echo '<div class="ps-dashboard-stat__label">' . esc_html($card['label']) . '</div>';
            echo '<div class="ps-dashboard-stat__value">' . esc_html(self::display_value($card['value'])) . '</div>';

            if (!empty($card['trend'])) {
                $t     = $card['trend'];
                $arrow = isset($arrows[$t['dir']]) ? $arrows[$t['dir']] : '→';
                $pct   = $t['pct'] !== null ? esc_html($t['pct']) . '%' : esc_html__('New', 'peepso-core');
                echo '<div class="ps-dashboard-stat__trend ps-dashboard-stat__trend--' . esc_attr($t['dir']) . '">';
                echo '<span>' . $arrow . ' ' . $pct . '</span>';
                if (!empty($t['vs'])) {
                    echo '<span class="ps-dashboard-stat__trend-vs">' . esc_html($t['vs']) . '</span>';
                }
                echo '</div>';
            }

            echo '<div class="ps-dashboard-stat__note">' . esc_html($card['note']) . '</div>';
            echo '</' . $tag . '>';
        }
        echo '</div>';
    }

    private static function render_needs_attention($data)
    {
        echo '<section class="ps-dashboard-card">';
        echo '<div class="ps-dashboard-card__head"><h3>' . esc_html__('Needs attention', 'peepso-core') . '</h3></div>';

        echo '<div class="ps-dashboard-attention-grid">';
        echo '<div class="ps-dashboard-callout">';
        echo '<strong>' . esc_html__('Pending approvals', 'peepso-core') . '</strong>';
        echo '<span>' . sprintf(esc_html__('%d members are waiting for approval.', 'peepso-core'), intval($data['pending_total'])) . '</span>';
        echo '</div>';

        echo '<div class="ps-dashboard-callout">';
        echo '<strong>' . esc_html__('Open reports', 'peepso-core') . '</strong>';
        if ($data['open_reports'] > 0 && !empty($data['oldest_report'])) {
            echo '<span>' . sprintf(
                esc_html__('%1$d open. Oldest unresolved report: %2$s.', 'peepso-core'),
                intval($data['open_reports']),
                PeepSoTemplate::maybe_absolute_date(strtotime($data['oldest_report']->rep_timestamp))
            ) . '</span>';
        } else {
            echo '<span>' . esc_html__('No open reports right now.', 'peepso-core') . '</span>';
        }
        echo '</div>';

        echo '<div class="ps-dashboard-callout">';
        echo '<strong>' . esc_html__('Mail queue', 'peepso-core') . '</strong>';
        echo '<span>' . sprintf(esc_html__('%1$d queued, %2$d failed.', 'peepso-core'), intval($data['mail_backlog']), intval($data['mail_failed'])) . '</span>';
        echo '</div>';

        echo '<div class="ps-dashboard-callout">';
        echo '<strong>' . esc_html__('GDPR requests', 'peepso-core') . '</strong>';
        echo '<span>' . sprintf(esc_html__('%d requests are still pending.', 'peepso-core'), intval($data['gdpr_backlog'])) . '</span>';
        echo '</div>';
        echo '</div>';

        echo '<div class="ps-dashboard-subsection">';
        echo '<div class="ps-dashboard-subsection__head">';
        echo '<h4>' . esc_html__('Pending members', 'peepso-core') . '</h4>';
        echo '<a href="' . esc_url(admin_url('users.php?psrole=verified')) . '">' . esc_html__('See all', 'peepso-core') . '</a>';
        echo '</div>';
        if (!empty($data['pending_members'])) {
            echo '<ul class="ps-dashboard-list">';
            foreach ($data['pending_members'] as $member) {
                $user = PeepSoUser::get_instance($member->ID);
                $nonce = wp_create_nonce('update-role-nonce_' . $member->ID);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div class="ps-dashboard-user">';
                echo '<img src="' . esc_url($user->get_avatar()) . '" alt="" />';
                echo '<div>';
                echo '<strong><a href="' . esc_url(trailingslashit($user->get_profileurl()) . 'about/') . '" target="_blank" rel="noopener noreferrer">' . esc_html($user->get_fullname(true)) . '</a></strong>';
                echo '<span>' . PeepSoTemplate::maybe_absolute_date(strtotime($member->user_registered), false) . ' </span>';
                echo '</div>';
                echo '</div>';
                echo '<div class="ps-dashboard-actions">';
                echo '<a class="button button-small button-primary" href="' . esc_url(admin_url('admin.php?page=peepso&action=update-user-role&set=member&id=' . intval($member->ID) . '&_wpnonce=' . $nonce)) . '">' . esc_html__('Approve', 'peepso-core') . '</a>';
                echo '<a class="button button-small" href="' . esc_url(admin_url('admin.php?page=peepso&action=update-user-role&set=ban&id=' . intval($member->ID) . '&_wpnonce=' . $nonce)) . '">' . esc_html__('Dismiss and Ban', 'peepso-core') . '</a>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('Nobody is waiting for approval right now.', 'peepso-core'));
        }
        echo '</div>';

        echo '<div class="ps-dashboard-subsection">';
        echo '<div class="ps-dashboard-subsection__head">';
        echo '<h4>' . esc_html__('Unanswered posts', 'peepso-core') . '</h4>';
        echo '<span>' . esc_html__('Older than 24 hours with no replies', 'peepso-core') . '</span>';
        echo '</div>';
        if (!empty($data['unanswered_posts'])) {
            echo '<ul class="ps-dashboard-list">';
            foreach ($data['unanswered_posts'] as $post) {
                $author = get_userdata($post->post_author);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div>';
                echo '<strong><a href="' . esc_url(PeepSo::get_page('activity_status', FALSE) . $post->post_title) . '" target="_blank" rel="noopener noreferrer">' . esc_html(self::trim_text($post->post_content, 90)) . '</a></strong>';
                echo '<span>' . esc_html($author ? $author->display_name : __('Unknown member', 'peepso-core')) . ' · ' . PeepSoTemplate::maybe_absolute_date(strtotime($post->post_date)) . ' </span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('No unanswered posts are piling up right now.', 'peepso-core'));
        }
        echo '</div>';

        echo '</section>';
    }

    private static function render_activity_overview($data)
    {
        $series = array(
            'peepso-post'    => array('label' => __('Posts',     'peepso-core'), 'color' => '#FC832C'),
            'peepso-comment' => array('label' => __('Comments',  'peepso-core'), 'color' => '#4F97D9'),
            'likes'          => array('label' => __('Reactions', 'peepso-core'), 'color' => '#7BC968'),
        );
        $legend_items = array(
            'posts'     => array('label' => __('Posts',     'peepso-core'), 'color' => '#FC832C'),
            'comments'  => array('label' => __('Comments',  'peepso-core'), 'color' => '#4F97D9'),
            'reactions' => array('label' => __('Reactions', 'peepso-core'), 'color' => '#7BC968'),
        );
        $day_labels = array(
            __('Mon', 'peepso-core'), __('Tue', 'peepso-core'), __('Wed', 'peepso-core'),
            __('Thu', 'peepso-core'), __('Fri', 'peepso-core'), __('Sat', 'peepso-core'),
            __('Sun', 'peepso-core'),
        );

        echo '<section class="ps-dashboard-card ps-dashboard-card--activity">';

        // ── Card head ─────────────────────────────────────────────────────
        echo '<div class="ps-dashboard-card__head">';
        echo '<h3>' . esc_html__('Community activity', 'peepso-core') . '</h3>';
        echo '</div>';

        // ── Line chart ────────────────────────────────────────────────────
        echo '<div class="ps-chart-toggles">';
        foreach ($series as $type => $cfg) {
            echo '<span class="ps-chart-toggle ps-chart-toggle--active" data-series="' . esc_attr($type) . '">';
            echo '<span class="ps-chart-toggle__dot" style="background:' . esc_attr($cfg['color']) . '"></span>';
            echo '<span class="ps-chart-toggle__label">' . esc_html($cfg['label']) . '</span>';
            echo '<span class="ps-chart-toggle__total"></span>';
            echo '</span>';
        }
        echo '</div>';
        echo '<div class="ps-chart-canvas-wrap">';
        echo '<canvas id="ps-activity-chart" aria-label="' . esc_attr__('Community activity chart', 'peepso-core') . '" role="img"></canvas>';
        echo '</div>';

        // ── Content breakdown + heatmap (2-column row) ───────────────────
        echo '<div class="ps-activity-section-row">';
        echo '<div class="ps-activity-section">';
        echo '<div class="ps-activity-section__head"><h4>' . esc_html__('Content breakdown', 'peepso-core') . '</h4></div>';
        echo '<div class="ps-donut-wrap">';
        echo '<div class="ps-donut-canvas-wrap">';
        echo '<canvas id="ps-content-donut" aria-label="' . esc_attr__('Content type breakdown chart', 'peepso-core') . '" role="img"></canvas>';
        echo '<div class="ps-donut-center" id="ps-donut-center"><strong>—</strong><span>' . esc_html__('total', 'peepso-core') . '</span></div>';
        echo '</div>';
        echo '<ul class="ps-donut-legend" id="ps-donut-legend">';
        foreach ($legend_items as $key => $item) {
            echo '<li data-type="' . esc_attr($key) . '">';
            echo '<span class="ps-donut-legend__dot" style="background:' . esc_attr($item['color']) . '"></span>';
            echo '<span class="ps-donut-legend__label">' . esc_html($item['label']) . '</span>';
            echo '<span class="ps-donut-legend__value">—</span>';
            echo '<span class="ps-donut-legend__pct">—</span>';
            echo '</li>';
        }
        echo '</ul>';
        echo '</div>';
        echo '</div>';

        // ── Peak activity heatmap ─────────────────────────────────────────
        echo '<div class="ps-activity-section">';
        echo '<div class="ps-activity-section__head">';
        echo '<h4>' . esc_html__('Peak activity times', 'peepso-core') . '</h4>';
        echo '<span class="ps-dashboard-card__meta">' . esc_html__('Posts &amp; comments · Website timezone', 'peepso-core') . '</span>';
        echo '</div>';
        echo '<div class="ps-heatmap-wrap">';
        echo '<div id="ps-activity-heatmap" class="ps-heatmap"></div>';
        echo '<div class="ps-heatmap__legend">';
        echo '<span>' . esc_html__('Less', 'peepso-core') . '</span>';
        for ($i = 0; $i <= 4; $i++) {
            echo '<span class="ps-heatmap__legend-cell" style="--intensity:' . esc_attr($i / 4) . '"></span>';
        }
        echo '<span>' . esc_html__('More', 'peepso-core') . '</span>';
        echo '</div>';
        echo '</div>';
        echo '</div>';
        echo '</div>'; // .ps-activity-section-row

        // Localized strings + WP locale/format data for the chart JS (assets/js/admin/dashboard.js).
        // The on-page toggles/legend/day labels above are already translated server-side; this
        // object carries the canvas-internal labels and date/time formatting that Chart.js renders
        // client-side, so they follow the site language and the configured time format.
        global $wp_locale;
        $months = array();
        for ($m = 1; $m <= 12; $m++) {
            $months[] = $wp_locale->get_month_abbrev($wp_locale->get_month($m)); // translated short month
        }
        $time_format = get_option('time_format');

        $i18n = array(
            'series'     => wp_list_pluck($series, 'label'),        // keyed by type: peepso-post/comment/likes
            'donut'      => wp_list_pluck($legend_items, 'label'),  // keyed: posts/comments/reactions
            'days'       => array_values($day_labels),              // Mon-first, translated
            'months'     => $months,
            'is24h'      => (bool) preg_match('/(?<!\\\\)[GH]/', $time_format),
            'am'         => $wp_locale->get_meridiem('am') ?: 'am',
            'pm'         => $wp_locale->get_meridiem('pm') ?: 'pm',
            'actionOne'  => __('1 action', 'peepso-core'),
            'actionMany' => __('%d actions', 'peepso-core'),       // %d filled in JS
        );
        echo '<script>window.psDashboardI18n=' . wp_json_encode($i18n) . ';</script>';
        echo '</section>';
    }

    private static function render_member_pulse($data)
    {
        echo '<section class="ps-dashboard-card">';
        echo '<div class="ps-dashboard-card__head"><h3>' . esc_html__('Member pulse', 'peepso-core') . '</h3></div>';

        echo '<div class="ps-dashboard-subsection">';
        echo '<div class="ps-dashboard-subsection__head"><h4>' . esc_html__('Newest members', 'peepso-core') . '</h4><a href="' . esc_url(admin_url('users.php')) . '">' . esc_html__('Manage users', 'peepso-core') . '</a></div>';
        echo '<ul class="ps-dashboard-list">';
        foreach ($data['recent_members'] as $member) {
            $user = PeepSoUser::get_instance($member->ID);
            echo '<li class="ps-dashboard-list__item">';
            echo '<div class="ps-dashboard-user">';
            echo '<img src="' . esc_url($user->get_avatar()) . '" alt="" />';
            echo '<div><strong><a href="' . esc_url($user->get_profileurl()) . '" target="_blank" rel="noopener noreferrer">' . esc_html($user->get_fullname(true)) . '</a></strong><span>' . PeepSoTemplate::maybe_absolute_date(strtotime($member->user_registered), false) . ' </span></div>';
            echo '</div>';
            echo '</li>';
        }
        echo '</ul>';
        echo '</div>';

        echo '<div class="ps-dashboard-subsection">';
        echo '<div class="ps-dashboard-subsection__head"><h4>' . esc_html__('Recently active members', 'peepso-core') . '</h4></div>';
        if (!empty($data['active_members'])) {
            echo '<ul class="ps-dashboard-list">';
            foreach ($data['active_members'] as $member) {
                $user = PeepSoUser::get_instance($member->ID);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div class="ps-dashboard-user">';
                echo '<img src="' . esc_url($user->get_avatar()) . '" alt="" />';
                echo '<div><strong><a href="' . esc_url($user->get_profileurl()) . '" target="_blank" rel="noopener noreferrer">' . esc_html($user->get_fullname(true)) . '</a></strong><span>' . PeepSoTemplate::maybe_absolute_date(strtotime($member->usr_last_activity), false) . ' </span></div>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('No recent member activity has been recorded yet.', 'peepso-core'));
        }
        echo '</div>';
        echo '</section>';
    }

    private static function render_top_content($data)
    {
        echo '<section class="ps-dashboard-card ps-top-content">';
        echo '<div class="ps-dashboard-card__head"><h3>' . esc_html__('Top content', 'peepso-core') . '</h3></div>';
        echo '<div id="ps-top-content-wrap">';
        self::_render_top_content_tabs($data);
        echo '</div>';
        echo '</section>';
    }

    private static function _render_top_content_tabs($data)
    {
        $tabs = array(
            'top-engaged' => __('Top Engagement',           'peepso-core'),
            'high-views'  => __('High Views · Low Action',  'peepso-core'),
            'trending'    => __('Trending',                  'peepso-core'),
        );

        // Tab nav
        echo '<div class="ps-tc-tabs">';
        $first = true;
        foreach ($tabs as $id => $label) {
            $active = $first ? ' ps-tc-tab--active' : '';
            echo '<button class="ps-tc-tab' . $active . '" data-pane="' . esc_attr($id) . '">' . esc_html($label) . '</button>';
            $first = false;
        }
        echo '</div>';

        // Pane 1 — Top Engagement
        echo '<div class="ps-tc-pane" id="ps-tc-top-engaged">';
        if (!empty($data['top_engaged'])) {
            echo '<ul class="ps-dashboard-list">';
            foreach ($data['top_engaged'] as $item) {
                $content = PeepSo3_Utilities_String::maybe_strlen($hf = get_post_meta($item->ID, 'peepso_human_friendly', TRUE)) ? $hf : strip_tags(apply_filters('peepso_remove_shortcodes', $item->post_content, $item->ID));
                $author  = get_userdata($item->post_author);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div>';
                echo '<strong><a href="' . esc_url(PeepSo::get_page('activity_status', FALSE) . $item->post_title) . '" target="_blank" rel="noopener noreferrer">' . esc_html(self::trim_text($content, 100)) . '</a></strong>';
                echo '<span>' . esc_html($author ? $author->display_name : __('Unknown member', 'peepso-core')) . ' · ' . PeepSoTemplate::maybe_absolute_date(strtotime($item->post_date_gmt)) . '</span>';
                echo '</div>';
                echo '<div class="ps-dashboard-kpis">';
                echo '<span>' . sprintf(esc_html__('%d %s', 'peepso-core'), intval($item->comment_count), _n('comment', 'comments', $item->comment_count, 'peepso-core')) . '</span>';
                echo '<span>' . sprintf(esc_html__('%d %s', 'peepso-core'), intval($item->reaction_count), _n('reaction', 'reactions', $item->reaction_count, 'peepso-core')) . '</span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('Not enough data yet to rank posts by engagement.', 'peepso-core'));
        }
        echo '</div>';

        // Pane 2 — High Views · Low Interaction
        echo '<div class="ps-tc-pane" id="ps-tc-high-views" style="display:none">';
        if (!empty($data['high_views'])) {
            echo '<p class="ps-dashboard-footnote">' . esc_html__('Posts many people viewed but few engaged with — content that may need a stronger call to action.', 'peepso-core') . '</p>';
            echo '<ul class="ps-dashboard-list">';
            foreach ($data['high_views'] as $item) {
                $content = PeepSo3_Utilities_String::maybe_strlen($hf = get_post_meta($item->ID, 'peepso_human_friendly', TRUE)) ? $hf : strip_tags(apply_filters('peepso_remove_shortcodes', $item->post_content, $item->ID));
                $author  = get_userdata($item->post_author);
                $views   = intval($item->view_count);
                $inter   = intval($item->interaction_count);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div>';
                echo '<strong><a href="' . esc_url(PeepSo::get_page('activity_status', FALSE) . $item->post_title) . '" target="_blank" rel="noopener noreferrer">' . esc_html(self::trim_text($content, 100)) . '</a></strong>';
                echo '<span>' . esc_html($author ? $author->display_name : __('Unknown member', 'peepso-core')) . ' · ' . PeepSoTemplate::maybe_absolute_date(strtotime($item->post_date_gmt)) . '</span>';
                echo '</div>';
                echo '<div class="ps-dashboard-kpis">';
                echo '<span class="ps-kpi--views">' . sprintf(esc_html__('%d views', 'peepso-core'), $views) . '</span>';
                echo '<span class="ps-kpi--low">' . sprintf(esc_html__('%d interactions', 'peepso-core'), $inter) . '</span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('No posts with high views and low interaction found for this period.', 'peepso-core'));
        }
        echo '</div>';

        // Pane 3 — Trending
        echo '<div class="ps-tc-pane" id="ps-tc-trending" style="display:none">';
        if (!empty($data['trending'])) {
            echo '<p class="ps-dashboard-footnote">' . esc_html__('Posts created in the selected period, ranked by their recent engagement (comments, reactions and reposts over the last 30 days).', 'peepso-core') . '</p>';
            echo '<ul class="ps-dashboard-list">';
            foreach ($data['trending'] as $item) {
                $content = PeepSo3_Utilities_String::maybe_strlen($hf = get_post_meta($item->ID, 'peepso_human_friendly', TRUE)) ? $hf : strip_tags(apply_filters('peepso_remove_shortcodes', $item->post_content, $item->ID));
                $author  = get_userdata($item->post_author);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div>';
                echo '<strong><a href="' . esc_url(PeepSo::get_page('activity_status', FALSE) . $item->post_title) . '" target="_blank" rel="noopener noreferrer">' . esc_html(self::trim_text($content, 100)) . '</a></strong>';
                echo '<span>' . esc_html($author ? $author->display_name : __('Unknown member', 'peepso-core')) . ' · ' . PeepSoTemplate::maybe_absolute_date(strtotime($item->post_date_gmt)) . '</span>';
                echo '</div>';
                echo '<div class="ps-dashboard-kpis">';
                echo '<span>' . sprintf(esc_html__('%d views', 'peepso-core'), intval($item->view_count)) . '</span>';
                echo '<span>' . sprintf(esc_html__('%d %s', 'peepso-core'), intval($item->comment_count), _n('comment', 'comments', $item->comment_count, 'peepso-core')) . '</span>';
                echo '<span>' . sprintf(esc_html__('%d %s', 'peepso-core'), intval($item->reaction_count), _n('reaction', 'reactions', $item->reaction_count, 'peepso-core')) . '</span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('No trending posts found for this period.', 'peepso-core'));
        }
        echo '</div>';
    }

    public static function render_top_content_for_ajax(array $range)
    {
        ob_start();
        self::_render_top_content_tabs(self::get_top_content_data($range));
        return ob_get_clean();
    }

    private static function render_top_contributors($data)
    {
        echo '<section class="ps-dashboard-card ps-dashboard-card--contributors">';
        echo '<div class="ps-dashboard-card__head">';
        echo '<h3>' . esc_html__('Most active contributors', 'peepso-core') . '</h3>';
        echo '</div>';
        echo '<div id="ps-contributors-wrap">';
        self::_render_contributors_list($data);
        echo '</div>';
        echo '</section>';
    }

    private static function _render_contributors_list($rows)
    {
        $rank_styles = array(
            1 => 'background:#FFD700;color:#7A5800;',
            2 => 'background:#C0C0C0;color:#505050;',
            3 => 'background:#CD7F32;color:#fff;',
        );

        if (!empty($rows)) {
            $max = intval($rows[0]->total_count);
            echo '<ol class="ps-contributors-list">';
            foreach ($rows as $i => $row) {
                $rank  = $i + 1;
                $user  = PeepSoUser::get_instance($row->post_author);
                $total = intval($row->total_count);
                $posts = intval($row->post_count);
                $comms = intval($row->comment_count);
                $pct   = $max > 0 ? round($total / $max * 100) : 0;
                $rank_style = isset($rank_styles[$rank]) ? $rank_styles[$rank] : 'background:var(--bg);color:var(--ink-muted);';

                echo '<li class="ps-contributors-list__item">';
                echo '<span class="ps-contributor-rank" style="' . esc_attr($rank_style) . '">' . $rank . '</span>';
                echo '<img class="ps-contributor-avatar" src="' . esc_url($user->get_avatar()) . '" alt="" />';
                echo '<div class="ps-contributor-info">';
                echo '<a href="' . esc_url($user->get_profileurl()) . '" target="_blank" rel="noopener noreferrer" class="ps-contributor-name">' . esc_html($user->get_fullname(true)) . '</a>';
                echo '<div class="ps-contributor-bar"><div class="ps-contributor-bar__fill" style="width:' . $pct . '%"></div></div>';
                echo '</div>';
                echo '<div class="ps-dashboard-kpis">';
                echo '<span>' . sprintf(_n('%d post', '%d posts', $posts, 'peepso-core'), $posts) . '</span>';
                echo '<span>' . sprintf(_n('%d comment', '%d comments', $comms, 'peepso-core'), $comms) . '</span>';
                echo '<span class="ps-contributor-total">' . $total . '</span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ol>';
        } else {
            self::render_empty(__('No activity recorded for this period yet.', 'peepso-core'));
        }
    }

    public static function render_contributors_for_ajax(array $range)
    {
        ob_start();
        self::_render_contributors_list(self::get_top_contributors_data($range));
        return ob_get_clean();
    }

    private static function render_at_risk_panel($data)
    {
        $new_silent  = $data['new_silent'];
        $going_quiet = $data['going_quiet'];

        $tabs = array(
            'new-silent'  => sprintf(
                _n('New &amp; Silent (%d)', 'New &amp; Silent (%d)', count($new_silent), 'peepso-core'),
                count($new_silent)
            ),
            'going-quiet' => sprintf(
                _n('Going Quiet (%d)', 'Going Quiet (%d)', count($going_quiet), 'peepso-core'),
                count($going_quiet)
            ),
        );

        echo '<section class="ps-dashboard-card ps-dashboard-card--at-risk">';
        echo '<div class="ps-dashboard-card__head">';
        echo '<h3>' . esc_html__('At-risk users', 'peepso-core') . '</h3>';
        echo '</div>';

        echo '<div class="ps-tc-tabs">';
        $first = true;
        foreach ($tabs as $id => $label) {
            $active = $first ? ' ps-tc-tab--active' : '';
            echo '<button class="ps-tc-tab' . $active . '" data-pane="' . esc_attr($id) . '">' . $label . '</button>';
            $first = false;
        }
        echo '</div>';

        // Pane 1 — New & Silent
        echo '<div class="ps-tc-pane" id="ps-tc-new-silent">';
        echo '<p class="ps-dashboard-footnote">' . esc_html__('Members who joined in the last 30 days but have not posted or commented yet.', 'peepso-core') . '</p>';
        if (!empty($new_silent)) {
            echo '<ul class="ps-dashboard-list">';
            foreach ($new_silent as $member) {
                $user      = PeepSoUser::get_instance($member->ID);
                $days_ago  = floor((time() - strtotime($member->user_registered)) / DAY_IN_SECONDS);
                echo '<li class="ps-dashboard-list__item">';
                echo '<div class="ps-dashboard-user">';
                echo '<img src="' . esc_url($user->get_avatar()) . '" alt="" />';
                echo '<div>';
                echo '<strong><a href="' . esc_url($user->get_profileurl()) . '" target="_blank" rel="noopener noreferrer">' . esc_html($user->get_fullname(true)) . '</a></strong>';
                echo '<span>' . sprintf(
                    _n('Joined %d day ago · No posts yet', 'Joined %d days ago · No posts yet', $days_ago, 'peepso-core'),
                    $days_ago
                ) . '</span>';
                echo '</div>';
                echo '</div>';
                echo '<div class="ps-dashboard-kpis">';
                echo '<span class="ps-kpi--silent">' . esc_html__('Never posted', 'peepso-core') . '</span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('No new members are sitting idle right now.', 'peepso-core'));
        }
        echo '</div>';

        // Pane 2 — Going Quiet
        echo '<div class="ps-tc-pane" id="ps-tc-going-quiet" style="display:none">';
        echo '<p class="ps-dashboard-footnote">' . esc_html__('Members who were active 30–90 days ago but have not posted or commented in the last 30 days.', 'peepso-core') . '</p>';
        if (!empty($going_quiet)) {
            echo '<ul class="ps-dashboard-list">';
            foreach ($going_quiet as $member) {
                $user          = PeepSoUser::get_instance($member->ID);
                $last_post_ts  = strtotime($member->last_post_date);
                $last_seen_ts  = !empty($member->usr_last_activity) ? strtotime($member->usr_last_activity) : null;
                echo '<li class="ps-dashboard-list__item">';
                echo '<div class="ps-dashboard-user">';
                echo '<img src="' . esc_url($user->get_avatar()) . '" alt="" />';
                echo '<div>';
                echo '<strong><a href="' . esc_url($user->get_profileurl()) . '" target="_blank" rel="noopener noreferrer">' . esc_html($user->get_fullname(true)) . '</a></strong>';
                $meta = esc_html__('Last post:', 'peepso-core') . ' ' . PeepSoTemplate::maybe_absolute_date($last_post_ts, false);
                if ($last_seen_ts) {
                    $meta .= ' · ' . esc_html__('Last seen:', 'peepso-core') . ' ' . PeepSoTemplate::maybe_absolute_date($last_seen_ts, false);
                }
                echo '<span>' . $meta . '</span>';
                echo '</div>';
                echo '</div>';
                echo '<div class="ps-dashboard-kpis">';
                echo '<span class="ps-kpi--quiet">' . esc_html__('Gone quiet', 'peepso-core') . '</span>';
                echo '</div>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            self::render_empty(__('No previously active members have gone quiet recently.', 'peepso-core'));
        }
        echo '</div>';

        echo '</section>';
    }

    private static function render_addon_section($addons)
    {
        echo '<section class="ps-dashboard-section">';
        echo '<div class="ps-dashboard-section__head">';
        echo '<h3>' . esc_html__('Add-on areas', 'peepso-core') . '</h3>';
        echo '<p>' . esc_html__('Each card shows real data when that add-on is active. If it is missing or inactive, the dashboard keeps the space warm and points you to the installer.', 'peepso-core') . '</p>';
        echo '</div>';
        echo '<div class="ps-dashboard-grid ps-dashboard-grid--addons">';
        foreach ($addons as $key => $addon) {
            self::render_addon_card($key, $addon);
        }
        echo '</div>';
        echo '</section>';
    }

    private static function render_addon_card($key, $addon)
    {
        echo '<section class="ps-dashboard-card ps-dashboard-card--addon">';
        echo '<div class="ps-dashboard-card__head"><h3>' . esc_html($addon['title']) . '</h3></div>';

        if (empty($addon['active'])) {
            echo '<div class="ps-dashboard-placeholder">';
            echo '<p>' . esc_html($addon['description']) . '</p>';
            echo '<a class="button button-primary" href="' . esc_url(admin_url(self::PLACEHOLDER_INSTALLER_URL)) . '">' . esc_html__('See PeepSo Installer', 'peepso-core') . '</a>';
            echo '</div>';
            echo '</section>';
            return;
        }

        if (!empty($addon['metrics'])) {
            $arrows = array('up' => '↑', 'down' => '↓', 'flat' => '→');
            echo '<div class="ps-dashboard-mini-stats">';
            foreach ($addon['metrics'] as $metric) {
                echo '<div class="ps-dashboard-mini-stat">';
                echo '<span>' . esc_html($metric['label']) . '</span>';
                echo '<strong>' . esc_html(self::display_value($metric['value'])) . '</strong>';
                if (!empty($metric['trend'])) {
                    $t     = $metric['trend'];
                    $arrow = isset($arrows[$t['dir']]) ? $arrows[$t['dir']] : '→';
                    $pct   = $t['pct'] !== null ? esc_html($t['pct']) . '%' : esc_html__('New', 'peepso-core');
                    echo '<div class="ps-dashboard-stat__trend ps-dashboard-stat__trend--' . esc_attr($t['dir']) . ' ps-mini-stat__trend">';
                    echo '<span>' . $arrow . ' ' . $pct . '</span>';
                    if (!empty($t['vs'])) {
                        echo '<span class="ps-dashboard-stat__trend-vs">' . esc_html($t['vs']) . '</span>';
                    }
                    echo '</div>';
                }
                echo '</div>';
            }
            echo '</div>';
        }

        if (!empty($addon['items'])) {
            echo '<div class="ps-dashboard-subsection">';
            echo '<div class="ps-dashboard-subsection__head">';
            echo '<h4>' . esc_html($addon['item_label']) . '</h4>';
            if (!empty($addon['manage_url'])) {
                echo '<a href="' . esc_url($addon['manage_url']) . '">' . esc_html__('Manage', 'peepso-core') . '</a>';
            }
            echo '</div>';
            echo '<ul class="ps-dashboard-list">';
            foreach ($addon['items'] as $item) {
                $title = !empty($item->post_title) ? $item->post_title : __('Untitled', 'peepso-core');
                $count = isset($item->member_count) ? intval($item->member_count) : 0;
                $item_type = $addon['item_type'] == 'groups' ? _n('member', 'members', $count, 'peepso-core') : _n('follower', 'followers', $count, 'peepso-core');
                echo '<li class="ps-dashboard-list__item">';
                echo '<div><strong><a href="' . esc_url($addon['item_url'] . '?' . $item->post_name . '/') . '">' . esc_html($title) . '</a></strong></div>';
                echo '<div class="ps-dashboard-kpis"><span>' . esc_html(sprintf('%d %s', $count, $item_type)) . '</span></div>';
                echo '</li>';
            }
            echo '</ul>';
            echo '</div>';
        } elseif (!empty($addon['manage_url'])) {
            echo '<div class="ps-dashboard-footer-link"><a href="' . esc_url($addon['manage_url']) . '">' . esc_html__('Open settings', 'peepso-core') . '</a></div>';
        }

        echo '</section>';
    }

    private static function placeholder_data($title, $description)
    {
        return array(
            'active' => false,
            'title' => $title,
            'description' => $description,
        );
    }

    private static function table_exists($table)
    {
        global $wpdb;
        $like = $wpdb->esc_like($table);
        $result = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $like));
        return ($result === $table);
    }

    private static function trim_text($text, $length)
    {
        $text = trim(wp_strip_all_tags(strip_shortcodes($text)));
        if (!strlen($text)) {
            return __('Untitled post', 'peepso-core');
        }

        if (function_exists('mb_strlen') && mb_strlen($text) > $length) {
            return rtrim(mb_substr($text, 0, $length - 1)) . '…';
        }

        if (strlen($text) > $length) {
            return rtrim(substr($text, 0, $length - 1)) . '…';
        }

        return $text;
    }

    private static function format_datetime($datetime)
    {
        if (empty($datetime) || '0000-00-00 00:00:00' === $datetime) {
            return __('Never', 'peepso-core');
        }

        $timestamp = strtotime($datetime);
        if (!$timestamp) {
            return __('Never', 'peepso-core');
        }

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

    private static function display_value($value)
    {
        if (is_numeric($value)) {
            return number_format_i18n($value);
        }

        return $value;
    }

    private static function render_empty($message)
    {
        echo '<div class="ps-dashboard-empty">' . esc_html($message) . '</div>';
    }

    private static function render_styles()
    {
?>
        <style>
            .ps-dashboard-modern {
                --brand: #FC832C;
                --brand-hover: #E57420;
                --brand-dark: #C9651D;
                --brand-soft: #FEF1E5;
                --brand-glow: rgba(252, 131, 44, 0.12);
                --accent-green: #7BC968;
                --accent-green-soft: #ECF8E8;
                --ink: #1D2327;
                --ink-soft: #3C434A;
                --ink-muted: #646970;
                --ink-faded: #8C8F94;
                --bg: #F0F0F1;
                --bg-card: #FFFFFF;
                --rule: #DCDCDE;
                --rule-soft: #E8E8E9;
                --shadow-sm: 0 1px 1px rgba(0, 0, 0, 0.04);
                --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.04), 0 4px 12px rgba(0, 0, 0, 0.05);
                --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.04), 0 12px 32px rgba(0, 0, 0, 0.08);
                --shadow-brand: 0 4px 12px rgba(252, 131, 44, 0.28);
                --display: inherit;
                --body: inherit;
                margin-top: 20px;
            }

            .ps-dashboard-modern .button,
            .ps-dashboard-modern .button-secondary,
            .ps-dashboard-modern .button-link-delete,
            .ps-dashboard-modern .pa-btn,
            .ps-dashboard-modern input[type="submit"].button {
                font-family: var(--display) !important;
                font-size: 13px !important;
                font-weight: 700 !important;
                line-height: 1 !important;
                padding: 10px 20px !important;
                border-radius: 4px !important;
                border: 1px solid var(--rule) !important;
                background: transparent !important;
                color: var(--ink-muted) !important;
                box-shadow: none !important;
                text-shadow: none !important;
                cursor: pointer;
                display: inline-flex;
                align-items: center;
                gap: 6px;
                letter-spacing: 0.02em;
                transition: color 0.15s, border-color 0.15s, background 0.15s !important;
                height: auto !important;
                vertical-align: middle;
            }

            .ps-dashboard-modern .button:hover,
            .ps-dashboard-modern .button:focus,
            .ps-dashboard-modern .button-secondary:hover,
            .ps-dashboard-modern .button-secondary:focus,
            .ps-dashboard-modern .pa-btn:hover,
            .ps-dashboard-modern .pa-btn:focus {
                color: var(--ink) !important;
                border-color: var(--ink-faded) !important;
                background: transparent !important;
                box-shadow: none !important;
            }

            /* Primary / brand button */
            .ps-dashboard-modern .button-primary,
            .ps-dashboard-modern .button.button-primary,
            .ps-dashboard-modern input[type="submit"].button-primary,
            .ps-dashboard-modern .pa-btn--active,
            .ps-dashboard-modern .pa-btn.pa-btn--action {
                background: var(--brand) !important;
                color: #fff !important;
                border-color: var(--brand) !important;
                box-shadow: 0 1px 2px rgba(252, 131, 44, 0.3) !important;
                text-shadow: none !important;
            }

            .ps-dashboard-modern .button-primary:hover,
            .ps-dashboard-modern .button-primary:focus,
            .ps-dashboard-modern .button.button-primary:hover,
            .ps-dashboard-modern .button.button-primary:focus,
            .ps-dashboard-modern input[type="submit"].button-primary:hover,
            .ps-dashboard-modern input[type="submit"].button-primary:focus,
            .ps-dashboard-modern .pa-btn--active:hover,
            .ps-dashboard-modern .pa-btn--active:focus,
            .ps-dashboard-modern .pa-btn.pa-btn--action:hover,
            .ps-dashboard-modern .pa-btn.pa-btn--action:focus {
                background: var(--brand-hover) !important;
                border-color: var(--brand-hover) !important;
                box-shadow: var(--shadow-brand) !important;
                color: #fff !important;
                transform: translateY(-1px);
            }

            .ps-dashboard-card {
                background: #fff;
                border: 1px solid #dcdcde;
                border-radius: 14px;
                padding: 20px;
                box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
            }

            .ps-dashboard-card--hero {
                display: flex;
                justify-content: space-between;
                gap: 20px;
                align-items: flex-start;
                margin-bottom: 18px;
            }

            .ps-dashboard-card--hero h2 {
                margin: 4px 0 8px;
                font-size: 28px;
                line-height: 1.2;
            }

            .ps-dashboard-card--hero p {
                margin: 0;
                max-width: 820px;
                color: #50575e;
            }

            .ps-dashboard-eyebrow {
                font-size: 11px;
                letter-spacing: .08em;
                text-transform: uppercase;
                color: #fc832c;
                font-weight: 700;
            }

            .ps-dashboard-hero-actions {
                display: flex;
                gap: 10px;
                flex-wrap: wrap;
            }

            .ps-dashboard-top-row {
                display: grid;
                grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
                gap: 18px;
                margin-bottom: 20px;
                align-items: start;
            }

            .ps-dashboard-top-row__attention > .ps-dashboard-card {
                height: 100%;
                box-sizing: border-box;
            }

            .ps-dashboard-snapshot {
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 14px;
                margin-bottom: 0;
            }

            .ps-dashboard-stat {
                text-decoration: none;
                color: inherit;
            }

            .ps-dashboard-stat:hover {
                border-color: #fc832c;
                box-shadow: 0 4px 18px rgba(252, 131, 44, .12);
            }

            .ps-dashboard-stat__label {
                font-size: 12px;
                text-transform: uppercase;
                letter-spacing: .06em;
                color: #646970;
                margin-bottom: 10px;
                font-weight: 700;
            }

            .ps-dashboard-stat__value {
                font-size: 32px;
                line-height: 1;
                font-weight: 700;
                color: #1d2327;
                margin-bottom: 8px;
            }

            .ps-dashboard-stat__note {
                font-size: 13px;
                color: #646970;
            }

            .ps-dashboard-stat__trend {
                display: inline-flex;
                align-items: center;
                gap: 4px;
                font-size: 12px;
                font-weight: 700;
                padding: 3px 8px;
                border-radius: 99px;
                margin-bottom: 7px;
                line-height: 1.3;
            }

            .ps-dashboard-stat__trend--up   { background: #edfbf0; color: #0a7c2f; }
            .ps-dashboard-stat__trend--down { background: #fdf0ee; color: #c0392b; }
            .ps-dashboard-stat__trend--flat { background: #f6f7f7; color: #646970; }

            .ps-dashboard-stat__trend-vs {
                font-weight: 400;
                font-size: 11px;
                opacity: 0.8;
            }

            .ps-mini-stat__trend {
                margin-top: 8px;
                margin-bottom: 0;
            }

            .ps-dashboard-grid {
                display: grid;
                gap: 18px;
            }

            .ps-dashboard-grid--main {
                grid-template-columns: repeat(2, minmax(0, 1fr));
                margin-bottom: 20px;
            }

            .ps-dashboard-grid--addons {
                grid-template-columns: repeat(2, minmax(0, 1fr));
            }

            .ps-dashboard-card__head {
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: 10px;
                margin-bottom: 16px;
            }

            .ps-dashboard-card__head h3,
            .ps-dashboard-section__head h3 {
                margin: 0;
                font-size: 18px;
            }

            .ps-dashboard-section {
                margin-top: 8px;
            }

            .ps-dashboard-section__head {
                margin-bottom: 12px;
            }

            .ps-dashboard-section__head p {
                margin: 6px 0 0;
                color: #646970;
            }

            .ps-dashboard-attention-grid {
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 12px;
                margin-bottom: 16px;
            }

            .ps-dashboard-callout {
                background: #f6f7f7;
                border: 1px solid #dcdcde;
                border-radius: 12px;
                padding: 14px;
            }

            .ps-dashboard-callout strong {
                display: block;
                margin-bottom: 4px;
            }

            .ps-dashboard-callout span {
                color: #646970;
            }

            .ps-dashboard-subsection {
                margin-top: 18px;
            }

            .ps-dashboard-subsection__head {
                display: flex;
                align-items: baseline;
                justify-content: space-between;
                gap: 10px;
                margin-bottom: 10px;
            }

            .ps-dashboard-subsection__head h4 {
                margin: 0;
                font-size: 15px;
            }

            .ps-dashboard-subsection__head a,
            .ps-dashboard-subsection__head span,
            .ps-dashboard-footer-link a {
                font-size: 12px;
                color: #fc832c;
                text-decoration: none;
                font-weight: 600;
            }

            .ps-dashboard-list {
                list-style: none;
                margin: 0;
                padding: 0;
            }

            .ps-dashboard-list__item {
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: 12px;
                padding: 11px 0;
                border-top: 1px solid #f0f0f1;
                margin-bottom: 0;
            }

            .ps-dashboard-list__item:first-child {
                border-top: 0;
            }

            .ps-dashboard-list__item strong {
                display: block;
                font-size: 14px;
            }

            .ps-dashboard-list__item strong a {
                color: #1d2327;
                text-decoration: none;
            }

            .ps-dashboard-list__item strong a:hover {
                color: #fc832c;
            }

            .ps-dashboard-list__item span {
                font-size: 12px;
                color: #646970;
            }

            .ps-dashboard-user {
                display: flex;
                align-items: center;
                gap: 12px;
                min-width: 0;
            }

            .ps-dashboard-user img {
                width: 38px;
                height: 38px;
                border-radius: 50%;
                object-fit: cover;
            }

            .ps-dashboard-actions {
                display: flex;
                gap: 8px;
                flex-wrap: wrap;
                justify-content: flex-end;
            }

            .ps-dashboard-kpis {
                display: flex;
                gap: 12px;
                flex-wrap: wrap;
                justify-content: flex-end;
            }

            .ps-dashboard-kpis span {
                background: #f6f7f7;
                border-radius: 999px;
                padding: 4px 10px;
                color: #50575e;
            }

            .ps-dashboard-empty {
                padding: 14px;
                border: 1px dashed #dcdcde;
                border-radius: 12px;
                color: #646970;
                background: #fcfcfc;
            }

            .ps-dashboard-period-grid {
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 14px;
            }

            .ps-dashboard-period {
                background: #f6f7f7;
                border: 1px solid #dcdcde;
                border-radius: 12px;
                padding: 16px;
            }

            .ps-dashboard-period__title {
                font-size: 13px;
                font-weight: 700;
                margin-bottom: 10px;
            }

            .ps-dashboard-period dl {
                margin: 0;
            }

            .ps-dashboard-period dl div {
                display: flex;
                justify-content: space-between;
                gap: 10px;
                padding: 8px 0;
                border-top: 1px solid #e2e4e7;
            }

            .ps-dashboard-period dl div:first-child {
                border-top: 0;
                padding-top: 0;
            }

            .ps-dashboard-period dt {
                color: #646970;
            }

            .ps-dashboard-period dd {
                margin: 0;
                font-weight: 700;
            }

            .ps-dashboard-footnote {
                margin: 12px 0 0;
                color: #646970;
                font-size: 12px;
            }

            .ps-dashboard-mini-stats {
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 10px;
            }

            .ps-dashboard-mini-stat {
                background: #f6f7f7;
                border: 1px solid #ececec;
                border-radius: 12px;
                padding: 12px;
            }

            .ps-dashboard-mini-stat span {
                display: block;
                font-size: 12px;
                color: #646970;
                margin-bottom: 6px;
            }

            .ps-dashboard-mini-stat strong {
                display: block;
                font-size: 22px;
                line-height: 1.1;
            }

            .ps-dashboard-placeholder {
                padding: 0 0 4px;
            }

            .ps-dashboard-placeholder p {
                margin: 0 0 14px;
                color: #646970;
            }

            .ps-dashboard-footer-link {
                margin-top: 14px;
            }

            /* ── Global date range strip ─────────────────────────── */

            .ps-dashboard-range-strip {
                display: flex;
                align-items: center;
                flex-wrap: wrap;
                gap: 10px 14px;
                background: #fff;
                border: 1px solid var(--rule);
                border-radius: 10px;
                padding: 12px 16px;
                margin-bottom: 18px;
            }

            .ps-dashboard-range-strip__label {
                font-size: 12px;
                font-weight: 700;
                color: var(--ink-muted);
                text-transform: uppercase;
                letter-spacing: 0.05em;
                white-space: nowrap;
            }

            .ps-dashboard-range-strip .ps-custom-range {
                padding: 10px 0 0;
                background: none;
                border-top: 1px solid var(--rule-soft);
                width: 100%;
                margin-top: 4px;
            }

            /* ── Activity chart ──────────────────────────────────── */

            .ps-dashboard-card--activity {
                padding: 0;
                overflow: hidden;
            }

            .ps-dashboard-card--activity > .ps-dashboard-card__head {
                padding: 20px 20px 0;
                margin-bottom: 14px;
            }

            .ps-chart-ranges {
                display: flex;
                gap: 4px;
                flex-wrap: wrap;
            }

            .ps-chart-range-btn {
                background: none;
                border: 1px solid var(--rule);
                border-radius: 6px;
                padding: 5px 11px;
                font-size: 12px;
                font-weight: 600;
                color: var(--ink-muted);
                cursor: pointer;
                line-height: 1.4;
                transition: color 0.15s, border-color 0.15s, background 0.15s;
            }

            .ps-chart-range-btn:hover {
                border-color: var(--brand);
                color: var(--brand);
            }

            .ps-chart-range-btn--active {
                background: var(--brand);
                border-color: var(--brand);
                color: #fff !important;
            }

            .ps-custom-range {
                display: none;
                align-items: center;
                gap: 8px;
                padding: 10px 20px;
                background: var(--bg);
                border-top: 1px solid var(--rule-soft);
                flex-wrap: wrap;
            }

            .ps-custom-range.is-visible { display: flex; }

            .ps-custom-range label {
                font-size: 12px;
                color: var(--ink-muted);
                font-weight: 600;
            }

            .ps-range-input {
                border: 1px solid var(--rule);
                border-radius: 6px;
                padding: 5px 8px;
                font-size: 12px;
                color: var(--ink);
                background: #fff;
                cursor: pointer;
                line-height: 1.4;
            }

            .ps-range-input:focus {
                outline: none;
                border-color: var(--brand);
                box-shadow: 0 0 0 2px var(--brand-glow);
            }

            .ps-custom-range__sep {
                color: var(--ink-faded);
                font-size: 13px;
            }

            .ps-range-apply-btn {
                background: var(--brand);
                color: #fff;
                border: none;
                border-radius: 6px;
                padding: 6px 14px;
                font-size: 12px;
                font-weight: 700;
                cursor: pointer;
                line-height: 1.4;
                transition: background 0.15s;
            }

            .ps-range-apply-btn:hover { background: var(--brand-hover); }

            .ps-chart-toggles {
                display: flex;
                gap: 16px;
                padding: 0 20px 14px;
                flex-wrap: wrap;
            }

            .ps-chart-toggle {
                display: inline-flex;
                align-items: center;
                gap: 6px;
                cursor: pointer;
                font-size: 13px;
                color: var(--ink-muted);
                opacity: 0.35;
                transition: opacity 0.15s;
                user-select: none;
            }

            .ps-chart-toggle--active {
                opacity: 1;
                color: var(--ink);
            }

            .ps-chart-toggle__dot {
                width: 10px;
                height: 10px;
                border-radius: 50%;
                flex-shrink: 0;
            }

            .ps-chart-toggle__label {
                font-weight: 600;
            }

            .ps-chart-toggle__total {
                color: var(--ink-faded);
                font-size: 12px;
                min-width: 1ch;
            }

            .ps-chart-canvas-wrap {
                padding: 4px 16px 20px;
                position: relative;
                height: 230px;
            }

            #ps-activity-chart {
                display: block;
            }

            /* ── End activity chart ──────────────────────────────── */

            /* ── Activity sections (donut + heatmap inside card) ─── */

            .ps-dashboard-card--activity {
                grid-column: 1 / -1;
            }

            .ps-dashboard-card__meta {
                font-size: 12px;
                color: var(--ink-faded);
                white-space: nowrap;
            }

            .ps-activity-section {
                padding: 18px 20px 20px;
                border-top: 1px solid var(--rule-soft);
            }

            .ps-activity-section-row {
                display: grid;
                grid-template-columns: minmax(240px, 1fr) minmax(0, 2fr);
                border-top: 1px solid var(--rule-soft);
            }

            .ps-activity-section-row > .ps-activity-section {
                border-top: none;
            }

            .ps-activity-section-row > .ps-activity-section:first-child {
                border-right: 1px solid var(--rule-soft);
            }

            .ps-activity-section__head {
                display: flex;
                align-items: center;
                justify-content: space-between;
                margin-bottom: 16px;
            }

            .ps-activity-section__head h4 {
                margin: 0;
                font-size: 12px;
                font-weight: 700;
                color: var(--ink-muted);
                text-transform: uppercase;
                letter-spacing: 0.06em;
            }

            /* Donut */

            .ps-donut-wrap {
                display: flex;
                gap: 20px;
                align-items: center;
            }

            .ps-donut-canvas-wrap {
                position: relative;
                width: 130px;
                height: 130px;
                flex-shrink: 0;
            }

            .ps-donut-center {
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                text-align: center;
                line-height: 1.2;
                pointer-events: none;
            }

            .ps-donut-center strong {
                display: block;
                font-size: 20px;
                font-weight: 700;
                color: var(--ink);
            }

            .ps-donut-center span {
                font-size: 10px;
                color: var(--ink-faded);
                text-transform: uppercase;
                letter-spacing: 0.06em;
            }

            .ps-donut-legend {
                list-style: none;
                margin: 0;
                padding: 0;
                flex: 1;
                min-width: 0;
            }

            .ps-donut-legend li {
                display: flex;
                align-items: center;
                gap: 7px;
                padding: 7px 0;
                border-bottom: 1px solid var(--rule-soft);
                font-size: 13px;
            }

            .ps-donut-legend li:last-child { border-bottom: none; }

            .ps-donut-legend__dot {
                width: 9px;
                height: 9px;
                border-radius: 50%;
                flex-shrink: 0;
            }

            .ps-donut-legend__label {
                flex: 1;
                color: var(--ink-soft);
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }

            .ps-donut-legend__value { font-weight: 700; color: var(--ink); }

            .ps-donut-legend__pct {
                color: var(--ink-faded);
                font-size: 12px;
                min-width: 30px;
                text-align: right;
            }

            /* Heatmap */

            .ps-heatmap-wrap { overflow-x: auto; }

            .ps-heatmap {
                display: grid;
                grid-template-columns: 34px repeat(24, minmax(0, 1fr));
                gap: 3px;
                min-width: 420px;
            }

            .ps-heatmap__corner,
            .ps-heatmap__hour-label { height: 18px; }

            .ps-heatmap__hour-label {
                font-size: 10px;
                color: var(--ink-faded);
                text-align: center;
                display: flex;
                align-items: flex-end;
                justify-content: center;
                padding-bottom: 2px;
            }

            .ps-heatmap__day-label {
                font-size: 11px;
                color: var(--ink-muted);
                font-weight: 600;
                display: flex;
                align-items: center;
            }

            .ps-heatmap__cell {
                height: 20px;
                border-radius: 3px;
                position: relative;
                cursor: default;
                transition: transform 0.1s;
                background: var(--rule-soft);
            }

            .ps-heatmap__cell::after {
                content: '';
                position: absolute;
                inset: 0;
                border-radius: inherit;
                background: rgba(252, 131, 44, var(--intensity));
            }

            .ps-heatmap__cell:hover {
                transform: scale(1.25);
                z-index: 2;
            }

            .ps-heatmap__legend {
                display: flex;
                align-items: center;
                gap: 4px;
                margin-top: 10px;
                font-size: 11px;
                color: var(--ink-faded);
            }

            .ps-heatmap__legend-cell {
                display: inline-block;
                width: 13px;
                height: 13px;
                border-radius: 2px;
                background: var(--rule-soft);
                position: relative;
            }

            .ps-heatmap__legend-cell::after {
                content: '';
                position: absolute;
                inset: 0;
                border-radius: inherit;
                background: rgba(252, 131, 44, var(--intensity));
            }

            .ps-heatmap-tooltip {
                position: absolute;
                background: #1D2327;
                color: #fff;
                padding: 8px 12px;
                border-radius: 8px;
                font-size: 12px;
                line-height: 1.5;
                pointer-events: none;
                z-index: 99999;
                display: none;
                white-space: nowrap;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.22);
            }

            /* ── Chart.js external tooltip ───────────────────────── */

            .ps-chart-tooltip {
                position: absolute;
                background: #1D2327;
                color: #fff;
                padding: 8px 12px;
                border-radius: 8px;
                font-size: 12px;
                line-height: 1.5;
                pointer-events: none;
                z-index: 99999;
                display: none;
                white-space: nowrap;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.22);
            }

            .ps-chart-tooltip__title {
                color: rgba(255, 255, 255, 0.55);
                font-size: 11px;
                margin-bottom: 4px;
            }

            .ps-chart-tooltip__row {
                display: flex;
                align-items: center;
                gap: 6px;
            }

            .ps-chart-tooltip__dot {
                width: 8px;
                height: 8px;
                border-radius: 50%;
                flex-shrink: 0;
                display: inline-block;
            }

            /* ── End activity sections ────────────────────────────── */

            /* ── Top content tabs ─────────────────────────────────── */

            .ps-tc-tabs {
                display: flex;
                gap: 4px;
                flex-wrap: wrap;
                padding-bottom: 14px;
                border-bottom: 1px solid var(--rule-soft);
                margin-bottom: 2px;
            }

            .ps-tc-tab {
                background: none;
                border: 1px solid var(--rule);
                border-radius: 6px;
                padding: 5px 12px;
                font-size: 12px;
                font-weight: 600;
                color: var(--ink-muted);
                cursor: pointer;
                line-height: 1.4;
                transition: color 0.15s, border-color 0.15s, background 0.15s;
            }

            .ps-tc-tab:hover {
                border-color: var(--brand);
                color: var(--brand);
            }

            .ps-tc-tab--active {
                background: var(--brand);
                border-color: var(--brand);
                color: #fff !important;
            }

            .ps-kpi--views  { background: #EEF4FB; color: #2271B1; }
            .ps-kpi--low    { background: #FDF6EC; color: #996500; }
            .ps-kpi--silent { background: #F6F7F7; color: #646970; }
            .ps-kpi--quiet  { background: #FDF6EC; color: #8A4B00; }

            /* ── End top content tabs ─────────────────────────────── */

            /* ── Most active contributors ─────────────────────────── */

            .ps-dashboard-card--contributors {
                grid-column: 1 / -1;
            }

            .ps-contributors-list {
                list-style: none;
                margin: 0;
                padding: 0;
            }

            .ps-contributors-list__item {
                display: flex;
                align-items: center;
                gap: 12px;
                padding: 10px 0;
                border-top: 1px solid var(--rule-soft);
            }

            .ps-contributors-list__item:first-child {
                border-top: none;
            }

            .ps-contributor-rank {
                width: 26px;
                height: 26px;
                border-radius: 50%;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 11px;
                font-weight: 800;
                flex-shrink: 0;
            }

            .ps-contributor-avatar {
                width: 36px;
                height: 36px;
                border-radius: 50%;
                object-fit: cover;
                flex-shrink: 0;
            }

            .ps-contributor-info {
                flex: 1;
                min-width: 0;
            }

            .ps-contributor-name {
                display: block;
                font-size: 14px;
                font-weight: 600;
                color: var(--ink);
                text-decoration: none;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                margin-bottom: 5px;
            }

            .ps-contributor-name:hover { color: var(--brand); }

            .ps-contributor-bar {
                height: 4px;
                background: var(--rule-soft);
                border-radius: 99px;
                overflow: hidden;
            }

            .ps-contributor-bar__fill {
                height: 100%;
                background: var(--brand);
                border-radius: 99px;
                transition: width 0.4s ease;
            }

            .ps-contributor-total {
                font-weight: 700;
                color: var(--ink) !important;
                background: var(--brand-soft) !important;
                color: var(--brand-dark) !important;
            }

            /* ── Range-aware panel loading state ─────────────────── */

            .ps-range-loading {
                opacity: 0.4;
                pointer-events: none;
                transition: opacity 0.15s;
            }

            /* ── End most active contributors ─────────────────────── */

            /* ── At-risk users panel ──────────────────────────────── */

            .ps-dashboard-card--at-risk {
                grid-column: 1 / -1;
            }

            /* ── End at-risk users panel ──────────────────────────── */

            @media (max-width: 1080px) {

                .ps-dashboard-top-row,
                .ps-dashboard-snapshot,
                .ps-dashboard-grid--main,
                .ps-dashboard-grid--addons,
                .ps-dashboard-attention-grid,
                .ps-dashboard-period-grid,
                .ps-dashboard-mini-stats {
                    grid-template-columns: 1fr;
                }

                .ps-dashboard-card--hero {
                    flex-direction: column;
                }

                .ps-activity-section-row {
                    grid-template-columns: 1fr;
                }

                .ps-activity-section-row > .ps-activity-section:first-child {
                    border-right: none;
                    border-bottom: 1px solid var(--rule-soft);
                }
            }
        </style>
<?php
    }
}
