// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "mining_tab.h"
#include "mining_benchmark.h"
#include "mining_tab_helpers.h"
#include "mining_pool_panel.h"
#include "../../app.h"
#include "../../util/i18n.h"
#include "../../config/version.h"
#include "../../data/wallet_state.h"
#include "../../config/settings.h"
#include "../../util/platform.h"
#include "../schema/ui_schema.h"
#include "../material/type.h"
#include "../material/draw_helpers.h"
#include "../material/colors.h"
#include "../material/components/buttons.h"
#include "../effects/low_spec.h"
#include "../layout.h"
#include "../notifications.h"
#include "../../embedded/IconsMaterialDesign.h"
#include "imgui.h"
#include "imgui_internal.h"

#include <thread>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <vector>

namespace dragonx {
namespace ui {

using namespace material;

// Local UI state for thread grid
static int s_selected_threads = 0;
static bool s_threads_initialized = false;

// Drag-to-select state
static bool s_drag_active = false;
static int  s_drag_anchor_thread = 0;   // thread# where drag started

// Earnings filter: 0 = All, 1 = Solo, 2 = Pool
static int s_earnings_filter = 0;

static ThreadBenchmark s_benchmark;

bool IsMiningBenchmarkActive() {
    return s_benchmark.active();
}

// Pool mode state
static bool  s_pool_mode = false;
static char  s_pool_url[256] = "pool.dragonx.is:3433";
static char  s_pool_worker[256] = "x";
static bool  s_pool_settings_dirty = false;
static bool  s_pool_state_loaded = false;
static bool  s_show_pool_log = false;  // Toggle: false=chart, true=log
static bool  s_show_solo_log = false;  // Toggle: false=chart, true=log (solo mode)

static void RenderMiningTabContent(App* app);

void RenderMiningTab(App* app)
{
    // Scrollable child to contain all content within available space
    ImVec2 miningAvail = ImGui::GetContentRegionAvail();
    ImGui::BeginChild("##MiningScroll", miningAvail, false, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);

    ImGuiErrorRecoveryState erState;
    ImGui::ErrorRecoveryStoreState(&erState);
    try {
        RenderMiningTabContent(app);
    } catch (const std::exception& e) {
        DEBUG_LOGF("[MiningTab] Exception: %s\n", e.what());
        ImGui::ErrorRecoveryTryToRecoverState(&erState);
    } catch (...) {
        DEBUG_LOGF("[MiningTab] Unknown exception\n");
        ImGui::ErrorRecoveryTryToRecoverState(&erState);
    }

    ImGui::EndChild(); // ##MiningScroll
}

static void RenderMiningTabContent(App* app)
{
    auto& S = schema::UI();
    ImVec2 miningAvail = ImGui::GetContentRegionAvail();
    auto sliderInput = S.input("tabs.mining", "thread-slider");
    auto startBtn    = S.button("tabs.mining", "start-button");
    auto lbl         = S.label("tabs.mining", "label-column");
    const auto& state = app->getWalletState();
    const auto& mining = state.mining;

    // Responsive: scale factors per frame
    float availWidth = ImGui::GetContentRegionAvail().x;
    float hs = Layout::hScale(availWidth);
    float vs = Layout::vScale(miningAvail.y);
    float pad = Layout::cardInnerPadding();
    float gap = Layout::cardGap();
    const float dp = Layout::dpiScale();
    auto tier = Layout::currentTier(availWidth, miningAvail.y);
    (void)tier;

    int max_threads = GetMaxMiningThreads();
    
    if (!s_threads_initialized) {
        int saved = app->settings()->getPoolThreads();
        if (mining.generate)
            s_selected_threads = ClampMiningThreads(mining.genproclimit, max_threads);
        else if (saved > 0)
            s_selected_threads = ClampMiningThreads(saved, max_threads);
        else
            s_selected_threads = 1;
        s_threads_initialized = true;
    }

    // Sync thread grid with actual count when idle thread scaling adjusts threads
    // Skip during benchmark — the benchmark controls thread counts directly
    if (app->settings()->getMineWhenIdle() && app->settings()->getIdleThreadScaling() && !s_drag_active && !IsMiningBenchmarkActive()) {
        if (s_pool_mode && state.pool_mining.xmrig_running) {
            // Use the requested thread count (available immediately) rather
            // than threads_active from the xmrig API which lags during restarts.
            int reqThreads = app->getXmrigRequestedThreads();
            if (reqThreads > 0)
                s_selected_threads = ClampMiningThreads(reqThreads, max_threads);
            else if (state.pool_mining.threads_active > 0)
                s_selected_threads = ClampMiningThreads(state.pool_mining.threads_active, max_threads);
        } else if (mining.generate && mining.genproclimit > 0) {
            s_selected_threads = ClampMiningThreads(mining.genproclimit, max_threads);
        }
    }

    ImDrawList* dl = ImGui::GetWindowDrawList();
    GlassPanelSpec glassSpec;
    glassSpec.rounding = Layout::glassRounding();
    ImFont* ovFont  = Type().overline();
    ImFont* capFont = Type().caption();
    ImFont* sub1    = Type().subtitle1();
    if (!ovFont || !capFont || !sub1) {
        return;
    }
    char buf[128];

    // Load pool state from settings on first frame
    if (!s_pool_state_loaded) {
        s_pool_mode = app->settings()->getPoolMode();
        strncpy(s_pool_url, app->settings()->getPoolUrl().c_str(), sizeof(s_pool_url) - 1);
        strncpy(s_pool_worker, app->settings()->getPoolWorker().c_str(), sizeof(s_pool_worker) - 1);
        s_pool_state_loaded = true;
    }
    if (!app->supportsSoloMining() && !s_pool_mode) {
        s_pool_mode = true;
        app->settings()->setPoolMode(true);
        app->settings()->save();
    }

    // Default pool worker to user's first shielded (z) address once available.
    // For new wallets without a z-address, leave the field blank so the user
    // is prompted to generate one before mining.
    {
        static bool s_pool_worker_defaulted = false;
        std::string workerStr(s_pool_worker);
        if (shouldDefaultPoolWorker(workerStr, s_pool_worker_defaulted) && !state.addresses.empty()) {
            std::string defaultAddr = defaultPoolWorkerAddress(state.addresses);
            if (!defaultAddr.empty()) {
                strncpy(s_pool_worker, defaultAddr.c_str(), sizeof(s_pool_worker) - 1);
                s_pool_worker[sizeof(s_pool_worker) - 1] = '\0';
                s_pool_settings_dirty = true;
            } else {
                // No z-address yet — clear the placeholder "x" so field shows empty
                s_pool_worker[0] = '\0';
                s_pool_settings_dirty = true;
            }
            s_pool_worker_defaulted = true;
        }
    }

    // Persist pool settings when dirty and no field is active
    if (s_pool_settings_dirty && !ImGui::IsAnyItemActive()) {
        app->settings()->setPoolUrl(s_pool_url);
        app->settings()->setPoolWorker(s_pool_worker);
        app->settings()->save();
        s_pool_settings_dirty = false;

        // Auto-restart pool miner if it is currently running so the new
        // URL / worker address takes effect immediately.
        if (state.pool_mining.xmrig_running) {
            app->stopPoolMining();
            app->startPoolMining(s_selected_threads);
        }
    }

    // Determine active mining state for UI
    // Include pool mining running state even when user just switched to solo,
    // so the button shows STOP/STOPPING while xmrig shuts down.
    bool isMiningActive = IsPoolMiningActive(s_pool_mode,
                                             state.pool_mining.xmrig_running,
                                             mining.generate);

    // ================================================================
    // Thread Benchmark state machine — runs pool mining at each candidate
    // thread count to find the optimal setting for this CPU.
    // ================================================================
    if (s_benchmark.active()) {
        auto benchmarkUpdate = AdvanceThreadBenchmark(
            s_benchmark, ImGui::GetIO().DeltaTime, state.pool_mining.hashrate_10s);
        if (benchmarkUpdate.stopPoolMining) {
            app->stopPoolMining();
        }
        if (benchmarkUpdate.saveOptimalThreads) {
            s_selected_threads = benchmarkUpdate.optimalThreads;
            app->settings()->setPoolThreads(s_selected_threads);
            app->settings()->save();
        }
        if (benchmarkUpdate.startPoolMining) {
            app->startPoolMining(benchmarkUpdate.startThreads);
        }
    }

    // ================================================================
    // Proportional section budget — ensures all content fits without
    // scrolling at the minimum window size (1024×775).
    // ================================================================
    float sHdr = ovFont->LegacySize + Layout::spacingXs()
               + ImGui::GetStyle().ItemSpacing.y * 2.0f;
    float gapOver = gap + ImGui::GetStyle().ItemSpacing.y;
    // 3 sections with headers (CHART+STATS, DETAILS+EARNINGS, BLOCKS)
    float totalOverhead = 3.0f * (sHdr + gapOver) + 1.0f * gapOver;
    float cardBudget = std::max(schema::UI().drawElement("tabs.mining", "card-budget-min").size, miningAvail.y - totalOverhead);

    Layout::SectionBudget cb(cardBudget);
    float controlsBudgetH = cb.allocate(0.26f, 80.0f * dp);
    float chartBudgetH    = cb.allocate(0.22f, 60.0f * dp);
    (void)cb; // remaining budget used by combined earnings+details card

    // ================================================================
    // MODE TOGGLE — SOLO | POOL segmented control
    // ================================================================
    {
        const bool soloMiningAvailable = app->supportsSoloMining();
        float toggleW  = schema::UI().drawElement("tabs.mining", "mode-toggle-width").size * hs;
        float toggleH  = schema::UI().drawElement("tabs.mining", "mode-toggle-height").size;
        float toggleRnd = schema::UI().drawElement("tabs.mining", "mode-toggle-rounding").size;
        float totalW   = soloMiningAvailable ? (toggleW * 2) : toggleW;

        ImVec2 tMin = ImGui::GetCursorScreenPos();
        ImVec2 tMax(tMin.x + totalW, tMin.y + toggleH);

        // Glass background for the segmented control
        dl->AddRectFilled(tMin, tMax, WithAlpha(OnSurface(), 15), toggleRnd);
        dl->AddRect(tMin, tMax, WithAlpha(OnSurface(), 40), toggleRnd);

        ImVec2 soloMin = tMin;
        ImVec2 soloMax(tMin.x + toggleW, tMax.y);
        bool soloHov = material::IsRectHovered(soloMin, soloMax);
        if (soloMiningAvailable) {
            // SOLO button (left half)
            if (!s_pool_mode) {
                dl->AddRectFilled(soloMin, soloMax, WithAlpha(Primary(), 180), toggleRnd);
            } else if (soloHov) {
                dl->AddRectFilled(soloMin, soloMax, WithAlpha(OnSurface(), 20), toggleRnd);
            }
            const char* label = TR("mining_solo");
            ImVec2 sz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, label);
            float lx = soloMin.x + (toggleW - sz.x) * 0.5f;
            float ly = soloMin.y + (toggleH - sz.y) * 0.5f;
            ImU32 col = !s_pool_mode ? IM_COL32(255, 255, 255, 230) : OnSurfaceMedium();
            dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lx, ly), col, label);
        }

        // POOL button (right half) — disabled when solo mining is active
        bool soloMiningActive = mining.generate;
        ImVec2 poolMin(tMin.x + (soloMiningAvailable ? toggleW : 0.0f), tMin.y);
        ImVec2 poolMax = tMax;
        bool poolHov = material::IsRectHovered(poolMin, poolMax);
        if (s_pool_mode) {
            dl->AddRectFilled(poolMin, poolMax, WithAlpha(Primary(), 180), toggleRnd);
        } else if (soloMiningActive) {
            // Dimmed — solo mining blocks pool mode
        } else if (poolHov) {
            dl->AddRectFilled(poolMin, poolMax, WithAlpha(OnSurface(), 20), toggleRnd);
        }
        {
            const char* label = TR("mining_pool");
            ImVec2 sz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, label);
            float lx = poolMin.x + (toggleW - sz.x) * 0.5f;
            float ly = poolMin.y + (toggleH - sz.y) * 0.5f;
            ImU32 col = s_pool_mode ? IM_COL32(255, 255, 255, 230)
                      : (soloMiningActive ? OnSurfaceDisabled() : OnSurfaceMedium());
            dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lx, ly), col, label);
        }

        // Invisible buttons for click targets
        if (soloMiningAvailable) {
            ImGui::SetCursorScreenPos(soloMin);
            ImGui::InvisibleButton("##SoloMode", ImVec2(toggleW, toggleH));
            if (ImGui::IsItemClicked() && s_pool_mode) {
                s_pool_mode = false;
                app->settings()->setPoolMode(false);
                app->settings()->save();
                app->stopPoolMining();
            }
            if (soloHov) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
        }

        ImGui::SetCursorScreenPos(poolMin);
        ImGui::InvisibleButton("##PoolMode", ImVec2(toggleW, toggleH));
        if (soloMiningAvailable && ImGui::IsItemClicked() && !s_pool_mode && !soloMiningActive) {
            s_pool_mode = true;
            app->settings()->setPoolMode(true);
            app->settings()->save();
            // Note: soloMiningActive is already false (checked above),
            // so no need to call stopMining() — it would just set the
            // toggle-in-progress flag and make the button show "STARTING".
        }
        if (soloMiningAvailable && poolHov && !soloMiningActive) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
        if (poolHov && soloMiningActive && !s_pool_mode) {
            ImGui::SetTooltip("%s", TR("mining_stop_solo_for_pool"));
        }

        ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y));
        ImGui::Dummy(ImVec2(totalW, 0));

        // Pool URL + Worker inputs inline next to toggle (pool mode only)
        if (s_pool_mode && soloMiningActive) {
            // Solo mining is active — show disabled message instead of inputs
            float inputFrameH = ImGui::GetFrameHeight();
            float vertOff = (toggleH - inputFrameH) * 0.5f;
            ImGui::SetCursorScreenPos(ImVec2(tMax.x + Layout::spacingLg(), tMin.y + vertOff));

            ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Warning()));
            ImGui::AlignTextToFramePadding();
            ImGui::PushFont(Type().iconSmall());
            ImGui::TextUnformatted(ICON_MD_INFO);
            ImGui::PopFont();
            ImGui::SameLine(0, Layout::spacingSm());
            ImGui::PushFont(capFont);
            ImGui::TextUnformatted(TR("mining_stop_solo_for_pool_settings"));
            ImGui::PopFont();
            ImGui::PopStyleColor();
        } else if (s_pool_mode) {
            // Position inputs to the right of the toggle
            float inputFrameH = ImGui::GetFrameHeight();
            float vertOff = (toggleH - inputFrameH) * 0.5f;
            float inputsStartX = tMax.x + Layout::spacingLg();
            ImGui::SetCursorScreenPos(ImVec2(inputsStartX, tMin.y + vertOff));

            ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp);
            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8 * dp, 4 * dp));

            float inputFrameH2 = ImGui::GetFrameHeight();
            float iconBtnW = inputFrameH2;
            float resetBtnW = iconBtnW;
            float contentEndX = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;
            // Each input group: [input][▼][bookmark]
            // Layout: [URL group] [spacing] [Worker group] [spacing] [reset]
            float perGroupExtra = iconBtnW * 2;  // dropdown + bookmark
            float remainW = contentEndX - inputsStartX - Layout::spacingSm() - resetBtnW
                            - Layout::spacingSm() - perGroupExtra * 2;
            float urlW  = std::max(60.0f, remainW * 0.30f);
            float wrkW  = std::max(40.0f, remainW * 0.70f);

            // Track positions for popup alignment
            float urlGroupStartX = ImGui::GetCursorScreenPos().x;
            float urlGroupStartY = ImGui::GetCursorScreenPos().y;
            float urlGroupW = urlW + perGroupExtra;

            // === Pool URL input ===
            ImGui::SetNextItemWidth(urlW);
            if (ImGui::InputTextWithHint("##PoolURL", TR("mining_pool_url"), s_pool_url, sizeof(s_pool_url))) {
                s_pool_settings_dirty = true;
            }

            // --- URL: Dropdown arrow button ---
            ImGui::SameLine(0, 0);
            {
                ImVec2 btnPos = ImGui::GetCursorScreenPos();
                ImVec2 btnSize(iconBtnW, inputFrameH2);
                ImGui::InvisibleButton("##PoolDropdown", btnSize);
                bool btnHov = ImGui::IsItemHovered();
                bool btnClk = ImGui::IsItemClicked();
                ImDrawList* dl2 = ImGui::GetWindowDrawList();
                ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
                if (btnHov) {
                    dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
                                       StateHover(), 4.0f * dp);
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", TR("mining_saved_pools"));
                }
                ImFont* icoFont = Type().iconSmall();
                const char* dropIcon = ICON_MD_ARROW_DROP_DOWN;
                ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, dropIcon);
                dl2->AddText(icoFont, icoFont->LegacySize,
                             ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
                             OnSurfaceMedium(), dropIcon);
                if (btnClk) {
                    ImGui::OpenPopup("##SavedPoolsPopup");
                }
            }

            // --- URL: Bookmark button ---
            ImGui::SameLine(0, 0);
            {
                ImVec2 btnPos = ImGui::GetCursorScreenPos();
                ImVec2 btnSize(iconBtnW, inputFrameH2);
                ImGui::InvisibleButton("##SavePoolUrl", btnSize);
                bool btnHov = ImGui::IsItemHovered();
                bool btnClk = ImGui::IsItemClicked();
                ImDrawList* dl2 = ImGui::GetWindowDrawList();
                ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
                std::string currentUrl(s_pool_url);
                bool alreadySaved = miningValueAlreadySaved(app->settings()->getSavedPoolUrls(), currentUrl);
                if (btnHov) {
                    dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
                                       StateHover(), 4.0f * dp);
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", alreadySaved ? TR("mining_already_saved") : TR("mining_save_pool_url"));
                }
                ImFont* icoFont = Type().iconSmall();
                const char* saveIcon = alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER;
                ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, saveIcon);
                dl2->AddText(icoFont, icoFont->LegacySize,
                             ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
                             alreadySaved ? Primary() : OnSurfaceMedium(), saveIcon);
                if (btnClk && !currentUrl.empty() && !alreadySaved) {
                    app->settings()->addSavedPoolUrl(currentUrl);
                    app->settings()->save();
                }
            }

            // --- URL: Popup positioned below the input group ---
            // Match popup width to input group; zero horizontal padding so
            // item highlights are flush with the popup container edges.
            ImGui::SetNextWindowPos(ImVec2(urlGroupStartX, urlGroupStartY + inputFrameH2));
            ImGui::SetNextWindowSize(ImVec2(urlGroupW, 0));
            ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f * dp);
            ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2 * dp));
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
            if (ImGui::BeginPopup("##SavedPoolsPopup")) {
                const auto& savedUrls = app->settings()->getSavedPoolUrls();
                if (savedUrls.empty()) {
                    ImGui::SetCursorPosX(8 * dp);
                    ImGui::PushFont(Type().caption());
                    ImGui::TextDisabled("%s", TR("mining_no_saved_pools"));
                    ImGui::PopFont();
                    ImGui::SetCursorPosX(8 * dp);
                    ImGui::PushFont(Type().caption());
                    ImGui::TextDisabled("%s", TR("mining_click"));
                    ImGui::PopFont();
                    ImGui::SameLine(0, 2 * dp);
                    ImGui::PushFont(Type().iconSmall());
                    ImGui::TextDisabled(ICON_MD_BOOKMARK_BORDER);
                    ImGui::PopFont();
                    ImGui::SameLine(0, 2 * dp);
                    ImGui::PushFont(Type().caption());
                    ImGui::TextDisabled("%s", TR("mining_to_save"));
                    ImGui::PopFont();
                } else {
                    std::string urlToRemove;
                    float popupInnerW = ImGui::GetContentRegionAvail().x;
                    float xZoneW = ImGui::GetFrameHeight();
                    float textPadX = 8 * dp;
                    ImFont* rowFont = ImGui::GetFont();
                    float rowFontSz = ImGui::GetFontSize();
                    float rowH = ImGui::GetFrameHeight();
                    for (const auto& url : savedUrls) {
                        ImGui::PushID(url.c_str());
                        bool isCurrent = (std::string(s_pool_url) == url);
                        ImVec2 rowMin = ImGui::GetCursorScreenPos();
                        ImVec2 rowMax(rowMin.x + popupInnerW, rowMin.y + rowH);
                        ImGui::InvisibleButton("##row", ImVec2(popupInnerW, rowH));
                        bool rowHov = ImGui::IsItemHovered();
                        bool rowClk = ImGui::IsItemClicked();
                        ImDrawList* pdl = ImGui::GetWindowDrawList();
                        bool inXZone = rowHov && ImGui::GetMousePos().x >= rowMax.x - xZoneW;
                        // Row background — flush with popup edges
                        if (isCurrent)
                            pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10));
                        if (rowHov && !inXZone)
                            pdl->AddRectFilled(rowMin, rowMax, StateHover());
                        // Item text with internal padding
                        float textY = rowMin.y + (rowH - rowFontSz) * 0.5f;
                        pdl->AddText(rowFont, rowFontSz,
                            ImVec2(rowMin.x + textPadX, textY),
                            isCurrent ? Primary() : OnSurface(), url.c_str());
                        // X button — flush with right edge, icon centered
                        {
                            ImVec2 xMin(rowMax.x - xZoneW, rowMin.y);
                            ImVec2 xMax(rowMax.x, rowMax.y);
                            if (inXZone) {
                                pdl->AddRectFilled(xMin, xMax, IM_COL32(255, 80, 80, 30));
                                ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                                ImGui::SetTooltip("%s", TR("mining_remove"));
                            } else if (rowHov) {
                                // Show faint X when row is hovered
                                ImFont* icoF = Type().iconSmall();
                                const char* xIcon = ICON_MD_CLOSE;
                                ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon);
                                ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f);
                                pdl->AddText(icoF, icoF->LegacySize,
                                    ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f),
                                    OnSurfaceDisabled(), xIcon);
                            }
                            // Always draw icon when hovering X zone
                            if (inXZone) {
                                ImFont* icoF = Type().iconSmall();
                                const char* xIcon = ICON_MD_CLOSE;
                                ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon);
                                ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f);
                                pdl->AddText(icoF, icoF->LegacySize,
                                    ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f),
                                    Error(), xIcon);
                            }
                        }
                        // Click handling
                        if (rowClk) {
                            if (inXZone) {
                                urlToRemove = url;
                            } else {
                                strncpy(s_pool_url, url.c_str(), sizeof(s_pool_url) - 1);
                                s_pool_url[sizeof(s_pool_url) - 1] = '\0';
                                s_pool_settings_dirty = true;
                                ImGui::CloseCurrentPopup();
                            }
                        }
                        if (rowHov && !inXZone)
                            ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::PopID();
                    }
                    if (!urlToRemove.empty()) {
                        app->settings()->removeSavedPoolUrl(urlToRemove);
                        app->settings()->save();
                    }
                }
                ImGui::EndPopup();
            }
            ImGui::PopStyleVar(3);  // WindowRounding, WindowPadding, ItemSpacing for URL popup
            ImGui::SameLine(0, Layout::spacingSm());
            float wrkGroupStartX = ImGui::GetCursorScreenPos().x;
            float wrkGroupStartY = ImGui::GetCursorScreenPos().y;
            float wrkGroupW = wrkW + perGroupExtra;

            ImGui::SetNextItemWidth(wrkW);
            if (ImGui::InputTextWithHint("##PoolWorker", TR("mining_payout_address"), s_pool_worker, sizeof(s_pool_worker))) {
                s_pool_settings_dirty = true;
            }
            if (ImGui::IsItemHovered()) {
                std::string currentWorkerStr(s_pool_worker);
                if (currentWorkerStr.empty()) {
                    ImGui::SetTooltip("%s", TR("mining_generate_z_address_hint"));
                } else {
                    ImGui::SetTooltip("%s", TR("mining_payout_tooltip"));
                }
            }

            // --- Worker: Dropdown arrow button ---
            ImGui::SameLine(0, 0);
            {
                ImVec2 btnPos = ImGui::GetCursorScreenPos();
                ImVec2 btnSize(iconBtnW, inputFrameH2);
                ImGui::InvisibleButton("##WorkerDropdown", btnSize);
                bool btnHov = ImGui::IsItemHovered();
                bool btnClk = ImGui::IsItemClicked();
                ImDrawList* dl2 = ImGui::GetWindowDrawList();
                ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
                if (btnHov) {
                    dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
                                       StateHover(), 4.0f * dp);
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", TR("mining_saved_addresses"));
                }
                ImFont* icoFont = Type().iconSmall();
                const char* dropIcon = ICON_MD_ARROW_DROP_DOWN;
                ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, dropIcon);
                dl2->AddText(icoFont, icoFont->LegacySize,
                             ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
                             OnSurfaceMedium(), dropIcon);
                if (btnClk) {
                    ImGui::OpenPopup("##SavedWorkersPopup");
                }
            }

            // --- Worker: Bookmark button ---
            ImGui::SameLine(0, 0);
            {
                ImVec2 btnPos = ImGui::GetCursorScreenPos();
                ImVec2 btnSize(iconBtnW, inputFrameH2);
                ImGui::InvisibleButton("##SaveWorkerAddr", btnSize);
                bool btnHov = ImGui::IsItemHovered();
                bool btnClk = ImGui::IsItemClicked();
                ImDrawList* dl2 = ImGui::GetWindowDrawList();
                ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
                std::string currentWorker(s_pool_worker);
                bool alreadySaved = miningValueAlreadySaved(app->settings()->getSavedPoolWorkers(), currentWorker);
                if (btnHov) {
                    dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
                                       StateHover(), 4.0f * dp);
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", alreadySaved ? TR("mining_already_saved") : TR("mining_save_payout_address"));
                }
                ImFont* icoFont = Type().iconSmall();
                const char* saveIcon = alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER;
                ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, saveIcon);
                dl2->AddText(icoFont, icoFont->LegacySize,
                             ImVec2(btnCenter.x - icoSz.x * 0.5f, btnCenter.y - icoSz.y * 0.5f),
                             alreadySaved ? Primary() : OnSurfaceMedium(), saveIcon);
                if (btnClk && !currentWorker.empty() && currentWorker != "x" && !alreadySaved) {
                    app->settings()->addSavedPoolWorker(currentWorker);
                    app->settings()->save();
                }
            }

            // --- Worker: Popup positioned below the input group ---
            // Popup sized to fit full z-addresses without truncation;
            // zero horizontal padding so item highlights are flush with edges.
            float addrPopupW = std::max(wrkGroupW, availWidth * 0.55f);
            ImGui::SetNextWindowPos(ImVec2(wrkGroupStartX, wrkGroupStartY + inputFrameH2));
            ImGui::SetNextWindowSize(ImVec2(addrPopupW, 0));
            ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f * dp);
            ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2 * dp));
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
            if (ImGui::BeginPopup("##SavedWorkersPopup")) {
                const auto& savedWorkers = app->settings()->getSavedPoolWorkers();
                if (savedWorkers.empty()) {
                    ImGui::SetCursorPosX(8 * dp);
                    ImGui::PushFont(Type().caption());
                    ImGui::TextDisabled("%s", TR("mining_no_saved_addresses"));
                    ImGui::PopFont();
                    ImGui::SetCursorPosX(8 * dp);
                    ImGui::PushFont(Type().caption());
                    ImGui::TextDisabled("%s", TR("mining_click"));
                    ImGui::PopFont();
                    ImGui::SameLine(0, 2 * dp);
                    ImGui::PushFont(Type().iconSmall());
                    ImGui::TextDisabled(ICON_MD_BOOKMARK_BORDER);
                    ImGui::PopFont();
                    ImGui::SameLine(0, 2 * dp);
                    ImGui::PushFont(Type().caption());
                    ImGui::TextDisabled("%s", TR("mining_to_save"));
                    ImGui::PopFont();
                } else {
                    std::string addrToRemove;
                    float wPopupInnerW = ImGui::GetContentRegionAvail().x;
                    float wXZoneW = ImGui::GetFrameHeight();
                    float wTextPadX = 8 * dp;
                    ImFont* wRowFont = ImGui::GetFont();
                    float wRowFontSz = ImGui::GetFontSize();
                    float wRowH = ImGui::GetFrameHeight();
                    for (const auto& addr : savedWorkers) {
                        ImGui::PushID(addr.c_str());
                        bool isCurrent = (std::string(s_pool_worker) == addr);
                        ImVec2 rowMin = ImGui::GetCursorScreenPos();
                        ImVec2 rowMax(rowMin.x + wPopupInnerW, rowMin.y + wRowH);
                        ImGui::InvisibleButton("##row", ImVec2(wPopupInnerW, wRowH));
                        bool rowHov = ImGui::IsItemHovered();
                        bool rowClk = ImGui::IsItemClicked();
                        ImDrawList* pdl = ImGui::GetWindowDrawList();
                        bool inXZone = rowHov && ImGui::GetMousePos().x >= rowMax.x - wXZoneW;
                        // Row background — flush with popup edges
                        if (isCurrent)
                            pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10));
                        if (rowHov && !inXZone)
                            pdl->AddRectFilled(rowMin, rowMax, StateHover());
                        // Full address text with internal padding
                        float textY = rowMin.y + (wRowH - wRowFontSz) * 0.5f;
                        pdl->AddText(wRowFont, wRowFontSz,
                            ImVec2(rowMin.x + wTextPadX, textY),
                            isCurrent ? Primary() : OnSurface(), addr.c_str());
                        // Tooltip for long addresses
                        if (rowHov && !inXZone)
                            ImGui::SetTooltip("%s", addr.c_str());
                        // X button — flush with right edge, icon centered
                        {
                            ImVec2 xMin(rowMax.x - wXZoneW, rowMin.y);
                            ImVec2 xMax(rowMax.x, rowMax.y);
                            if (inXZone) {
                                pdl->AddRectFilled(xMin, xMax, IM_COL32(255, 80, 80, 30));
                                ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                                ImGui::SetTooltip("%s", TR("mining_remove"));
                            } else if (rowHov) {
                                ImFont* icoF = Type().iconSmall();
                                const char* xIcon = ICON_MD_CLOSE;
                                ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon);
                                ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f);
                                pdl->AddText(icoF, icoF->LegacySize,
                                    ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f),
                                    OnSurfaceDisabled(), xIcon);
                            }
                            if (inXZone) {
                                ImFont* icoF = Type().iconSmall();
                                const char* xIcon = ICON_MD_CLOSE;
                                ImVec2 iSz = icoF->CalcTextSizeA(icoF->LegacySize, FLT_MAX, 0, xIcon);
                                ImVec2 xCenter((xMin.x + xMax.x) * 0.5f, (xMin.y + xMax.y) * 0.5f);
                                pdl->AddText(icoF, icoF->LegacySize,
                                    ImVec2(xCenter.x - iSz.x * 0.5f, xCenter.y - iSz.y * 0.5f),
                                    Error(), xIcon);
                            }
                        }
                        // Click handling
                        if (rowClk) {
                            if (inXZone) {
                                addrToRemove = addr;
                            } else {
                                strncpy(s_pool_worker, addr.c_str(), sizeof(s_pool_worker) - 1);
                                s_pool_worker[sizeof(s_pool_worker) - 1] = '\0';
                                s_pool_settings_dirty = true;
                                ImGui::CloseCurrentPopup();
                            }
                        }
                        if (rowHov && !inXZone)
                            ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::PopID();
                    }
                    if (!addrToRemove.empty()) {
                        app->settings()->removeSavedPoolWorker(addrToRemove);
                        app->settings()->save();
                    }
                }
                ImGui::EndPopup();
            }
            ImGui::PopStyleVar(3);  // WindowRounding, WindowPadding, ItemSpacing for Worker popup

            // === Reset to defaults button ===
            ImGui::SameLine(0, Layout::spacingSm());
            {
                ImVec2 btnPos = ImGui::GetCursorScreenPos();
                ImVec2 btnSize(inputFrameH2, inputFrameH2);
                ImGui::InvisibleButton("##ResetPoolDefaults", btnSize);
                bool btnHov = ImGui::IsItemHovered();
                bool btnClk = ImGui::IsItemClicked();
                
                ImDrawList* dl2 = ImGui::GetWindowDrawList();
                ImVec2 btnCenter(btnPos.x + btnSize.x * 0.5f, btnPos.y + btnSize.y * 0.5f);
                
                // Hover highlight
                if (btnHov) {
                    dl2->AddRectFilled(btnPos, ImVec2(btnPos.x + btnSize.x, btnPos.y + btnSize.y),
                                       StateHover(), 4.0f * dp);
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", TR("mining_reset_defaults"));
                }
                
                // Icon
                ImFont* iconFont = Type().iconSmall();
                const char* resetIcon = ICON_MD_REFRESH;
                ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, resetIcon);
                dl2->AddText(iconFont, iconFont->LegacySize,
                             ImVec2(btnCenter.x - iconSz.x * 0.5f, btnCenter.y - iconSz.y * 0.5f),
                             OnSurfaceMedium(), resetIcon);
                
                if (btnClk) {
                    strncpy(s_pool_url, defaultPoolUrl(), sizeof(s_pool_url) - 1);
                    // Default to user's first shielded (z) address for pool payouts.
                    // Leave blank if no z-address exists yet.
                    std::string defaultAddr = defaultPoolWorkerAddress(state.addresses);
                    strncpy(s_pool_worker, defaultAddr.c_str(), sizeof(s_pool_worker) - 1);
                    s_pool_worker[sizeof(s_pool_worker) - 1] = '\0';
                    s_pool_settings_dirty = true;
                }
            }

            ImGui::PopStyleVar(2);
        }

        // Ensure cursor Y is at toggle bottom regardless of pool input widgets,
        // so the cards below stay at the same position in both solo and pool modes.
        ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y));
        ImGui::Dummy(ImVec2(0, gap * 0.5f));
    }

    // ================================================================
    // CONTROLS — Glass card with CPU core grid (no heading)
    // ================================================================
    {
        // Mining button beside the controls card
        float miningBtnGap = gap;
        float miningBtnMaxW = availWidth * schema::UI().drawElement("tabs.mining", "btn-max-width-ratio").size;

        // --- Compute thread grid layout based on controls card width ---
        // Estimate controlsW first to compute cols correctly
        float estControlsW = availWidth - std::min(schema::UI().drawElement("tabs.mining", "button-max-width-clamp").size, miningBtnMaxW) - miningBtnGap;
        float innerW  = estControlsW - pad * 2;
        float cellSz  = std::clamp(schema::UI().drawElement("tabs.mining", "cell-size").size * vs, schema::UI().drawElement("tabs.mining", "cell-min-size").size, schema::UI().drawElement("tabs.mining", "cell-max-size").sizeOr(42.0f));
        float cellGap = std::max(schema::UI().drawElement("tabs.mining", "cell-gap-min").size, cellSz * schema::UI().drawElement("tabs.mining", "cell-gap-ratio").size);
        int   cols    = std::max(1, std::min(max_threads, (int)(innerW / (cellSz + cellGap))));
        int   rows    = (max_threads + cols - 1) / cols;
        float gridW   = cols * cellSz + (cols - 1) * cellGap;
        float gridH   = rows * cellSz + (rows - 1) * cellGap;

        // Card sections: header(label+info+RAM inline) | grid
        float headerH  = capFont->LegacySize + Layout::spacingXs();
        float secGap   = Layout::spacingLg();

        // Card height from actual content, capped by proportional budget
        float cardH    = pad + headerH + secGap + gridH + pad;
        cardH = std::clamp(cardH, schema::UI().drawElement("tabs.mining", "control-card-min-height").size, controlsBudgetH);

        // Mining button — sized to match card height (square)
        float miningBtnSz = cardH;
        if (miningBtnSz + miningBtnGap > miningBtnMaxW)
            miningBtnSz = miningBtnMaxW;
        float controlsW = availWidth - miningBtnSz - miningBtnGap;

        ImVec2 cardMin = ImGui::GetCursorScreenPos();
        ImVec2 cardMax(cardMin.x + controlsW, cardMin.y + cardH);
        DrawGlassPanel(dl, cardMin, cardMax, glassSpec);

        float curY = cardMin.y + pad;

        // --- Header row: "THREADS  4 / 16" + RAM est + active indicator ---
        {
            ImVec2 labelPos(cardMin.x + pad, curY);
            dl->AddText(ovFont, ovFont->LegacySize, labelPos, OnSurfaceMedium(), TR("mining_threads"));

            float labelW = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, TR("mining_threads")).x;
            snprintf(buf, sizeof(buf), "  %d / %d", s_selected_threads, max_threads);
            ImVec2 countPos(labelPos.x + labelW, curY);
            dl->AddText(sub1, sub1->LegacySize, countPos, OnSurface(), buf);

            // RAM estimate inline (after thread count)
            // Model matches DragonX RandomX: shared ~2080MB dataset + ~256MB cache (allocated once),
            // plus ~2MB scratchpad per mining thread VM.
            {
                float countW = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, buf).x;
                double ram_estimate_mb = schema::UI().drawElement("business", "ram-base-mb").size
                                       + schema::UI().drawElement("business", "ram-dataset-mb").size
                                       + schema::UI().drawElement("business", "ram-cache-mb").size
                                       + s_selected_threads * schema::UI().drawElement("business", "ram-per-thread-mb").size;
                double ram_estimate_gb = ram_estimate_mb / 1024.0;
                snprintf(buf, sizeof(buf), "  RAM ~%.1fGB", ram_estimate_gb);
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(countPos.x + countW, curY + (sub1->LegacySize - capFont->LegacySize) * 0.5f),
                            OnSurfaceDisabled(), buf);
            }

            // Idle mining toggle (top-right corner of card)
            float idleRightEdge = cardMax.x - pad;
            {
                bool idleOn = app->settings()->getMineWhenIdle();
                bool threadScaling = app->settings()->getIdleThreadScaling();
                ImFont* icoFont = Type().iconSmall();
                const char* idleIcon = ICON_MD_SCHEDULE;
                float icoH = icoFont->LegacySize;
                float btnSz = icoH + 8.0f * dp;
                float btnX = idleRightEdge - btnSz;
                float btnY = curY + (headerH - btnSz) * 0.5f;

                // Pill background when active
                if (idleOn) {
                    dl->AddRectFilled(ImVec2(btnX, btnY), ImVec2(btnX + btnSz, btnY + btnSz),
                                      WithAlpha(Primary(), 60), btnSz * 0.5f);
                }

                // Icon centered in button
                ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, idleIcon);
                ImU32 icoCol = idleOn ? Primary() : OnSurfaceDisabled();
                dl->AddText(icoFont, icoFont->LegacySize,
                            ImVec2(btnX + (btnSz - icoSz.x) * 0.5f, btnY + (btnSz - icoSz.y) * 0.5f),
                            icoCol, idleIcon);

                // Click target (save/restore cursor so layout is unaffected)
                ImVec2 savedCur = ImGui::GetCursorScreenPos();
                ImGui::SetCursorScreenPos(ImVec2(btnX, btnY));
                ImGui::InvisibleButton("##IdleMining", ImVec2(btnSz, btnSz));
                if (ImGui::IsItemClicked()) {
                    app->settings()->setMineWhenIdle(!idleOn);
                    app->settings()->save();
                }
                if (ImGui::IsItemHovered()) {
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", idleOn ? TR("mining_idle_on_tooltip") : TR("mining_idle_off_tooltip"));
                }

                idleRightEdge = btnX - 4.0f * dp;

                // Thread scaling mode toggle (to the left of idle icon, shown when idle is on)
                if (idleOn) {
                    const char* scaleIcon = threadScaling ? ICON_MD_TUNE : ICON_MD_POWER_SETTINGS_NEW;
                    float sBtnX = idleRightEdge - btnSz;
                    float sBtnY = btnY;

                    if (threadScaling) {
                        dl->AddRectFilled(ImVec2(sBtnX, sBtnY), ImVec2(sBtnX + btnSz, sBtnY + btnSz),
                                          WithAlpha(Primary(), 40), btnSz * 0.5f);
                    }

                    ImVec2 sIcoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, scaleIcon);
                    ImU32 sIcoCol = threadScaling ? Primary() : OnSurfaceMedium();
                    dl->AddText(icoFont, icoFont->LegacySize,
                                ImVec2(sBtnX + (btnSz - sIcoSz.x) * 0.5f, sBtnY + (btnSz - sIcoSz.y) * 0.5f),
                                sIcoCol, scaleIcon);

                    ImGui::SetCursorScreenPos(ImVec2(sBtnX, sBtnY));
                    ImGui::InvisibleButton("##IdleScaleMode", ImVec2(btnSz, btnSz));
                    if (ImGui::IsItemClicked()) {
                        app->settings()->setIdleThreadScaling(!threadScaling);
                        app->settings()->save();
                    }
                    if (ImGui::IsItemHovered()) {
                        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::SetTooltip("%s", threadScaling
                            ? TR("mining_idle_scale_on_tooltip")
                            : TR("mining_idle_scale_off_tooltip"));
                    }
                    idleRightEdge = sBtnX - 4.0f * dp;
                }

                // GPU-aware idle toggle (to the left, when idle is on)
                // When ON (default): GPU utilization >= 10% counts as "not idle"
                // When OFF: unrestricted mode, only keyboard/mouse input matters
                if (idleOn) {
                    bool gpuAware = app->settings()->getIdleGpuAware();
                    const char* gpuIcon = gpuAware ? ICON_MD_MONITOR : ICON_MD_MONITOR;
                    float gBtnX = idleRightEdge - btnSz;
                    float gBtnY = btnY;

                    if (gpuAware) {
                        dl->AddRectFilled(ImVec2(gBtnX, gBtnY), ImVec2(gBtnX + btnSz, gBtnY + btnSz),
                                          WithAlpha(Primary(), 40), btnSz * 0.5f);
                    }

                    ImVec2 gIcoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, gpuIcon);
                    ImU32 gIcoCol = gpuAware ? Primary() : OnSurfaceDisabled();
                    dl->AddText(icoFont, icoFont->LegacySize,
                                ImVec2(gBtnX + (btnSz - gIcoSz.x) * 0.5f, gBtnY + (btnSz - gIcoSz.y) * 0.5f),
                                gIcoCol, gpuIcon);

                    ImGui::SetCursorScreenPos(ImVec2(gBtnX, gBtnY));
                    ImGui::InvisibleButton("##IdleGpuAware", ImVec2(btnSz, btnSz));
                    if (ImGui::IsItemClicked()) {
                        app->settings()->setIdleGpuAware(!gpuAware);
                        app->settings()->save();
                    }
                    if (ImGui::IsItemHovered()) {
                        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::SetTooltip("%s", gpuAware
                            ? TR("mining_idle_gpu_on_tooltip")
                            : TR("mining_idle_gpu_off_tooltip"));
                    }
                    idleRightEdge = gBtnX - 4.0f * dp;
                }

                // Idle delay combo (to the left, when idle is enabled and NOT in thread scaling mode)
                if (idleOn && !threadScaling) {
                    struct DelayOption { int seconds; const char* label; };
                    static const DelayOption delays[] = {
                        {30, "30s"}, {60, "1m"}, {120, "2m"}, {300, "5m"}, {600, "10m"}
                    };
                    int curDelay = app->settings()->getMineIdleDelay();
                    const char* previewLabel = "2m";
                    for (const auto& d : delays) {
                        if (d.seconds == curDelay) { previewLabel = d.label; break; }
                    }
                    float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
                    float comboX = idleRightEdge - comboW;
                    float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
                    ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
                    ImGui::SetNextItemWidth(comboW);
                    if (ImGui::BeginCombo("##IdleDelay", previewLabel, ImGuiComboFlags_NoArrowButton)) {
                        for (const auto& d : delays) {
                            bool selected = (d.seconds == curDelay);
                            if (ImGui::Selectable(d.label, selected)) {
                                app->settings()->setMineIdleDelay(d.seconds);
                                app->settings()->save();
                            }
                            if (selected) ImGui::SetItemDefaultFocus();
                        }
                        ImGui::EndCombo();
                    }
                    if (ImGui::IsItemHovered())
                        ImGui::SetTooltip("%s", TR("tt_idle_delay"));
                    idleRightEdge = comboX - 4.0f * dp;
                }

                // Thread scaling controls: idle delay + active threads / idle threads combos
                if (idleOn && threadScaling) {
                    int hwThreads = std::max(1, (int)std::thread::hardware_concurrency());

                    // Idle delay combo
                    {
                        struct DelayOption { int seconds; const char* label; };
                        static const DelayOption delays[] = {
                            {30, "30s"}, {60, "1m"}, {120, "2m"}, {300, "5m"}, {600, "10m"}
                        };
                        int curDelay = app->settings()->getMineIdleDelay();
                        const char* previewLabel = "2m";
                        for (const auto& d : delays) {
                            if (d.seconds == curDelay) { previewLabel = d.label; break; }
                        }
                        float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
                        float comboX = idleRightEdge - comboW;
                        float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
                        ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
                        ImGui::SetNextItemWidth(comboW);
                        if (ImGui::BeginCombo("##IdleDelayScale", previewLabel, ImGuiComboFlags_NoArrowButton)) {
                            for (const auto& d : delays) {
                                bool selected = (d.seconds == curDelay);
                                if (ImGui::Selectable(d.label, selected)) {
                                    app->settings()->setMineIdleDelay(d.seconds);
                                    app->settings()->save();
                                }
                                if (selected) ImGui::SetItemDefaultFocus();
                            }
                            ImGui::EndCombo();
                        }
                        if (ImGui::IsItemHovered())
                            ImGui::SetTooltip("%s", TR("tt_idle_delay"));
                        idleRightEdge = comboX - 4.0f * dp;
                    }

                    // Idle threads combo (threads when system is idle)
                    {
                        int curVal = app->settings()->getIdleThreadsIdle();
                        if (curVal <= 0) curVal = hwThreads;
                        char previewBuf[16];
                        snprintf(previewBuf, sizeof(previewBuf), "%d", curVal);
                        float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
                        float comboX = idleRightEdge - comboW;
                        float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
                        ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
                        ImGui::SetNextItemWidth(comboW);
                        if (ImGui::BeginCombo("##IdleThreadsIdle", previewBuf, ImGuiComboFlags_NoArrowButton)) {
                            for (int t = 1; t <= hwThreads; t++) {
                                char lbl[16];
                                snprintf(lbl, sizeof(lbl), "%d", t);
                                bool selected = (t == curVal);
                                if (ImGui::Selectable(lbl, selected)) {
                                    app->settings()->setIdleThreadsIdle(t);
                                    app->settings()->save();
                                }
                                if (selected) ImGui::SetItemDefaultFocus();
                            }
                            ImGui::EndCombo();
                        }
                        if (ImGui::IsItemHovered())
                            ImGui::SetTooltip("%s", TR("mining_idle_threads_idle_tooltip"));
                        idleRightEdge = comboX - 4.0f * dp;
                    }

                    // Separator arrow icon
                    {
                        const char* arrowIcon = ICON_MD_ARROW_BACK;
                        ImVec2 arrSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, arrowIcon);
                        float arrX = idleRightEdge - arrSz.x;
                        float arrY = curY + (headerH - arrSz.y) * 0.5f;
                        dl->AddText(icoFont, icoFont->LegacySize, ImVec2(arrX, arrY), OnSurfaceDisabled(), arrowIcon);
                        idleRightEdge = arrX - 4.0f * dp;
                    }

                    // Active threads combo (threads when user is active)
                    {
                        int curVal = app->settings()->getIdleThreadsActive();
                        if (curVal <= 0) curVal = std::max(1, hwThreads / 2);
                        char previewBuf[16];
                        snprintf(previewBuf, sizeof(previewBuf), "%d", curVal);
                        float comboW = schema::UI().drawElement("components.settings-page", "idle-combo-width").sizeOr(64.0f);
                        float comboX = idleRightEdge - comboW;
                        float comboY = curY + (headerH - ImGui::GetFrameHeight()) * 0.5f;
                        ImGui::SetCursorScreenPos(ImVec2(comboX, comboY));
                        ImGui::SetNextItemWidth(comboW);
                        if (ImGui::BeginCombo("##IdleThreadsActive", previewBuf, ImGuiComboFlags_NoArrowButton)) {
                            for (int t = 1; t <= hwThreads; t++) {
                                char lbl[16];
                                snprintf(lbl, sizeof(lbl), "%d", t);
                                bool selected = (t == curVal);
                                if (ImGui::Selectable(lbl, selected)) {
                                    app->settings()->setIdleThreadsActive(t);
                                    app->settings()->save();
                                }
                                if (selected) ImGui::SetItemDefaultFocus();
                            }
                            ImGui::EndCombo();
                        }
                        if (ImGui::IsItemHovered())
                            ImGui::SetTooltip("%s", TR("mining_idle_threads_active_tooltip"));
                        idleRightEdge = comboX - 4.0f * dp;
                    }
                }

                ImGui::SetCursorScreenPos(savedCur);
            }

            // --- Thread Benchmark button / progress (left of idle toggle) ---
            {
                ImVec2 benchSavedCur = ImGui::GetCursorScreenPos();
                bool benchRunning = s_benchmark.phase != ThreadBenchmark::Phase::Idle &&
                                    s_benchmark.phase != ThreadBenchmark::Phase::Done;
                bool benchDone    = s_benchmark.phase == ThreadBenchmark::Phase::Done;
                ImFont* icoFont = Type().iconSmall();

                if (benchRunning) {
                    // Show progress bar + current test info
                    float barW = std::min(180.0f * hs, idleRightEdge - (cardMin.x + pad) - 10.0f * dp);
                    float barH = 4.0f * dp;
                    float barX = idleRightEdge - barW;
                    float barY = curY + headerH - barH - 2.0f * dp;

                    // Progress bar track
                    dl->AddRectFilled(ImVec2(barX, barY), ImVec2(barX + barW, barY + barH),
                                      WithAlpha(OnSurface(), 30), barH * 0.5f);
                    // Progress bar fill
                    float pct = s_benchmark.progress();
                    dl->AddRectFilled(ImVec2(barX, barY), ImVec2(barX + barW * pct, barY + barH),
                                      Primary(), barH * 0.5f);

                    // Status text above bar
                    int ct = s_benchmark.current_index < (int)s_benchmark.candidates.size()
                           ? s_benchmark.candidates[s_benchmark.current_index] : 0;
                    // Estimated remaining time (uses observed warmup for better accuracy)
                    int remaining_tests = (int)s_benchmark.candidates.size() - s_benchmark.current_index;
                    float elapsed_in_phase = s_benchmark.phase_timer;
                    float phase_total;
                    if (s_benchmark.phase == ThreadBenchmark::Phase::WarmingUp)
                        phase_total = s_benchmark.avgWarmupSecs();  // adaptive estimate
                    else if (s_benchmark.phase == ThreadBenchmark::Phase::CoolingDown)
                        phase_total = ThreadBenchmark::COOLDOWN_SECS;
                    else
                        phase_total = ThreadBenchmark::MEASURE_SECS;
                    float remaining_in_current = std::max(0.0f, phase_total - elapsed_in_phase);
                    // Remaining tests after current each need warmup + measure + cooldown
                    float est_secs = remaining_in_current
                                   + (remaining_tests - 1) * (s_benchmark.avgWarmupSecs() + ThreadBenchmark::MEASURE_SECS + ThreadBenchmark::COOLDOWN_SECS);
                    int est_min = (int)(est_secs / 60.0f);
                    int est_sec = (int)est_secs % 60;
                    const char* phase_label;
                    if (s_benchmark.phase == ThreadBenchmark::Phase::CoolingDown)
                        phase_label = TR("mining_benchmark_cooling");
                    else if (s_benchmark.phase == ThreadBenchmark::Phase::WarmingUp
                             && s_benchmark.phase_timer >= ThreadBenchmark::MIN_WARMUP_SECS)
                        phase_label = TR("mining_benchmark_stabilizing");
                    else
                        phase_label = TR("mining_benchmark_testing");
                    if (est_min > 0)
                        snprintf(buf, sizeof(buf), "%s %d/%d (%dt) ~%dm%ds",
                                 phase_label,
                                 s_benchmark.current_index + 1,
                                 (int)s_benchmark.candidates.size(), ct, est_min, est_sec);
                    else
                        snprintf(buf, sizeof(buf), "%s %d/%d (%dt) ~%ds",
                                 phase_label,
                                 s_benchmark.current_index + 1,
                                 (int)s_benchmark.candidates.size(), ct, est_sec);
                    ImVec2 txtSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
                    dl->AddText(capFont, capFont->LegacySize,
                                ImVec2(barX + (barW - txtSz.x) * 0.5f, barY - txtSz.y - 2.0f * dp),
                                OnSurfaceMedium(), buf);

                    // Cancel button (small X)
                    float cancelSz = icoFont->LegacySize + 4.0f * dp;
                    float cancelX  = barX - cancelSz - 4.0f * dp;
                    float cancelY  = curY + (headerH - cancelSz) * 0.5f;
                    ImGui::SetCursorScreenPos(ImVec2(cancelX, cancelY));
                    ImGui::InvisibleButton("##BenchCancel", ImVec2(cancelSz, cancelSz));
                    if (ImGui::IsItemClicked()) {
                        app->stopPoolMining();
                        if (s_benchmark.was_pool_running)
                            app->startPoolMining(s_benchmark.prev_threads);
                        s_benchmark.reset();
                    }
                    if (ImGui::IsItemHovered()) {
                        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::SetTooltip("%s", TR("mining_benchmark_cancel"));
                    }
                    const char* cancelIcon = ICON_MD_CLOSE;
                    ImVec2 cIcoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, cancelIcon);
                    dl->AddText(icoFont, icoFont->LegacySize,
                                ImVec2(cancelX + (cancelSz - cIcoSz.x) * 0.5f,
                                       cancelY + (cancelSz - cIcoSz.y) * 0.5f),
                                OnSurfaceMedium(), cancelIcon);

                    idleRightEdge = cancelX - 4.0f * dp;

                } else if (benchDone && s_benchmark.optimal_threads > 0) {
                    // Show result briefly, then reset on next click
                    snprintf(buf, sizeof(buf), "%s: %dt (%.1f H/s)",
                             TR("mining_benchmark_result"),
                             s_benchmark.optimal_threads, s_benchmark.optimal_hashrate);
                    ImVec2 txtSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
                    float txtX = idleRightEdge - txtSz.x;
                    dl->AddText(capFont, capFont->LegacySize,
                                ImVec2(txtX, curY + (headerH - txtSz.y) * 0.5f),
                                WithAlpha(Success(), 220), buf);

                    // Dismiss button
                    float dismissSz = icoFont->LegacySize + 4.0f * dp;
                    float dismissX  = txtX - dismissSz - 4.0f * dp;
                    float dismissY  = curY + (headerH - dismissSz) * 0.5f;
                    ImGui::SetCursorScreenPos(ImVec2(dismissX, dismissY));
                    ImGui::InvisibleButton("##BenchDismiss", ImVec2(dismissSz, dismissSz));
                    if (ImGui::IsItemClicked())
                        s_benchmark.reset();
                    if (ImGui::IsItemHovered()) {
                        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::SetTooltip("%s", TR("mining_benchmark_dismiss"));
                    }
                    const char* okIcon = ICON_MD_CHECK;
                    ImVec2 oIcoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, okIcon);
                    dl->AddText(icoFont, icoFont->LegacySize,
                                ImVec2(dismissX + (dismissSz - oIcoSz.x) * 0.5f,
                                       dismissY + (dismissSz - oIcoSz.y) * 0.5f),
                                WithAlpha(Success(), 200), okIcon);

                    idleRightEdge = dismissX - 4.0f * dp;

                } else if (s_pool_mode) {
                    // Show benchmark button (only in pool mode)
                    float btnSz = icoFont->LegacySize + 8.0f * dp;
                    float btnX  = idleRightEdge - btnSz;
                    float btnY  = curY + (headerH - btnSz) * 0.5f;

                    ImGui::SetCursorScreenPos(ImVec2(btnX, btnY));
                    ImGui::InvisibleButton("##BenchStart", ImVec2(btnSz, btnSz));
                    bool benchHovered = ImGui::IsItemHovered();
                    bool benchClicked = ImGui::IsItemClicked();

                    // Hover highlight
                    if (benchHovered) {
                        dl->AddRectFilled(ImVec2(btnX, btnY), ImVec2(btnX + btnSz, btnY + btnSz),
                                          StateHover(), btnSz * 0.5f);
                        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                        ImGui::SetTooltip("%s", TR("mining_benchmark_tooltip"));
                    }

                    const char* benchIcon = ICON_MD_SPEED;
                    ImVec2 bIcoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, benchIcon);
                    dl->AddText(icoFont, icoFont->LegacySize,
                                ImVec2(btnX + (btnSz - bIcoSz.x) * 0.5f,
                                       btnY + (btnSz - bIcoSz.y) * 0.5f),
                                OnSurfaceMedium(), benchIcon);

                    if (benchClicked) {
                        // Require a wallet address for pool mining
                        std::string worker(s_pool_worker);
                        if (!worker.empty()) {
                            s_benchmark.reset();
                            s_benchmark.was_pool_running = state.pool_mining.xmrig_running;
                            s_benchmark.prev_threads = s_selected_threads;
                            s_benchmark.buildCandidates(max_threads);
                            s_benchmark.phase = ThreadBenchmark::Phase::Starting;
                            // Stop any active solo mining first
                            if (mining.generate)
                                app->stopMining();
                        }
                    }

                    idleRightEdge = btnX - 4.0f * dp;
                }

                ImGui::SetCursorScreenPos(benchSavedCur);
            }

            // Active mining indicator (left of idle toggle)
            if (mining.generate) {
                float pulse = effects::isLowSpecMode()
                    ? schema::UI().drawElement("animations", "pulse-base-normal").size
                    : schema::UI().drawElement("animations", "pulse-base-normal").size + schema::UI().drawElement("animations", "pulse-amp-normal").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-normal").size);
                ImU32 pulseCol = WithAlpha(Success(), (int)(255 * pulse));
                float dotR = schema::UI().drawElement("tabs.mining", "active-dot-radius").size + 2.0f * hs;
                dl->AddCircleFilled(ImVec2(idleRightEdge - dotR, curY + dotR + 1 * dp), dotR, pulseCol);
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(idleRightEdge - dotR - dotR - 60 * hs, curY),
                            WithAlpha(Success(), 200), TR("mining_active"));
            }
            curY += headerH + secGap;
        }

        // --- Thread Grid (drag-to-select) ---
        {
            // Center the grid horizontally within the card
            float gridX = cardMin.x + pad + (innerW - gridW) * 0.5f;
            float gridY = curY;

            bool threads_changed = false;

            // Track which thread the mouse is currently over (-1 = none)
            int hovered_thread = -1;

            // First pass: hit-test all cells to find hovered thread
            for (int i = 0; i < max_threads; i++) {
                int row = i / cols;
                int col = i % cols;
                float cx = gridX + col * (cellSz + cellGap);
                float cy = gridY + row * (cellSz + cellGap);
                if (material::IsRectHovered(ImVec2(cx, cy), ImVec2(cx + cellSz, cy + cellSz))) {
                    hovered_thread = i + 1;
                    break;
                }
            }

            // Show pointer cursor when hovering the thread grid
            bool benchActive = s_benchmark.phase != ThreadBenchmark::Phase::Idle &&
                               s_benchmark.phase != ThreadBenchmark::Phase::Done;
            if (hovered_thread > 0 && !benchActive)
                ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);

            // Drag-to-select logic (disabled during benchmark)
            if (!benchActive && ImGui::IsMouseClicked(0) && hovered_thread > 0) {
                // Begin drag
                s_drag_active = true;
                s_drag_anchor_thread = hovered_thread;
                s_selected_threads = hovered_thread;
                threads_changed = true;
            }
            if (s_drag_active) {
                if (ImGui::IsMouseDown(0)) {
                    if (hovered_thread > 0 && hovered_thread != s_selected_threads) {
                        // Drag selects up to the hovered thread in either direction
                        s_selected_threads = hovered_thread;
                        threads_changed = true;
                    }
                } else {
                    // Mouse released — end drag
                    s_drag_active = false;
                }
            }

            // Render cells
            for (int i = 0; i < max_threads; i++) {
                int row = i / cols;
                int col = i % cols;
                float cx = gridX + col * (cellSz + cellGap);
                float cy = gridY + row * (cellSz + cellGap);
                ImVec2 cMin(cx, cy);
                ImVec2 cMax(cx + cellSz, cy + cellSz);

                int threadNum = i + 1;
                bool active   = threadNum <= s_selected_threads;
                bool hovered  = (threadNum == hovered_thread);

                // Determine visual state
                float rounding = schema::UI().drawElement("tabs.mining", "cell-rounding").size;
                if (active && mining.generate) {
                    // Mining + active: animated heat glow using theme colors
                    float t       = (float)ImGui::GetTime();
                    float phase   = t * schema::UI().drawElement("animations", "heat-glow-speed").size + i * schema::UI().drawElement("animations", "heat-glow-phase-offset").size;
                    float glow    = effects::isLowSpecMode() ? 0.5f : 0.5f + 0.5f * (float)std::sin(phase);

                    // Base color from theme (--mining-heat-glow) falling back to Primary
                    ImU32 heatBase = schema::UI().resolveColor("var(--mining-heat-glow)", Primary());
                    int baseR = (heatBase >> 0)  & 0xFF;
                    int baseG = (heatBase >> 8)  & 0xFF;
                    int baseB = (heatBase >> 16) & 0xFF;
                    // Brighten toward white as glow increases
                    int   r   = std::min(255, (int)(baseR + (255 - baseR) * 0.3f * glow));
                    int   g   = std::min(255, (int)(baseG + (255 - baseG) * 0.3f * glow));
                    int   b   = std::min(255, (int)(baseB + (255 - baseB) * 0.3f * glow));
                    int   a   = (int)(180 + 60 * glow);
                    ImU32 fillCol = IM_COL32(r, g, b, a);
                    dl->AddRectFilled(cMin, cMax, fillCol, rounding);
                    // Bright border
                    dl->AddRect(cMin, cMax, WithAlpha(Primary(), (int)(160 + 60 * glow)), rounding, 0, schema::UI().drawElement("tabs.mining", "active-cell-border-thickness").size);
                } else if (active) {
                    // Active but not mining: solid primary fill
                    ImU32 pri = Primary();
                    int priR = (pri >> 0)  & 0xFF;
                    int priG = (pri >> 8)  & 0xFF;
                    int priB = (pri >> 16) & 0xFF;
                    ImU32 fillCol = hovered
                        ? IM_COL32(priR, priG, priB, 220)
                        : IM_COL32(priR, priG, priB, 180);
                    dl->AddRectFilled(cMin, cMax, fillCol, rounding);
                    dl->AddRect(cMin, cMax, IM_COL32(priR, priG, priB, 255), rounding, 0, schema::UI().drawElement("tabs.mining", "cell-border-thickness").size);
                } else {
                    // Inactive: dim outline
                    ImU32 fillCol = hovered
                        ? WithAlpha(OnSurface(), 25)
                        : WithAlpha(OnSurface(), 8);
                    dl->AddRectFilled(cMin, cMax, fillCol, rounding);
                    dl->AddRect(cMin, cMax, WithAlpha(OnSurface(), hovered ? 80 : 35), rounding);
                }

                // Thread number label (centered)
                snprintf(buf, sizeof(buf), "%d", threadNum);
                ImVec2 txtSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
                ImVec2 txtPos(cx + (cellSz - txtSz.x) * 0.5f, cy + (cellSz - txtSz.y) * 0.5f);
                ImU32  txtCol = active ? schema::UI().resolveColor("var(--on-primary)", IM_COL32(255, 255, 255, 230)) : WithAlpha(OnSurface(), 80);
                dl->AddText(capFont, capFont->LegacySize, txtPos, txtCol, buf);
            }

            if (threads_changed) {
                app->settings()->setPoolThreads(s_selected_threads);
                app->settings()->save();
                if (mining.generate) {
                    app->startMining(s_selected_threads);
                }
                if (s_pool_mode && state.pool_mining.xmrig_running) {
                    app->stopPoolMining();
                    app->startPoolMining(s_selected_threads);
                }
            }

            curY += gridH + secGap;
        }

        // ============================================================
        // Large square mining button — right of the controls card
        // ============================================================
        {
            float btnX = cardMin.x + controlsW + miningBtnGap;
            float btnY = cardMin.y;
            ImVec2 bMin(btnX, btnY);
            ImVec2 bMax(btnX + miningBtnSz, btnY + cardH);

            bool btnHovered = material::IsRectHovered(bMin, bMax);
            bool btnClicked = btnHovered && ImGui::IsMouseClicked(0);
            bool isSyncing  = state.sync.syncing;
            bool poolBlockedBySolo = s_pool_mode && mining.generate && !state.pool_mining.xmrig_running;
            bool isToggling = app->isMiningToggleInProgress();
            // Pool mining connects to an external pool via xmrig — it does not
            // need the local blockchain synced or even the daemon connected.
            // If pool mining is still shutting down after switching to solo,
            // keep the button enabled so user can stop it.
            bool poolStillRunning = !s_pool_mode && state.pool_mining.xmrig_running;
            bool disabled = s_pool_mode
                ? (isToggling || poolBlockedBySolo)
                : (poolStillRunning ? false : (!app->isConnected() || isToggling || isSyncing));

            // Glass panel background with state-dependent tint
            GlassPanelSpec btnGlass;
            btnGlass.rounding = Layout::glassRounding();
            if (isToggling) {
                // Toggling: subtle pulsing glow to indicate activity
                float pulse = effects::isLowSpecMode()
                    ? 0.5f
                    : 0.5f + 0.5f * (float)std::sin((double)ImGui::GetTime() * 4.0);
                int glowA = (int)(15 + 25 * pulse);
                btnGlass.fillAlpha = glowA;
            } else if (isMiningActive) {
                // Active mining: warm glow
                float pulse = effects::isLowSpecMode()
                    ? schema::UI().drawElement("animations", "pulse-base-glow").size
                    : schema::UI().drawElement("animations", "pulse-base-glow").size + schema::UI().drawElement("animations", "pulse-amp-glow").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-slow").size);
                int glowA = (int)(20 + 30 * pulse);
                btnGlass.fillAlpha = glowA;
            } else {
                btnGlass.fillAlpha = btnHovered ? 30 : 18;
            }
            DrawGlassPanel(dl, bMin, bMax, btnGlass);

            // Hover highlight
            if (btnHovered && !disabled) {
                dl->AddRectFilled(bMin, bMax,
                    isMiningActive ? WithAlpha(Error(), 25) : WithAlpha(Success(), 20),
                    btnGlass.rounding);
            }

            // Draw mining icon centered in button — Material Design ICON_MD_CONSTRUCTION
            {
                float btnW = bMax.x - bMin.x;
                float btnH = bMax.y - bMin.y;
                float cx = bMin.x + btnW * 0.5f;
                float cy = bMin.y + btnH * schema::UI().drawElement("tabs.mining", "button-icon-y-ratio").size;  // shift up to leave room for label

                if (isToggling) {
                    // Draw a spinning arc spinner to indicate progress
                    float spinnerR = std::min(btnW, btnH) * 0.18f;
                    float thickness = std::max(2.5f, spinnerR * 0.15f);
                    float time = (float)ImGui::GetTime();

                    // Track circle (faint)
                    ImU32 trackCol = WithAlpha(Primary(), 40);
                    dl->AddCircle(ImVec2(cx, cy), spinnerR, trackCol, 0, thickness);

                    // Animated arc
                    float rotation = fmodf(time * 2.0f * IM_PI / 1.4f, IM_PI * 2.0f);
                    float cycleTime = fmodf(time, 1.333f);
                    float arcLength = (cycleTime < 0.666f)
                        ? (cycleTime / 0.666f) * 0.75f + 0.1f
                        : ((1.333f - cycleTime) / 0.666f) * 0.75f + 0.1f;

                    float startAngle = rotation - IM_PI * 0.5f;
                    float endAngle = startAngle + IM_PI * 2.0f * arcLength;
                    int segments = (int)(32 * arcLength) + 1;
                    float angleStep = (endAngle - startAngle) / segments;
                    ImU32 arcCol = Primary();

                    for (int i = 0; i < segments; i++) {
                        float a1 = startAngle + angleStep * i;
                        float a2 = startAngle + angleStep * (i + 1);
                        ImVec2 p1(cx + cosf(a1) * spinnerR, cy + sinf(a1) * spinnerR);
                        ImVec2 p2(cx + cosf(a2) * spinnerR, cy + sinf(a2) * spinnerR);
                        dl->AddLine(p1, p2, arcCol, thickness);
                    }
                } else {
                    ImU32 iconCol;
                    if (disabled) {
                        iconCol = OnSurfaceDisabled();
                    } else if (isMiningActive) {
                        float pulse = effects::isLowSpecMode()
                            ? schema::UI().drawElement("animations", "pulse-base-normal").size
                            : schema::UI().drawElement("animations", "pulse-base-normal").size + schema::UI().drawElement("animations", "pulse-amp-normal").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-normal").size);
                        iconCol = WithAlpha(Error(), (int)(200 + 55 * pulse));
                    } else {
                        iconCol = btnHovered ? WithAlpha(Success(), 255) : OnSurfaceMedium();
                    }

                    // Use XL icon for the large mining button
                    ImFont* iconFont = Type().iconXL();
                    const char* mineIcon = isMiningActive ? ICON_MD_CLOSE : ICON_MD_CONSTRUCTION;
                    ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, mineIcon);
                    dl->AddText(iconFont, iconFont->LegacySize,
                                ImVec2(cx - iconSz.x * 0.5f, cy - iconSz.y * 0.5f), iconCol, mineIcon);
                }
            }

            // Label below icon
            {
                float btnW = bMax.x - bMin.x;
                float btnH = bMax.y - bMin.y;
                const char* label;
                ImU32 lblCol;
                if (isToggling) {
                    label = isMiningActive ? TR("mining_stopping") : TR("mining_starting");
                    // Animated dots effect via alpha pulse
                    float pulse = effects::isLowSpecMode()
                        ? 0.7f
                        : 0.5f + 0.5f * (float)std::sin((double)ImGui::GetTime() * 3.0);
                    lblCol = WithAlpha(Primary(), (int)(120 + 135 * pulse));
                } else if (isMiningActive) {
                    label = TR("mining_stop");
                    lblCol = WithAlpha(Error(), 220);
                } else if (disabled) {
                    label = TR("mining_mine");
                    lblCol = WithAlpha(OnSurface(), 50);
                } else {
                    label = TR("mining_mine");
                    lblCol = WithAlpha(OnSurface(), 160);
                }
                ImVec2 lblSz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, label);
                float lblX = bMin.x + (btnW - lblSz.x) * 0.5f;
                float lblY = bMin.y + btnH * schema::UI().drawElement("tabs.mining", "button-label-y-ratio").size;
                dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lblX, lblY), lblCol, label);
            }

            // Tooltip + pointer cursor
            if (btnHovered) {
                if (!disabled)
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                if (isToggling)
                    ImGui::SetTooltip("%s", isMiningActive ? TR("mining_stopping_tooltip") : TR("mining_starting_tooltip"));
                else if (isSyncing && !s_pool_mode)
                    ImGui::SetTooltip(TR("mining_syncing_tooltip"), state.sync.verification_progress * 100.0);
                else if (poolBlockedBySolo)
                    ImGui::SetTooltip("%s", TR("mining_stop_solo_for_pool"));
                else
                    ImGui::SetTooltip("%s", isMiningActive ? TR("stop_mining") : TR("start_mining"));
            }

            // Click action — pool or solo
            if (btnClicked && !disabled) {
                if (s_pool_mode) {
                    if (state.pool_mining.xmrig_running)
                        app->stopPoolMining();
                    else
                        app->startPoolMining(s_selected_threads);
                } else {
                    // If pool mining is still running (user just switched from pool to solo),
                    // stop pool mining first
                    if (state.pool_mining.xmrig_running)
                        app->stopPoolMining();
                    else if (mining.generate)
                        app->stopMining();
                    else
                        app->startMining(s_selected_threads);
                }
            }
        }

        ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
        ImGui::Dummy(ImVec2(availWidth, 0));
        ImGui::Dummy(ImVec2(0, gap));
    }

    // ================================================================
    // HASHRATE + STATS — Combined glass card: stat values on top, chart below
    // (Or full-card log view when toggled in pool mode)
    // ================================================================
    {
        ImU32 greenCol = Success();

        // Determine view mode first
        bool showLogView = s_pool_mode
            ? (s_show_pool_log && !state.pool_mining.log_lines.empty())
            : (s_show_solo_log && !mining.log_lines.empty());
        bool hasLogContent = s_pool_mode
            ? !state.pool_mining.log_lines.empty()
            : !mining.log_lines.empty();
        // Use pool hashrate history when in pool mode, solo otherwise
        const std::vector<double>& chartHistory = s_pool_mode
            ? state.pool_mining.hashrate_history
            : mining.hashrate_history;
        bool hasChartContent = chartHistory.size() >= 2;

        // Stat row height (single line: overline + value)
        float statRowH = ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize + Layout::spacingSm();
        float totalCardH = statRowH + chartBudgetH + pad;

        ImVec2 cardMin = ImGui::GetCursorScreenPos();
        ImVec2 cardMax(cardMin.x + availWidth, cardMin.y + totalCardH);
        DrawGlassPanel(dl, cardMin, cardMax, glassSpec);

        bool& showLogFlag = s_pool_mode ? s_show_pool_log : s_show_solo_log;

        if (showLogView) {
            // --- Full-card log view (selectable + copyable) ---
            const std::vector<std::string>& logLines = s_pool_mode
                ? state.pool_mining.log_lines
                : mining.log_lines;

            // Build a single string buffer for InputTextMultiline
            static std::string s_log_buf;
            s_log_buf.clear();
            for (const auto& line : logLines) {
                if (!line.empty()) {
                    s_log_buf += line;
                    s_log_buf += '\n';
                }
            }

            float logPad = pad * 0.5f;
            float logW = availWidth - logPad * 2;
            float logH = totalCardH - logPad * 2;
            ImGui::SetCursorScreenPos(ImVec2(cardMin.x + logPad, cardMin.y + logPad));

            ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(OnSurface()));
            ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
            ImFont* monoFont = Type().body2();
            ImGui::PushFont(monoFont);

            const char* inputId = s_pool_mode ? "##PoolLogText" : "##SoloLogText";
            ImGui::InputTextMultiline(inputId,
                const_cast<char*>(s_log_buf.c_str()), s_log_buf.size() + 1,
                ImVec2(logW, logH),
                ImGuiInputTextFlags_ReadOnly);

            ImGui::PopFont();
            ImGui::PopStyleColor(2);

            // Reset cursor to end of card after the input
            ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
            ImGui::Dummy(ImVec2(availWidth, 0));  // Register position with layout system
        } else {
            // --- Stats + Chart view ---
            
            // Pool vs Solo stats — different columns
            std::string col1Str, col2Str, col3Str, col4Str;
            const char* col1Label;
            const char* col2Label;
            const char* col3Label;
            const char* col4Label = nullptr;
            ImU32 col1Col, col2Col, col3Col, col4Col = OnSurface();
            int numStats = 3;

            if (s_pool_mode) {
                col1Label = TR("mining_local_hashrate");
                col1Str   = FormatHashrate(state.pool_mining.hashrate_10s);
                col1Col   = state.pool_mining.xmrig_running ? greenCol : OnSurfaceDisabled();

                col2Label = TR("mining_pool_hashrate");
                col2Str   = FormatHashrate(state.pool_mining.pool_hashrate);
                col2Col   = state.pool_mining.pool_hashrate > 0 ? OnSurface() : OnSurfaceDisabled();

                col3Label = TR("mining_shares");
                char sharesBuf[64];
                snprintf(sharesBuf, sizeof(sharesBuf), "%lld / %lld",
                         (long long)state.pool_mining.accepted,
                         (long long)state.pool_mining.rejected);
                col3Str = sharesBuf;
                col3Col = OnSurface();

                col4Label = TR("mining_uptime");
                int64_t up = state.pool_mining.uptime_sec;
                char uptBuf[64];
                if (up <= 0)
                    snprintf(uptBuf, sizeof(uptBuf), "N/A");
                else if (up < 3600)
                    snprintf(uptBuf, sizeof(uptBuf), "%lldm %llds", (long long)(up / 60), (long long)(up % 60));
                else
                    snprintf(uptBuf, sizeof(uptBuf), "%lldh %lldm", (long long)(up / 3600), (long long)((up % 3600) / 60));
                col4Str = uptBuf;
                col4Col = OnSurface();
                numStats = 4;
            } else {
                double est_hours = EstimateHoursToBlock(mining.localHashrate, mining.networkHashrate, mining.difficulty);

                col1Label = TR("mining_local_hashrate");
                col1Str   = FormatHashrate(mining.localHashrate);
                col1Col   = mining.generate ? greenCol : OnSurfaceDisabled();

                col2Label = TR("mining_network");
                col2Str   = FormatHashrate(mining.networkHashrate);
                col2Col   = OnSurface();

                col3Label = TR("mining_est_block");
                col3Str   = FormatEstTime(est_hours);
                col3Col   = OnSurface();
            }

            // Draw stat values as inline columns at top of card
            {
                float statColW = (availWidth - pad * 2) / (float)numStats;
                float sy = cardMin.y + pad * 0.5f;

                struct StatEntry { const char* label; const char* value; ImU32 col; };
                char c1Buf[64], c2Buf[64], c3Buf[64], c4Buf[64];
                snprintf(c1Buf, sizeof(c1Buf), "%s", col1Str.c_str());
                snprintf(c2Buf, sizeof(c2Buf), "%s", col2Str.c_str());
                snprintf(c3Buf, sizeof(c3Buf), "%s", col3Str.c_str());
                if (numStats > 3) snprintf(c4Buf, sizeof(c4Buf), "%s", col4Str.c_str());
                StatEntry stats[] = {
                    { col1Label, c1Buf, col1Col },
                    { col2Label, c2Buf, col2Col },
                    { col3Label, c3Buf, col3Col },
                    { col4Label ? col4Label : "", c4Buf, col4Col },
                };

                for (int si = 0; si < numStats; si++) {
                    float sx = cardMin.x + pad + si * statColW;
                    float centerX = sx + statColW * 0.5f;

                    ImVec2 lblSz = ovFont->CalcTextSizeA(ovFont->LegacySize, 10000, 0, stats[si].label);
                    dl->AddText(ovFont, ovFont->LegacySize,
                                ImVec2(centerX - lblSz.x * 0.5f, sy), OnSurfaceMedium(), stats[si].label);

                    ImVec2 valSz = sub1->CalcTextSizeA(sub1->LegacySize, 10000, 0, stats[si].value);
                    float valY = sy + ovFont->LegacySize + Layout::spacingXs();
                    dl->AddText(sub1, sub1->LegacySize,
                                ImVec2(centerX - valSz.x * 0.5f, valY), stats[si].col, stats[si].value);

                    // Trend arrow for hashrate (first column only)
                    if (si == 0 && chartHistory.size() >= 6) {
                        size_t hn = chartHistory.size();
                        double recent = (chartHistory[hn-1] + chartHistory[hn-2] + chartHistory[hn-3]) / 3.0;
                        double older  = (chartHistory[hn-4] + chartHistory[hn-5] + chartHistory[hn-6]) / 3.0;
                        ImFont* iconFont = Type().iconSmall();
                        float arrowX = centerX + valSz.x * 0.5f + Layout::spacingSm();
                        if (recent > older * 1.02) {
                            const char* icon = ICON_MD_TRENDING_UP;
                            ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, icon);
                            dl->AddText(iconFont, iconFont->LegacySize,
                                        ImVec2(arrowX, valY + sub1->LegacySize * 0.5f - iSz.y * 0.5f),
                                        WithAlpha(Success(), 220), icon);
                        } else if (recent < older * 0.98) {
                            const char* icon = ICON_MD_TRENDING_DOWN;
                            ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, icon);
                            dl->AddText(iconFont, iconFont->LegacySize,
                                        ImVec2(arrowX, valY + sub1->LegacySize * 0.5f - iSz.y * 0.5f),
                                        WithAlpha(Error(), 220), icon);
                        }
                    }
                }
            }

            // Sparkline chart below stats
            if (hasChartContent) {
                float chartTop = cardMin.y + statRowH;
                float chartBot = cardMax.y;

                // Compute Y range
                double yMin = *std::min_element(chartHistory.begin(), chartHistory.end());
                double yMax = *std::max_element(chartHistory.begin(), chartHistory.end());
                if (yMax <= yMin) { yMax = yMin + 1.0; }
                double yRange = yMax - yMin;
                double yPad2 = yRange * 0.1;
                yMin -= yPad2;
                yMax += yPad2;

                float plotLeft   = cardMin.x + pad;
                float plotRight  = cardMax.x - pad;
                float plotTop    = chartTop + capFont->LegacySize + 4 * dp;
                float plotBottom = chartBot - capFont->LegacySize * 2 - 16 * dp;
                float plotW = plotRight - plotLeft;
                float plotH = std::max(1.0f, plotBottom - plotTop);

                // Build raw data points — evenly spaced across the plot.
                // No smooth-scroll animation: the chart updates in-place
                // when new data arrives without any interim compression.
                size_t n = chartHistory.size();
                float stepW = (n > 1) ? plotW / (float)(n - 1) : plotW;

                std::vector<ImVec2> rawPts(n);
                for (size_t i = 0; i < n; i++) {
                    float x = plotLeft + (float)i * stepW;
                    float y = plotBottom - (float)((chartHistory[i] - yMin) / (yMax - yMin)) * plotH;
                    rawPts[i] = ImVec2(x, y);
                }

                // Catmull-Rom spline interpolation for smooth curve
                // Subdivisions are adaptive: more when points are far apart,
                // none when points are already sub-2px apart.
                std::vector<ImVec2> points;
                if (n <= 2) {
                    points = rawPts;
                } else {
                    points.reserve(n * 4); // conservative estimate
                    for (size_t i = 0; i + 1 < n; i++) {
                        ImVec2 p0 = rawPts[i > 0 ? i - 1 : 0];
                        ImVec2 p1 = rawPts[i];
                        ImVec2 p2 = rawPts[i + 1];
                        ImVec2 p3 = rawPts[i + 2 < n ? i + 2 : n - 1];

                        // Adaptive subdivision: ~1 segment per 3px of distance
                        float dx = p2.x - p1.x, dy = p2.y - p1.y;
                        float dist = sqrtf(dx * dx + dy * dy);
                        int subdivs = std::clamp((int)(dist / 3.0f), 1, 16);

                        for (int s = 0; s < subdivs; s++) {
                            float t = (float)s / (float)subdivs;
                            float t2 = t * t;
                            float t3 = t2 * t;
                            float q0 = -t3 + 2.0f * t2 - t;
                            float q1 =  3.0f * t3 - 5.0f * t2 + 2.0f;
                            float q2 = -3.0f * t3 + 4.0f * t2 + t;
                            float q3 =  t3 - t2;
                            float sx = 0.5f * (p0.x * q0 + p1.x * q1 + p2.x * q2 + p3.x * q3);
                            float sy = 0.5f * (p0.y * q0 + p1.y * q1 + p2.y * q2 + p3.y * q3);
                            // Clamp Y to plot bounds to prevent Catmull-Rom overshoot
                            sy = std::clamp(sy, plotTop, plotBottom);
                            points.push_back(ImVec2(sx, sy));
                        }
                    }
                    points.push_back(rawPts[n - 1]); // final point
                }

                // Fill under curve (single concave polygon to avoid AA seam shimmer)
                if (points.size() >= 2) {
                    for (size_t i = 0; i < points.size(); i++)
                        dl->PathLineTo(points[i]);
                    dl->PathLineTo(ImVec2(points.back().x, plotBottom));
                    dl->PathLineTo(ImVec2(points.front().x, plotBottom));
                    dl->PathFillConcave(WithAlpha(Success(), 25));
                }

                // Green line
                dl->AddPolyline(points.data(), (int)points.size(),
                                WithAlpha(Success(), 200), ImDrawFlags_None, schema::UI().drawElement("tabs.mining", "chart-line-thickness").size);

                // Y-axis labels
                std::string yMaxStr = FormatHashrate(yMax);
                std::string yMinStr = FormatHashrate(yMin);
                dl->AddText(capFont, capFont->LegacySize, ImVec2(plotLeft + 2 * dp, plotTop - capFont->LegacySize - 2 * dp), OnSurfaceDisabled(), yMaxStr.c_str());
                dl->AddText(capFont, capFont->LegacySize, ImVec2(plotLeft + 2 * dp, plotBottom + 4 * dp), OnSurfaceDisabled(), yMinStr.c_str());

                // X-axis labels
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(plotLeft, chartBot - capFont->LegacySize - 2 * dp),
                            OnSurfaceDisabled(),
                            chartHistory.size() >= 300 ? TR("mining_chart_5m_ago") :
                            chartHistory.size() >= 60  ? TR("mining_chart_1m_ago") : TR("mining_chart_start"));
                std::string nowLbl = TR("mining_chart_now");
                ImVec2 nowSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, nowLbl.c_str());
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(plotRight - nowSz.x, chartBot - capFont->LegacySize - 2 * dp),
                            OnSurfaceDisabled(), nowLbl.c_str());
            }
            
            // Advance cursor past the card (stats/chart view only)
            ImGui::Dummy(ImVec2(availWidth, totalCardH));
        }

        // --- Toggle button in top-right corner ---
        // Rendered after content so the Hand cursor takes priority over
        // the InputTextMultiline text-cursor when hovering the button.
        if (hasLogContent || hasChartContent) {
            ImFont* iconFont = Type().iconSmall();
            const char* toggleIcon = showLogFlag ? ICON_MD_SHOW_CHART : ICON_MD_ARTICLE;
            const char* toggleTip = showLogFlag ? TR("mining_show_chart") : TR("mining_show_log");
            ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, toggleIcon);
            float btnSize = iconSz.y + 8 * dp;
            float btnX = cardMax.x - pad - btnSize;
            float btnY = cardMin.y + pad * 0.5f;
            ImVec2 btnMin(btnX, btnY);
            ImVec2 btnMax(btnX + btnSize, btnY + btnSize);
            ImVec2 btnCenter((btnMin.x + btnMax.x) * 0.5f, (btnMin.y + btnMax.y) * 0.5f);

            bool hov = IsRectHovered(btnMin, btnMax);
            if (hov) {
                dl->AddCircleFilled(btnCenter, btnSize * 0.5f, StateHover());
                ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                ImGui::SetTooltip("%s", toggleTip);
            }
            dl->AddText(iconFont, iconFont->LegacySize,
                        ImVec2(btnCenter.x - iconSz.x * 0.5f, btnCenter.y - iconSz.y * 0.5f),
                        OnSurfaceMedium(), toggleIcon);
            if (hov && ImGui::IsMouseClicked(0)) {
                showLogFlag = !showLogFlag;
            }
        }

        ImGui::Dummy(ImVec2(0, gap));
    }

    // ================================================================
    // EARNINGS — Horizontal row card (Today | Yesterday | All Time | Est. Daily)
    // ================================================================
    {
        // Gather mining transactions from state
        double minedToday = 0.0, minedYesterday = 0.0, minedAllTime = 0.0;
        int minedTodayCount = 0, minedYesterdayCount = 0, minedAllTimeCount = 0;
        int64_t now = (int64_t)std::time(nullptr);

        // Calendar-day boundaries (local time)
        time_t nowT = (time_t)now;
        struct tm local;
#ifdef _WIN32
        localtime_s(&local, &nowT);
#else
        localtime_r(&nowT, &local);
#endif
        local.tm_hour = 0;
        local.tm_min = 0;
        local.tm_sec = 0;
        int64_t todayStart = (int64_t)mktime(&local);
        int64_t yesterdayStart = todayStart - 86400;

        struct MinedTx {
            int64_t timestamp;
            double amount;
            int confirmations;
            bool mature;
            std::string txid;
            bool isPoolPayout;
        };
        std::vector<MinedTx> recentMined;

        for (const auto& tx : state.transactions) {
            bool isSoloMined = (tx.type == "generate" || tx.type == "immature" || tx.type == "mined");
            bool isPoolPayout = (tx.type == "receive"
                                 && !tx.memo.empty()
                                 && tx.memo.find("Mining Pool payout") != std::string::npos);
            if (isSoloMined || isPoolPayout) {
                // Apply earnings filter
                if (!app->supportsSoloMining()) {
                    if (s_earnings_filter == 1 && !isPoolPayout) continue;
                } else {
                    if (s_earnings_filter == 1 && !isSoloMined) continue;
                    if (s_earnings_filter == 2 && !isPoolPayout) continue;
                }

                double amt = std::abs(tx.amount);
                minedAllTime += amt;
                minedAllTimeCount++;
                if (tx.timestamp >= todayStart) {
                    minedToday += amt;
                    minedTodayCount++;
                } else if (tx.timestamp >= yesterdayStart) {
                    minedYesterday += amt;
                    minedYesterdayCount++;
                }
                // Separate solo blocks from pool payouts based on current mode
                bool showInCurrentMode = s_pool_mode ? isPoolPayout : isSoloMined;
                if (showInCurrentMode && recentMined.size() < 4) {
                    recentMined.push_back({tx.timestamp, amt, tx.confirmations, tx.confirmations >= 100, tx.txid, isPoolPayout});
                }
            }
        }

        // Use pool hashrate for EST. DAILY when in pool mode
        double estHashrate = s_pool_mode ? state.pool_mining.hashrate_10s : mining.localHashrate;
        double est_hours_2 = EstimateHoursToBlock(estHashrate, mining.networkHashrate, mining.difficulty);
        double estDailyBlocks = (est_hours_2 > 0) ? (24.0 / est_hours_2) : 0.0;
        double blockReward = schema::UI().drawElement("business", "block-reward").size;
        double estDaily = estDailyBlocks * blockReward;
        bool estActive = isMiningActive && estDaily > 0;

        ImU32 greenCol2 = Success();

        // --- Combined Earnings + Details card ---
        float earningsRowH = ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize + pad * 1.5f;
        float detailsContentH = pad * 0.5f + capFont->LegacySize + pad * 0.5f;
        float barH_est = capFont->LegacySize + Layout::spacingMd() * 2.0f;
        float combinedCardH = earningsRowH + detailsContentH + barH_est + pad;
        combinedCardH = std::max(combinedCardH,
            schema::UI().drawElement("tabs.mining", "details-card-min-height").size + earningsRowH);

        ImVec2 cardMin = ImGui::GetCursorScreenPos();
        ImVec2 cardMax(cardMin.x + availWidth, cardMin.y + combinedCardH);
        DrawGlassPanel(dl, cardMin, cardMax, glassSpec);

        // === Earnings filter toggle button (top-right of card) ===
        {
            const bool soloMiningAvailable = app->supportsSoloMining();
            const char* filterLabels[] = { TR("mining_filter_all"), soloMiningAvailable ? TR("mining_solo") : TR("mining_pool"), TR("mining_pool") };
            const char* filterIcons[] = { ICON_MD_FUNCTIONS, soloMiningAvailable ? ICON_MD_MEMORY : ICON_MD_CLOUD, ICON_MD_CLOUD };
            const int filterCount = soloMiningAvailable ? 3 : 2;
            const char* curIcon = filterIcons[s_earnings_filter];
            const char* curLabel = filterLabels[s_earnings_filter];

            ImFont* icoFont = Type().iconSmall();
            float icoH = icoFont->LegacySize;
            ImVec2 icoSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, curIcon);
            ImVec2 lblSz = ovFont->CalcTextSizeA(ovFont->LegacySize, FLT_MAX, 0, curLabel);
            float padH = Layout::spacingSm();
            float btnW = padH + icoSz.x + Layout::spacingXs() + lblSz.x + padH;
            float btnH = icoH + 8.0f * dp;
            float btnX = cardMax.x - pad - btnW;
            float btnY = cardMin.y + (earningsRowH - btnH) * 0.5f;

            ImVec2 bMin(btnX, btnY), bMax(btnX + btnW, btnY + btnH);
            bool hov = material::IsRectHovered(bMin, bMax);

            // Pill background
            ImU32 pillBg = s_earnings_filter != 0
                ? WithAlpha(Primary(), 60)
                : WithAlpha(OnSurface(), hov ? 25 : 12);
            dl->AddRectFilled(bMin, bMax, pillBg, btnH * 0.5f);

            // Icon
            ImU32 icoCol = s_earnings_filter != 0 ? Primary() : (hov ? OnSurface() : OnSurfaceDisabled());
            float cx = bMin.x + padH;
            float cy = bMin.y + (btnH - icoSz.y) * 0.5f;
            dl->AddText(icoFont, icoFont->LegacySize, ImVec2(cx, cy), icoCol, curIcon);

            // Label
            ImU32 lblCol = s_earnings_filter != 0 ? Primary() : (hov ? OnSurface() : OnSurfaceMedium());
            float lx = cx + icoSz.x + Layout::spacingXs();
            float ly = bMin.y + (btnH - lblSz.y) * 0.5f;
            dl->AddText(ovFont, ovFont->LegacySize, ImVec2(lx, ly), lblCol, curLabel);

            // Click target
            ImVec2 savedCur = ImGui::GetCursorScreenPos();
            ImGui::SetCursorScreenPos(bMin);
            ImGui::InvisibleButton("##EarningsFilter", ImVec2(btnW, btnH));
            if (ImGui::IsItemClicked()) {
                s_earnings_filter = (s_earnings_filter + 1) % filterCount;
            }
            if (ImGui::IsItemHovered()) {
                ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                const char* tips[] = {
                    TR("mining_filter_tip_all"),
                    soloMiningAvailable ? TR("mining_filter_tip_solo") : TR("mining_filter_tip_pool"),
                    TR("mining_filter_tip_pool")
                };
                ImGui::SetTooltip("%s", tips[s_earnings_filter]);
            }
            ImGui::SetCursorScreenPos(savedCur);
        }

        // === Earnings section (top of combined card) ===
        {
            const int numCols = 4;
            float colW = (availWidth - pad * 2) / (float)numCols;
            float ey = cardMin.y + pad * 0.5f;
            char valBuf[64], subBuf2[64];

            struct EarningsEntry {
                const char* label;
                const char* value;
                const char* sub;
                ImU32 col;
            };

            snprintf(valBuf, sizeof(valBuf), "+%.4f", minedToday);
            snprintf(subBuf2, sizeof(subBuf2), "(%d txn)", minedTodayCount);
            char todayVal[64], todaySub[64];
            strncpy(todayVal, valBuf, sizeof(todayVal));
            strncpy(todaySub, subBuf2, sizeof(todaySub));

            char yesterdayVal[64], yesterdaySub[64];
            snprintf(yesterdayVal, sizeof(yesterdayVal), "+%.4f", minedYesterday);
            snprintf(yesterdaySub, sizeof(yesterdaySub), "(%d txn)", minedYesterdayCount);

            char allVal[64], allSub[64];
            snprintf(allVal, sizeof(allVal), "+%.4f", minedAllTime);
            snprintf(allSub, sizeof(allSub), "(%d txn)", minedAllTimeCount);

            char estVal[64];
            if (estActive)
                snprintf(estVal, sizeof(estVal), "~%.4f", estDaily);
            else
                snprintf(estVal, sizeof(estVal), "N/A");

            EarningsEntry entries[] = {
                { TR("mining_today"),      todayVal,      todaySub,      greenCol2 },
                { TR("mining_yesterday"),  yesterdayVal,   yesterdaySub,   OnSurface() },
                { TR("mining_all_time"),   allVal,         allSub,         OnSurface() },
                { TR("mining_est_daily"), estVal,         nullptr,        estActive ? greenCol2 : OnSurfaceDisabled() },
            };

            for (int ei = 0; ei < numCols; ei++) {
                float sx = cardMin.x + pad + ei * colW;
                float centerX = sx + colW * 0.5f;

                // Overline label (centered)
                ImVec2 lblSz = ovFont->CalcTextSizeA(ovFont->LegacySize, 10000, 0, entries[ei].label);
                dl->AddText(ovFont, ovFont->LegacySize,
                            ImVec2(centerX - lblSz.x * 0.5f, ey), OnSurfaceMedium(), entries[ei].label);

                // Value (centered)
                float valY = ey + ovFont->LegacySize + Layout::spacingXs();
                ImVec2 valSz = sub1->CalcTextSizeA(sub1->LegacySize, 10000, 0, entries[ei].value);

                if (entries[ei].sub) {
                    // Value + sub annotation side by side, centered together
                    ImVec2 subSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, entries[ei].sub);
                    float totalW = valSz.x + 4 * dp + subSz.x;
                    float startX = centerX - totalW * 0.5f;
                    dl->AddText(sub1, sub1->LegacySize, ImVec2(startX, valY), entries[ei].col, entries[ei].value);
                    dl->AddText(capFont, capFont->LegacySize,
                                ImVec2(startX + valSz.x + 4 * dp, valY + (sub1->LegacySize - capFont->LegacySize) * 0.5f),
                                OnSurfaceDisabled(), entries[ei].sub);
                } else {
                    dl->AddText(sub1, sub1->LegacySize,
                                ImVec2(centerX - valSz.x * 0.5f, valY), entries[ei].col, entries[ei].value);
                }
            }
        }

        // === Separator between earnings & details ===
        float earningsSepY = cardMin.y + earningsRowH;
        {
            float rnd = glassSpec.rounding;
            dl->AddLine(ImVec2(cardMin.x + rnd * 0.5f, earningsSepY),
                        ImVec2(cardMax.x - rnd * 0.5f, earningsSepY),
                        WithAlpha(OnSurface(), 15), 1.0f * dp);
        }

        // === Details section (below separator) ===
        {
            float cx = cardMin.x + pad;
            float cy = earningsSepY + pad * 0.5f;

            // Three equal columns: Difficulty | Block | Mining Address
            float colW = availWidth / 3.0f;
            float valOffX = availWidth * schema::UI().drawElement("tabs.mining", "stats-col1-value-x-ratio").size;

            float col1X = cx;
            float col2X = cx + colW;
            float col3X = cx + colW * 2.0f;

            // -- Difficulty --
            dl->AddText(capFont, capFont->LegacySize, ImVec2(col1X, cy), OnSurfaceMedium(), TR("difficulty"));
            if (mining.difficulty > 0) {
                snprintf(buf, sizeof(buf), "%.4f", mining.difficulty);
                dl->AddText(capFont, capFont->LegacySize, ImVec2(col1X + valOffX, cy), OnSurface(), buf);
                ImVec2 diffSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, buf);
                ImGui::SetCursorScreenPos(ImVec2(col1X + valOffX, cy));
                ImGui::InvisibleButton("##DiffCopy", ImVec2(diffSz.x + Layout::spacingMd(), capFont->LegacySize + 4 * dp));
                if (ImGui::IsItemHovered()) {
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", TR("mining_click_copy_difficulty"));
                    dl->AddLine(ImVec2(col1X + valOffX, cy + capFont->LegacySize + 1 * dp),
                                ImVec2(col1X + valOffX + diffSz.x, cy + capFont->LegacySize + 1 * dp),
                                WithAlpha(OnSurface(), 60), 1.0f * dp);
                }
                if (ImGui::IsItemClicked()) {
                    ImGui::SetClipboardText(buf);
                    Notifications::instance().info(TR("mining_difficulty_copied"));
                }
            }

            // -- Block --
            dl->AddText(capFont, capFont->LegacySize, ImVec2(col2X, cy), OnSurfaceMedium(), TR("block"));
            if (mining.blocks > 0) {
                snprintf(buf, sizeof(buf), "%d", mining.blocks);
                dl->AddText(capFont, capFont->LegacySize, ImVec2(col2X + valOffX, cy), OnSurface(), buf);
                ImVec2 blkSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, buf);
                ImGui::SetCursorScreenPos(ImVec2(col2X + valOffX, cy));
                ImGui::InvisibleButton("##BlockCopy", ImVec2(blkSz.x + Layout::spacingMd(), capFont->LegacySize + 4 * dp));
                if (ImGui::IsItemHovered()) {
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", TR("mining_click_copy_block"));
                    dl->AddLine(ImVec2(col2X + valOffX, cy + capFont->LegacySize + 1 * dp),
                                ImVec2(col2X + valOffX + blkSz.x, cy + capFont->LegacySize + 1 * dp),
                                WithAlpha(OnSurface(), 60), 1.0f * dp);
                }
                if (ImGui::IsItemClicked()) {
                    ImGui::SetClipboardText(buf);
                    Notifications::instance().info(TR("mining_block_copied"));
                }
            }

            // -- Mining Address --
            dl->AddText(capFont, capFont->LegacySize, ImVec2(col3X, cy), OnSurfaceMedium(), TR("mining_mining_addr"));
            std::string mining_address = "";
            for (const auto& addr : state.addresses) {
                if (addr.type == "transparent" && !addr.address.empty()) {
                    mining_address = addr.address;
                    break;
                }
            }
            if (!mining_address.empty()) {
                float addrAvailW = colW - pad - valOffX;
                float charW = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, "M").x;
                if (charW <= 0.0f) charW = 8.0f;
                int maxChars = std::max(8, (int)(addrAvailW / charW));
                std::string truncAddr = mining_address;
                if ((int)truncAddr.length() > maxChars && maxChars > 5) {
                    int half = (maxChars - 3) / 2;
                    if (half > 0 && (size_t)half < truncAddr.length())
                        truncAddr = truncAddr.substr(0, half) + "..." + truncAddr.substr(truncAddr.length() - half);
                }
                dl->AddText(capFont, capFont->LegacySize, ImVec2(col3X + valOffX, cy), OnSurface(), truncAddr.c_str());

                ImVec2 addrTextSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, truncAddr.c_str());
                ImGui::SetCursorScreenPos(ImVec2(col3X + valOffX, cy));
                ImGui::InvisibleButton("##MiningAddrCopy", ImVec2(addrTextSz.x + Layout::spacingMd(), capFont->LegacySize + 4 * dp));
                if (ImGui::IsItemHovered()) {
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip("%s", TR("mining_click_copy_address"));
                    dl->AddLine(ImVec2(col3X + valOffX, cy + capFont->LegacySize + 1 * dp),
                                ImVec2(col3X + valOffX + addrTextSz.x, cy + capFont->LegacySize + 1 * dp),
                                WithAlpha(OnSurface(), 60), 1.0f * dp);
                }
                if (ImGui::IsItemClicked()) {
                    ImGui::SetClipboardText(mining_address.c_str());
                    Notifications::instance().info(TR("mining_address_copied"));
                }
            }
        }

        // ---- Memory bar — centered in remaining space below details ----
        {
            double totalRAM  = dragonx::util::Platform::getTotalSystemRAM_MB();
            double usedRAM   = dragonx::util::Platform::getUsedSystemRAM_MB();
            double selfRAM   = dragonx::util::Platform::getSelfMemoryUsageMB();
            double daemonRAM = app->getDaemonMemoryUsageMB();
            // Include xmrig memory when pool mining
            double xmrigRAM  = state.pool_mining.memory_used / (1024.0 * 1024.0); // bytes -> MB
            double appRAM    = selfRAM + daemonRAM + xmrigRAM; // App + daemon + xmrig combined

            // Fixed bar height (text + padding)
            float barH = capFont->LegacySize + Layout::spacingMd() * 2.0f;
            float barRnd  = barH * 0.5f; // fully rounded corners

            // Details content ends here
            float detailsEndY = earningsSepY + detailsContentH;

            // Subtle top separator at the boundary
            float rnd = glassSpec.rounding;
            float stripY = detailsEndY;
            dl->AddLine(ImVec2(cardMin.x + rnd * 0.5f, stripY),
                        ImVec2(cardMax.x - rnd * 0.5f, stripY),
                        WithAlpha(OnSurface(), 15), 1.0f * dp);
            float remainingH = cardMax.y - stripY;
            float barX = cardMin.x + pad;
            float barW = cardMax.x - pad - barX;
            float barY = stripY + (remainingH - barH) * 0.5f;
            float textY = barY + (barH - capFont->LegacySize) * 0.5f;
            float textPadX = Layout::spacingMd();

            // Background track
            dl->AddRectFilled(ImVec2(barX, barY), ImVec2(barX + barW, barY + barH),
                              WithAlpha(OnSurface(), 20), barRnd);

            float sysFrac = 0.0f, appFrac = 0.0f;
            float sysFillW = 0.0f, appFillW = 0.0f;

            // Helper: draw a fill bar that perfectly matches the track's rounded
            // left edge regardless of fill width.  We draw a full-width rounded
            // rect (same shape as the track) but clip it to just the fill portion.
            auto drawFillBar = [&](float fillW, ImU32 col) {
                if (fillW <= 1.0f) return;
                dl->PushClipRect(ImVec2(barX, barY), ImVec2(barX + fillW, barY + barH), true);
                dl->AddRectFilled(ImVec2(barX, barY), ImVec2(barX + barW, barY + barH),
                                  col, barRnd);
                dl->PopClipRect();
            };

            if (usedRAM > 0 && totalRAM > 0) {
                sysFrac = std::clamp((float)(usedRAM / totalRAM), 0.0f, 1.0f);
                sysFillW = barW * sysFrac;

                // System memory bar (subtle fill)
                ImU32 ramBarCol = schema::UI().resolveColor("var(--ram-bar-system)",
                    IsDarkTheme() ? IM_COL32(255, 255, 255, 46) : IM_COL32(0, 0, 0, 46));
                drawFillBar(sysFillW, ramBarCol);

                // App+daemon memory bar (saturated accent)
                if (appRAM > 0) {
                    appFrac = std::clamp((float)(appRAM / totalRAM), 0.0f, sysFrac);
                    appFillW = barW * appFrac;
                    ImU32 appBarCol = schema::UI().resolveColor("var(--ram-bar-app)",
                        ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_ButtonActive]));
                    drawFillBar(appFillW, appBarCol);
                }
            }

            // --- Text overlaying the bar ---
            float accentEdge = barX + appFillW;
            float whiteEdge  = barX + sysFillW;

            // Determine text colors for bar segments
            // On dark themes: white text on empty, dark on filled bars (both system and app)
            // On light themes: dark text on empty/system, white on app accent bar
            ImU32 barTextOnEmpty = IsDarkTheme() ? IM_COL32(255, 255, 255, 230)
                                                 : IM_COL32(30, 30, 30, 230);
            ImU32 barTextOnFill  = IsDarkTheme() ? IM_COL32(30, 30, 30, 230)
                                                 : IM_COL32(255, 255, 255, 230);
            ImU32 barTextOnAccent = IsDarkTheme() ? IM_COL32(0, 0, 0, 210)
                                                  : IM_COL32(255, 255, 255, 230);

            // App usage on the left
            char appBuf[64] = {};
            if (appRAM > 0) {
                if (appRAM >= 1024.0)
                    snprintf(appBuf, sizeof(appBuf), "%.1f GB", appRAM / 1024.0);
                else
                    snprintf(appBuf, sizeof(appBuf), "%.0f MB", appRAM);

                float appTextX = barX + textPadX;
                float appTextW = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, appBuf).x;
                float appTextEnd = appTextX + appTextW;

                // Inside accent bar: white | inside white bar: dark | outside: white
                if (appTextEnd <= accentEdge) {
                    dl->AddText(capFont, capFont->LegacySize, ImVec2(appTextX, textY),
                                barTextOnAccent, appBuf);
                } else if (appTextX >= whiteEdge) {
                    dl->AddText(capFont, capFont->LegacySize, ImVec2(appTextX, textY),
                                barTextOnEmpty, appBuf);
                } else {
                    // Part in accent bar: white
                    if (accentEdge > appTextX) {
                        dl->PushClipRect(ImVec2(appTextX, barY), ImVec2(accentEdge, barY + barH));
                        dl->AddText(capFont, capFont->LegacySize, ImVec2(appTextX, textY),
                                    barTextOnAccent, appBuf);
                        dl->PopClipRect();
                    }
                    // Part in white bar (past accent): dark
                    float dkS = std::max(appTextX, accentEdge);
                    float dkE = std::min(appTextEnd, whiteEdge);
                    if (dkE > dkS) {
                        dl->PushClipRect(ImVec2(dkS, barY), ImVec2(dkE, barY + barH));
                        dl->AddText(capFont, capFont->LegacySize, ImVec2(appTextX, textY),
                                    barTextOnFill, appBuf);
                        dl->PopClipRect();
                    }
                    // Part outside white bar: white
                    if (appTextEnd > whiteEdge) {
                        dl->PushClipRect(ImVec2(whiteEdge, barY), ImVec2(appTextEnd + 1, barY + barH));
                        dl->AddText(capFont, capFont->LegacySize, ImVec2(appTextX, textY),
                                    barTextOnEmpty, appBuf);
                        dl->PopClipRect();
                    }
                }
            }

            // System usage on the right
            char sysBuf[64] = {};
            if (usedRAM > 0 && totalRAM > 0)
                snprintf(sysBuf, sizeof(sysBuf), "%.1f / %.0f GB", usedRAM / 1024.0, totalRAM / 1024.0);
            else if (totalRAM > 0)
                snprintf(sysBuf, sizeof(sysBuf), "-- / %.0f GB", totalRAM / 1024.0);
            else
                snprintf(sysBuf, sizeof(sysBuf), "N/A");

            float sysTextW = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, sysBuf).x;
            float sysTextX = barX + barW - textPadX - sysTextW;

            if (sysTextX >= whiteEdge) {
                dl->AddText(capFont, capFont->LegacySize, ImVec2(sysTextX, textY),
                            barTextOnEmpty, sysBuf);
            } else if (sysTextX + sysTextW <= whiteEdge) {
                dl->AddText(capFont, capFont->LegacySize, ImVec2(sysTextX, textY),
                            barTextOnFill, sysBuf);
            } else {
                dl->PushClipRect(ImVec2(sysTextX, barY), ImVec2(whiteEdge, barY + barH));
                dl->AddText(capFont, capFont->LegacySize, ImVec2(sysTextX, textY),
                            barTextOnFill, sysBuf);
                dl->PopClipRect();
                dl->PushClipRect(ImVec2(whiteEdge, barY), ImVec2(sysTextX + sysTextW + 1, barY + barH));
                dl->AddText(capFont, capFont->LegacySize, ImVec2(sysTextX, textY),
                            barTextOnEmpty, sysBuf);
                dl->PopClipRect();
            }

            // Invisible button over the bar for tooltip interaction
            ImGui::SetCursorScreenPos(ImVec2(barX, barY));
            ImGui::InvisibleButton("##rambar", ImVec2(barW, barH));
            if (ImGui::IsItemHovered()) {
                ImGui::BeginTooltip();
                if (selfRAM >= 1024.0)
                    ImGui::Text(TR("ram_wallet_gb"), selfRAM / 1024.0);
                else
                    ImGui::Text(TR("ram_wallet_mb"), selfRAM);
                if (daemonRAM >= 1024.0)
                    ImGui::Text(TR("ram_daemon_gb"), daemonRAM / 1024.0, app->getDaemonMemDiag().c_str());
                else
                    ImGui::Text(TR("ram_daemon_mb"), daemonRAM, app->getDaemonMemDiag().c_str());
                ImGui::Separator();
                ImGui::Text(TR("ram_system_gb"), usedRAM / 1024.0, totalRAM / 1024.0);
                ImGui::EndTooltip();
            }
        }

        ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
        ImGui::Dummy(ImVec2(availWidth, 0));
        ImGui::Dummy(ImVec2(0, gap));

        // ============================================================
        // RECENT BLOCKS — last 4 mined blocks (always shown in pool mode)
        // ============================================================
        if (!recentMined.empty() || s_pool_mode) {
            Type().textColored(TypeStyle::Overline, OnSurfaceMedium(),
                s_pool_mode ? TR("mining_recent_payouts") : TR("mining_recent_blocks"));
            ImGui::Dummy(ImVec2(0, Layout::spacingXs()));

            float rowH_blocks = std::max(schema::UI().drawElement("tabs.mining", "recent-row-min-height").size, schema::UI().drawElement("tabs.mining", "recent-row-height").size * vs);
            // Size to remaining space — proportional budget ensures fit
            float recentAvailH = ImGui::GetContentRegionAvail().y - sHdr - gapOver;
            float minRows = recentMined.empty() ? 2.0f : (float)recentMined.size();
            float contentH_blocks = rowH_blocks * minRows + pad * 2.5f;
            float recentH = std::clamp(contentH_blocks, 30.0f * dp, std::max(30.0f * dp, recentAvailH));

            // Glass panel wrapping the list + scroll-edge mask state
            ImVec2 recentPanelMin = ImGui::GetCursorScreenPos();
            ImVec2 recentPanelMax(recentPanelMin.x + availWidth, recentPanelMin.y + recentH);
            GlassPanelSpec recentGlass;
            recentGlass.rounding = Layout::glassRounding();
            DrawGlassPanel(dl, recentPanelMin, recentPanelMax, recentGlass);

            float miningScrollY = 0.0f, miningScrollMaxY = 0.0f;
            int miningParentVtx = dl->VtxBuffer.Size;

            ImGui::BeginChild("##RecentBlocks", ImVec2(availWidth, recentH), false,
                ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
            ImDrawList* miningChildDL = ImGui::GetWindowDrawList();
            int miningChildVtx = miningChildDL->VtxBuffer.Size;

            miningScrollY    = ImGui::GetScrollY();
            miningScrollMaxY = ImGui::GetScrollMaxY();

            // Top padding inside glass card
            ImGui::Dummy(ImVec2(0, pad * 0.5f));

            if (recentMined.empty()) {
                // Empty state — card is visible but no rows yet
                float emptyY = ImGui::GetCursorScreenPos().y;
                float emptyX = ImGui::GetCursorScreenPos().x;
                float centerX = emptyX + availWidth * 0.5f;
                ImFont* icoFont = Type().iconMed();
                const char* emptyIcon = ICON_MD_HOURGLASS_EMPTY;
                ImVec2 iSz = icoFont->CalcTextSizeA(icoFont->LegacySize, FLT_MAX, 0, emptyIcon);
                miningChildDL->AddText(icoFont, icoFont->LegacySize,
                    ImVec2(centerX - iSz.x * 0.5f, emptyY),
                    OnSurfaceDisabled(), emptyIcon);
                const char* emptyMsg = s_pool_mode
                    ? TR("mining_no_payouts_yet")
                    : TR("mining_no_blocks_yet");
                ImVec2 msgSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, emptyMsg);
                miningChildDL->AddText(capFont, capFont->LegacySize,
                    ImVec2(centerX - msgSz.x * 0.5f, emptyY + iSz.y + Layout::spacingXs()),
                    OnSurfaceDisabled(), emptyMsg);
            }

            for (size_t mi = 0; mi < recentMined.size(); mi++) {
                const auto& mtx = recentMined[mi];

                ImVec2 rMin = ImGui::GetCursorScreenPos();
                float rH = std::max(schema::UI().drawElement("tabs.mining", "recent-row-min-height").size, schema::UI().drawElement("tabs.mining", "recent-row-height").size * vs);
                ImVec2 rMax(rMin.x + availWidth, rMin.y + rH);

                // Subtle background on hover (inset from card edges)
                bool hovered = material::IsRectHovered(rMin, rMax);
                bool isClickable = !mtx.txid.empty();
                if (hovered) {
                    dl->AddRectFilled(ImVec2(rMin.x + pad * 0.5f, rMin.y),
                                      ImVec2(rMax.x - pad * 0.5f, rMax.y),
                                      IM_COL32(255, 255, 255, 8), 3.0f * dp);
                    if (isClickable) {
                        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    }
                }

                float rx = rMin.x + pad;
                float ry = rMin.y + Layout::spacingXs();

                // Mining icon — Material Design
                ImFont* iconFont = Type().iconSmall();
                const char* mIcon = ICON_MD_CONSTRUCTION;
                ImVec2 iSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, mIcon);
                float iconX = rx + 2 * dp, iconY = ry + 2 * dp;
                dl->AddText(iconFont, iconFont->LegacySize,
                            ImVec2(iconX, iconY),
                            WithAlpha(Warning(), 200), mIcon);

                // Time
                int64_t diff = now - mtx.timestamp;
                if (diff < 60)
                    snprintf(buf, sizeof(buf), TR("time_seconds_ago"), (long long)diff);
                else if (diff < 3600)
                    snprintf(buf, sizeof(buf), TR("time_minutes_ago"), (long long)(diff / 60));
                else if (diff < 86400)
                    snprintf(buf, sizeof(buf), TR("time_hours_ago"), (long long)(diff / 3600));
                else
                    snprintf(buf, sizeof(buf), TR("time_days_ago"), (long long)(diff / 86400));
                dl->AddText(capFont, capFont->LegacySize, ImVec2(rx + iSz.x + 8 * dp, ry), OnSurfaceDisabled(), buf);

                // Amount
                snprintf(buf, sizeof(buf), "+%.8f %s", mtx.amount, DRAGONX_TICKER);
                float amtX = rMin.x + pad + (availWidth - pad * 2) * 0.35f;
                dl->AddText(capFont, capFont->LegacySize, ImVec2(amtX, ry), greenCol2, buf);

                // Maturity badge — inset from right edge
                float badgeX = rMax.x - pad - Layout::spacingXl() * 3.5f;
                if (mtx.mature) {
                    dl->AddText(capFont, capFont->LegacySize, ImVec2(badgeX, ry),
                                WithAlpha(Success(), 180), TR("mature"));
                } else {
                    snprintf(buf, sizeof(buf), TR("conf_count"), mtx.confirmations);
                    dl->AddText(capFont, capFont->LegacySize, ImVec2(badgeX, ry),
                                WithAlpha(Warning(), 200), buf);
                }

                // Click to open in block explorer
                ImGui::SetCursorScreenPos(rMin);
                char blockBtnId[32];
                snprintf(blockBtnId, sizeof(blockBtnId), "##RecentBlock%zu", mi);
                ImGui::InvisibleButton(blockBtnId, ImVec2(availWidth, rH));
                if (ImGui::IsItemClicked() && !mtx.txid.empty()) {
                    std::string url = app->settings()->getTxExplorerUrl() + mtx.txid;
                    dragonx::util::Platform::openUrl(url);
                }
                if (ImGui::IsItemHovered() && !mtx.txid.empty()) {
                    ImGui::SetTooltip("%s", TR("mining_open_in_explorer"));
                }
            }

            ImGui::EndChild(); // ##RecentBlocks

            // CSS-style clipping mask
            {
                float fadeZone = std::min(capFont->LegacySize * 3.0f, recentH * 0.18f);
                ApplyScrollEdgeMask(dl, miningParentVtx, miningChildDL, miningChildVtx,
                                    recentPanelMin.y, recentPanelMax.y, fadeZone, miningScrollY, miningScrollMaxY);
            }

            ImGui::Dummy(ImVec2(0, gap));
        }
    }

    // ================================================================
    // POOL CONNECTION STATUS — inline indicator (pool mode, no log)
    // ================================================================
    if (s_pool_mode && state.pool_mining.log_lines.empty() && state.pool_mining.xmrig_running) {
        ImFont* iconFont = Type().iconSmall();
        const char* dotIcon = ICON_MD_CIRCLE;
        ImU32 dotCol = state.pool_mining.connected ? WithAlpha(Success(), 200) : WithAlpha(Error(), 200);
        const char* statusText = state.pool_mining.connected
            ? (state.pool_mining.pool_url.empty() ? TR("mining_connected") : state.pool_mining.pool_url.c_str())
            : TR("mining_connecting");

        ImVec2 pos = ImGui::GetCursorScreenPos();
        ImVec2 dotSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, dotIcon);
        dl->AddText(iconFont, iconFont->LegacySize, pos, dotCol, dotIcon);
        dl->AddText(capFont, capFont->LegacySize,
                    ImVec2(pos.x + dotSz.x + 4 * dp, pos.y + (dotSz.y - capFont->LegacySize) * 0.5f),
                    OnSurfaceMedium(), statusText);
        ImGui::Dummy(ImVec2(availWidth, capFont->LegacySize + 4 * dp));
    }
}

} // namespace ui
} // namespace dragonx
