import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Layouts
import ISC
import ISC.Tabs.components
import QtQuick.Dialogs

// ===== Home Workspace Shell =====
// Unified left rail: pill tab bar → context panel → status footer → utility buttons.
// Workspace fills the remaining area with the active tab's content.
Rectangle {
    id: root
    anchors.fill: parent
    color: Theme.pageBackground
    clip: true
    border.color: Theme.outerFrameBorder
    border.width: Theme.borderStrong

    // ===== View State =====
    property int selectedTabIndex: 0

    // Map, Graph, and Data each keep their OWN remembered file selection —
    // switching tabs must not bleed one tab's pick into another. streamController
    // itself only holds ONE loaded file at a time (it's a single review session),
    // so "per-tab selection" means: remember the filename per tab here, and
    // re-load the right one into that single session whenever the tab becomes
    // active again. Data tab's compareFileA/B are tracked the same way further
    // below (Connections on dataTabLoader.item).
    property string mapReviewFile: ""
    property string graphReviewFile: ""
    property string dataCompareFileA: ""
    property string dataCompareFileB: ""

    onSelectedTabIndexChanged: {
        // Time-based, so re-check on every switch rather than trusting a
        // value computed at some earlier point in a possibly long-running session.
        root.reviewBlocked = licenseModel.reviewAccessBlocked()
        if (selectedTabIndex === 2) {
            file_browser.refreshFiles();
            if (root.mapReviewFile !== "") {
                if (streamController.loadedFile !== root.mapReviewFile)
                    streamController.loadFile(root.mapReviewFile)
            } else if (streamController.loadedFile !== "") {
                // Map has no memory of its own yet — don't inherit whatever
                // another tab (e.g. Data's last Run A/B pick) left loaded.
                streamController.unloadFile()
            }
        } else if (selectedTabIndex === 3) {
            if (root.graphReviewFile !== "") {
                if (streamController.loadedFile !== root.graphReviewFile)
                    streamController.loadFile(root.graphReviewFile)
            } else if (streamController.loadedFile !== "") {
                streamController.unloadFile()
            }
        }
        // Leaving Map tab no longer force-stops a live stream: streamController
        // (StreamReader + timers) is parented to itself, not to this QML tree,
        // so acquisition keeps running in the background. The Map Loader just
        // deactivates — trendChart's local 60s buffer resets on return (it's
        // QML-item-local state), but the wafer map and stream resume live.
    }

    // Records which tab's memory a file pick belongs to. Only fires for
    // picks made while actually on Map(2) or Graph(3) — Data tab's own
    // Connections (further below) captures its own picks independently,
    // and this is silent while on Status(0)/Data(1) for any other load.
    Connections {
        target: streamController
        function onLoadedFileChanged() {
            if (root.selectedTabIndex === 2) root.mapReviewFile = streamController.loadedFile
            else if (root.selectedTabIndex === 3) root.graphReviewFile = streamController.loadedFile
        }
        function onLiveError(message) {
            deviceController.appendActivityLog("Live stream error — stream stopped: " + message)
        }
    }

    // Data tab's Run A/B picks survive it being destroyed/recreated by its
    // Loader (see dataTabLoader below) by mirroring them here.
    Connections {
        target: dataTabLoader.item
        function onCompareFileAChanged() { root.dataCompareFileA = dataTabLoader.item.compareFileA }
        function onCompareFileBChanged() { root.dataCompareFileB = dataTabLoader.item.compareFileB }
    }



    property bool memoryRead: false
    property bool waferDetected: false
    property bool waferLicensed: false

    // Data/Graph/Map-review are gated on the 30-day trial once it's actually
    // expired (ADR-0005 "Deferred" item — the trigger existed, nothing ever
    // checked it). Status stays free: hardware actions are gated separately,
    // per-wafer-serial, via waferLicensed above.
    property bool reviewBlocked: false
    onReviewBlockedChanged: {
        // Property change signals only fire on an actual value change, so
        // this logs once per none/temporary → expired transition, not on
        // every recompute in onSelectedTabIndexChanged/onLicensesChanged.
        if (reviewBlocked) {
            deviceController.appendActivityLog(
                "Review trial expired — load a wafer license to continue using Data/Map/Graph (About dialog)")
            if (root.selectedTabIndex !== 0) {
                root.selectedTabIndex = 0
            }
        }
    }

    Connections {
        target: deviceController
        function onDetectResult(result){
            // detectResult payload is Signal(object): a dict may cross into QML
            // as an opaque wrapper whose fields read as undefined, so only
            // test truthiness (None on failure, dict on success).
            root.waferDetected = !!result
            root.waferLicensed = root.waferDetected && deviceController.waferLicensed()
            // Wafer's own onboard runtime — not a local file a technician can
            // reset — is what starts the review trial clock. startReplayTrial()
            // is a true no-op once the marker exists (see license_model.py),
            // so re-detecting the same over-limit wafer never extends it.
            if (root.waferDetected && deviceController.waferRuntimeExceeded()) {
                licenseModel.startReplayTrial()
            }
        }
    }
    Connections {
        // Deferred until the post-read metadata dialog (StatusTab.qml) closes —
        // jumping tabs from onParsedDataReady directly would deactivate the
        // Status Loader (StackLayout.index !== selectedTabIndex) and tear down
        // the dialog before the technician could see or use it.
        target: statusTabLoader.item
        function onCsvMetadataDialogClosed(csvPath) {
            if (!csvPath) return
            streamController.loadFile(csvPath)
            // Fires while still on Status(0), before the tab switch below —
            // the generic per-tab tracker only attributes picks made while
            // already on Map/Graph, so record this one explicitly.
            root.mapReviewFile = csvPath
            if (root.selectedTabIndex === 0 && licenseModel.licenses.length > 0) {
                root.selectedTabIndex = 2
            }
        }
    }
    Connections {
        target: licenseModel
        function onLicensesChanged() {
            // Map tab is license-gated: if its licenses vanish while it's
            // selected, snap back to STATUS instead of showing a bright
            // pill over an unloaded tab. Also re-check the wafer license
            // live so Read/Erase don't stay stale until the next detect.
            if (root.selectedTabIndex === 2 && licenseModel.licenses.length === 0) {
                root.selectedTabIndex = 0
            }
            root.waferLicensed = root.waferDetected && deviceController.waferLicensed()
            root.reviewBlocked = licenseModel.reviewAccessBlocked()
        }
    }
    Connections {
        target: deviceController
        function onReadResult(result) {
            root.memoryRead = (result !== null &&
                               result !== undefined &&
                               result.success === true)
            if (root.memoryRead) {
                console.log("Memory read complete:", result.bytes, "bytes")
            } else {
                console.log("Read failed:", result.error || "unknown")
            }
        }
    }

    function _doDetect() {
        root.memoryRead = false
        root.waferDetected = false
        streamController.setMode("review")
        streamController.stopStream()
        deviceController.detectWafer()
    }

    FolderDialog {
        id: saveDirDialog
        title: "Choose a folder to save Data"
        onAccepted: {
            var dir = String(selectedFolder)
            deviceController.setSaveDataDir(dir)
            file_browser.setCurrentDirectory(dir)
            root._doDetect()
        }
    }

    // ===== Split Dialog (Threshold Segmentation) =====
    SplitDialog {
        id: splitDialog
    }

    // ===== Settings Popup =====
    Popup {
        id: settingsPopup
        modal: true
        dim: true
        closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
        width: Math.min(root.width - 80, 800)
        height: Math.min(root.height - 80, 600)
        anchors.centerIn: Overlay.overlay

        background: Rectangle {
            radius: Theme.radiusMd
            color: Theme.cardBackground
            border.color: Theme.cardBorder
            border.width: 1
        }

        contentItem: Rectangle {
            color: Theme.pageBackground
            radius: Theme.radiusMd
            clip: true

            ColumnLayout {
                anchors.fill: parent
                spacing: 0

                Rectangle {
                    Layout.fillWidth: true
                    Layout.preferredHeight: 40
                    color: Theme.panelBackground
                    border.color: Theme.cardBorder
                    border.width: 1
                    radius: Theme.radiusMd

                    RowLayout {
                        anchors.fill: parent
                        anchors.leftMargin: 14
                        anchors.rightMargin: 4

                        Label {
                            text: "Settings"
                            font.pixelSize: Theme.fontLg
                            font.bold: true
                            color: Theme.headingColor
                            Layout.fillWidth: true
                        }

                        Button {
                            text: "\u2715"
                            flat: true
                            Layout.preferredWidth: 32
                            Layout.preferredHeight: 32
                            onClicked: settingsPopup.close()

                            background: Rectangle {
                                radius: Theme.radiusXs
                                color: parent.hovered ? Theme.buttonNeutralHover : "transparent"
                            }
                            contentItem: Label {
                                text: parent.text
                                color: Theme.bodyColor
                                horizontalAlignment: Text.AlignHCenter
                                verticalAlignment: Text.AlignVCenter
                            }
                        }
                    }
                }

                Item {
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    Layout.rightMargin: 6
                    clip: true

                    Loader {
                        anchors.fill: parent
                        source: "Tabs/SettingsTab.qml"
                    }
                }
            }
        }
    }

    // ===== About Dialog =====
    AboutDialog {
        id: aboutDialog
    }

    // ===== First-Launch Wizard =====
    FirstLaunchWizard {
        id: firstLaunchWizard
        onFinished: {
            close()
            // Wizard closed with no license loaded (skipped, or Load License
            // never landed a valid file) — start the 30-day review trial now
            // rather than waiting for a wafer to be detected over-runtime.
            if (licenseModel.licenses.length === 0) {
                licenseModel.startReplayTrial()
            }
            settingsModel.saveSettings()
        }
    }
    Component.onCompleted: {
        root.reviewBlocked = licenseModel.reviewAccessBlocked()
        if (isFirstLaunch) {
            firstLaunchWizard.open()
        }
    }

    // ===== Main Layout: Rail + Workspace =====
    RowLayout {
        anchors.fill: parent
        spacing: 0

        // ── LEFT RAIL ──────────────────────────────────────────────────────
        Rectangle {
            id: rail
            Layout.preferredWidth: Theme.sideRailWidth
            Layout.fillHeight: true
            color: Theme.sideRailBackground

            ColumnLayout {
                anchors.fill: parent
                anchors.margins: Theme.sideRailMargin
                spacing: Theme.sideRailSpacing

                // ── BOX 1: Pill Tab Bar ─────────────────────────────────────
                Rectangle {
                    id: pillBar
                    Layout.fillWidth: true
                    Layout.preferredHeight: Theme.sidePillHeight
                    radius: 999
                    border.color: Theme.sideBorder
                    border.width: 1
                    color: "transparent"

                    ListModel {
                        id: pillModel
                        ListElement {
                            label: "STATUS"
                            icon: "Tabs/icons/status.svg"
                            expandW: 86
                            desc: "Monitor real-time device connection, serial communication, and execute wafer actions."
                        }
                        ListElement {
                            label: "DATA"
                            icon: "Tabs/icons/data.svg"
                            expandW: 66
                            desc: "Manage, review, compare (DTW), and split historical sensor data records."
                        }
                        ListElement {
                            label: "MAP"
                            icon: "Tabs/icons/map.svg"
                            expandW: 58
                            desc: "Visualize spatial sensor readings on a 2D thin-plate spline RBF interpolated heatmap."
                        }
                        ListElement {
                            label: "GRAPH"
                            icon: "Tabs/icons/bar-chart.svg"
                            expandW: 70
                            desc: "Whole-run multi-sensor temperature chart for the currently loaded file."
                        }
                    }

                    RowLayout {
                        anchors.fill: parent
                        anchors.margins: 3
                        spacing: 0

                        Repeater {
                            model: pillModel
                            delegate: Button {
                                id: pillBtn
                                checked: root.selectedTabIndex === index
                                // MAP (index 2) is locked until a valid license .bin is installed.
                                // DATA/MAP/GRAPH (index !== 0) also lock once the 30-day review
                                // trial has actually expired with no permanent license (ADR-0005).
                                // Stays enabled (not `enabled: false`) so hover still fires and the
                                // tooltip can explain why — a disabled Item receives no mouse/hover
                                // events at all, which would silently kill the tooltip below.
                                property bool locked: (index === 2 && licenseModel.licenses.length === 0)
                                    || (index !== 0 && root.reviewBlocked)
                                flat: true
                                Layout.fillWidth: true
                                Layout.fillHeight: true
                                Layout.preferredWidth: pillBtn.checked ? model.expandW : 44
                                Behavior on Layout.preferredWidth {
                                    NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
                                }

                                background: Rectangle {
                                    radius: 50
                                    color: pillBtn.checked ? Theme.sidePanelBackground : "transparent"
                                }

                                contentItem: Row {
                                    id: pillContent
                                    spacing: pillBtn.checked ? 6 : 0
                                    width: pillIcon.width + (pillBtn.checked ? pillContent.spacing + pillLabel.width : 0)
                                    anchors.verticalCenter: parent.verticalCenter
                                    x: (parent.width - width) / 2
                                    Behavior on x {
                                        NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
                                    }

                                    IconImage {
                                        id: pillIcon
                                        anchors.verticalCenter: parent.verticalCenter
                                        width: 16; height: 16
                                        source: model.icon
                                        sourceSize.width: 16; sourceSize.height: 16
                                        color: (pillBtn.checked || pillBtn.hovered) ? Theme.headingColor : Theme.sideMutedText
                                        opacity: !pillBtn.locked ? (pillBtn.checked ? 0.9 : pillBtn.hovered ? 0.85 : 0.4) : 0.2
                                        Behavior on color {
                                            ColorAnimation { duration: Theme.durationFast }
                                        }
                                        Behavior on opacity {
                                            NumberAnimation { duration: Theme.durationFast }
                                        }
                                    }

                                    Label {
                                        id: pillLabel
                                        anchors.verticalCenter: parent.verticalCenter
                                        width: pillBtn.checked ? implicitWidth : 0
                                        text: model.label
                                        color: pillBtn.checked ? Theme.headingColor : Theme.sideMutedText
                                        font.family: Theme.uiFontFamily
                                        font.pixelSize: Theme.fontSm
                                        font.bold: true
                                        font.letterSpacing: 1.0
                                        opacity: !pillBtn.locked ? (pillBtn.checked ? 1.0 : 0.0) : 0.25
                                        clip: true
                                        Behavior on width {
                                            NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
                                        }
                                        Behavior on color {
                                            ColorAnimation { duration: Theme.durationBase; easing.type: Theme.easeStandard }
                                        }
                                        Behavior on opacity {
                                            NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
                                        }
                                    }
                                }

                                AppToolTip {
                                    visible: pillBtn.hovered
                                    // Trial-expired is checked first: it's the broader reason and
                                    // applies identically to Data/Map/Graph, so when it's true it
                                    // wins even for MAP — otherwise MAP's narrower "no license at
                                    // all" case (trial not yet expired, still just unlicensed) shows.
                                    text: (index !== 0 && root.reviewBlocked)
                                        ? "<b>" + model.label + "</b>: Review trial expired — load a license in About to unlock"
                                        : (index === 2 && licenseModel.licenses.length === 0)
                                            ? "<b>MAP</b>: No license loaded — load one in About to unlock"
                                            : "<b>" + model.label + "</b>: " + model.desc
                                }

                                onClicked: if (!pillBtn.locked) root.selectedTabIndex = index
                            }
                        }
                    }
                }

                // ── BOX 1.5: Run Comparison (Data tab only) ────────────────
                Rectangle {
                    id: runComparisonBox
                    Layout.fillWidth: true
                    Layout.preferredHeight: runCompCol.implicitHeight + 28
                    visible: root.selectedTabIndex === 1
                    radius: Theme.sidePanelRadius
                    border.color: Theme.sideBorder
                    border.width: 1
                    color: Theme.sidePanelBackground

                    readonly property var dataTab: dataTabLoader.item

                    component RunSlot: Rectangle {
                        id: slotBox
                        property string title
                        property string filePath: ""
                        property color accent: Theme.primaryAccent
                        property bool active: false
                        property bool locked: false
                        property string placeholderText: "Click to select"
                        signal cleared()
                        signal armed()

                        Layout.fillWidth: true
                        implicitHeight: 58
                        radius: Theme.radiusSm
                        color: Theme.fieldBackground
                        opacity: slotBox.locked ? 0.65 : 1.0
                        border.width: slotBox.active ? 2 : 1
                        border.color: slotBox.active ? accent : Theme.sideBorder

                        Behavior on border.color { ColorAnimation { duration: Theme.durationFast } }

                        MouseArea {
                            anchors.fill: parent
                            enabled: !slotBox.locked
                            cursorShape: slotBox.locked ? Qt.ArrowCursor : Qt.PointingHandCursor
                            onClicked: slotBox.armed()
                        }

                        RowLayout {
                            anchors.fill: parent
                            anchors.margins: 10
                            spacing: 8

                            Rectangle {
                                width: 8; height: 8; radius: 4
                                color: slotBox.accent
                                Layout.alignment: Qt.AlignVCenter
                            }

                            ColumnLayout {
                                Layout.fillWidth: true
                                spacing: 2
                                Label {
                                    text: slotBox.title
                                    font.pixelSize: Theme.fontXs
                                    font.bold: true
                                    font.letterSpacing: 1.0
                                    color: slotBox.active ? slotBox.accent : Theme.sideMutedText
                                }
                                Label {
                                    text: slotBox.filePath !== ""
                                        ? slotBox.filePath.split("/").pop()
                                        : (slotBox.locked ? slotBox.placeholderText : (slotBox.active ? "<- Select file below..." : "Click to select"))
                                    font.pixelSize: Theme.fontSm
                                    color: slotBox.filePath !== "" ? Theme.headingColor : Theme.bodyColor
                                    opacity: slotBox.filePath !== "" ? 1.0 : 0.6
                                    elide: Text.ElideMiddle
                                    Layout.fillWidth: true
                                }
                            }

                            Button {
                                visible: slotBox.filePath !== "" && !slotBox.locked
                                flat: true
                                implicitWidth: 24; implicitHeight: 24
                                onClicked: slotBox.cleared()
                                background: Rectangle {
                                    radius: Theme.radiusSm
                                    color: parent.hovered ? Theme.flatButtonHover : "transparent"
                                }
                                contentItem: IconImage {
                                    source: "Tabs/icons/x.svg"
                                    width: 14; height: 14
                                    sourceSize.width: 14
                                    sourceSize.height: 14
                                    color: Theme.bodyColor
                                    anchors.centerIn: parent
                                }
                            }
                        }
                    }

                    ColumnLayout {
                        id: runCompCol
                        anchors.fill: parent
                        anchors.margins: 14
                        spacing: 10

                        RowLayout {
                            Layout.fillWidth: true
                            spacing: 8

                            Label {
                                text: "RUN COMPARISON (DTW)"
                                color: Theme.sideMutedText
                                font.family: Theme.uiFontFamily
                                font.pixelSize: Theme.fontSm
                                font.letterSpacing: 1.5
                                font.bold: true
                                Layout.fillWidth: true
                                elide: Text.ElideRight
                            }

                            Button {
                                id: resetRunBtn
                                flat: true
                                implicitWidth: 32
                                implicitHeight: 32
                                visible: true
                                onClicked: if (runComparisonBox.dataTab) runComparisonBox.dataTab.resetComparison()

                                background: Rectangle {
                                    radius: Theme.radiusSm
                                    color: resetRunBtn.hovered ? Theme.flatButtonHover : "transparent"
                                }
                                contentItem: IconImage {
                                    source: "Tabs/icons/rotate-ccw.svg"
                                    width: 18; height: 18
                                    sourceSize.width: 18
                                    sourceSize.height: 18
                                    color: Theme.bodyColor
                                    anchors.centerIn: parent
                                }
                                AppToolTip {
                                    visible: resetRunBtn.hovered
                                    text: "Reset comparison"
                                }
                            }
                        }

                        PanelCheckBox {
                            id: useMasterCheck
                            Layout.fillWidth: true
                            text: "Compare vs Master File"
                            checked: runComparisonBox.dataTab ? runComparisonBox.dataTab.useMasterFile : false
                            onToggled: {
                                if (!runComparisonBox.dataTab) return
                                runComparisonBox.dataTab.useMasterFile = checked
                                if (checked) runComparisonBox.dataTab.activeBox = 2
                            }
                        }

                        RunSlot {
                            readonly property bool useMaster: runComparisonBox.dataTab ? runComparisonBox.dataTab.useMasterFile : false
                            title: useMaster ? "MASTER FILE" : "REFERENCE (RUN A)"
                            accent: Theme.primaryAccent
                            locked: useMaster
                            placeholderText: useMaster
                                ? (runComparisonBox.dataTab.runBWaferType !== ""
                                    ? "No master file for type " + runComparisonBox.dataTab.runBWaferType
                                    : "Select Run B to resolve master")
                                : "Click to select"
                            active: runComparisonBox.dataTab ? (!useMaster && runComparisonBox.dataTab.activeBox === 1) : false
                            filePath: runComparisonBox.dataTab
                                ? (useMaster ? runComparisonBox.dataTab.masterFileForRunB : runComparisonBox.dataTab.compareFileA)
                                : ""
                            onArmed: if (runComparisonBox.dataTab && !useMaster) runComparisonBox.dataTab.activeBox = 1
                            onCleared: {
                                if (!runComparisonBox.dataTab) return
                                runComparisonBox.dataTab.compareFileA = ""
                                runComparisonBox.dataTab.activeBox = 1
                                runComparisonBox.dataTab.clearResults()
                            }
                        }

                        RunSlot {
                            title: "SESSION (RUN B)"
                            accent: Theme.themeSkill
                            active: runComparisonBox.dataTab ? runComparisonBox.dataTab.activeBox === 2 : false
                            filePath: runComparisonBox.dataTab ? runComparisonBox.dataTab.compareFileB : ""
                            onArmed: if (runComparisonBox.dataTab) runComparisonBox.dataTab.activeBox = 2
                            onCleared: {
                                if (!runComparisonBox.dataTab) return
                                runComparisonBox.dataTab.compareFileB = ""
                                runComparisonBox.dataTab.activeBox = 2
                                runComparisonBox.dataTab.clearResults()
                            }
                        }

                        Button {
                            id: compareRunBtn
                            Layout.fillWidth: true
                            Layout.preferredHeight: 32
                            readonly property var dt: runComparisonBox.dataTab
                            readonly property string fileA: !!dt ? (dt.useMasterFile ? dt.masterFileForRunB : dt.compareFileA) : ""
                            readonly property bool masterMissing: !!dt && dt.useMasterFile && dt.masterFileForRunB === ""
                            readonly property bool sameFile: !!dt && fileA !== "" && fileA === dt.compareFileB
                            readonly property string gateError: !!dt ? dt.familyGateError : ""
                            enabled: !!dt && fileA !== "" && dt.compareFileB !== "" && !sameFile && !dt.comparing && !masterMissing && gateError === ""
                            AppToolTip {
                                visible: (compareRunBtn.sameFile || compareRunBtn.masterMissing || compareRunBtn.gateError !== "") && compareRunBtn.hovered
                                text: compareRunBtn.masterMissing
                                    ? "No master file registered for this wafer type (Settings → Master Files)"
                                    : compareRunBtn.gateError !== "" ? compareRunBtn.gateError
                                    : "Choose two different runs to compare"
                            }
                            onClicked: {
                                dt.comparing = true
                                dt.clearResults()
                                streamController.compareFiles(fileA, dt.compareFileB)
                            }

                            background: Rectangle {
                                radius: Theme.radiusSm
                                color: compareRunBtn.enabled
                                    ? (compareRunBtn.hovered ? Qt.lighter(Theme.primaryAccent, 1.1) : Theme.primaryAccent)
                                    : Theme.buttonNeutralBackground
                                Behavior on color { ColorAnimation { duration: Theme.durationFast } }
                            }
                            contentItem: Row {
                                spacing: 8
                                anchors.centerIn: parent
                                Label {
                                    anchors.verticalCenter: parent.verticalCenter
                                    text: !compareRunBtn.dt ? "Select both runs"
                                        : compareRunBtn.dt.comparing
                                            ? "Comparing…"
                                            : compareRunBtn.enabled
                                                ? "Run DTW Comparison"
                                                : (compareRunBtn.masterMissing ? "No master for this type"
                                                    : compareRunBtn.sameFile ? "Choose different runs"
                                                    : compareRunBtn.gateError !== "" ? "Wafer family mismatch"
                                                    : "Select both runs")
                                    color: compareRunBtn.enabled ? Theme.tone100 : Theme.sideMutedText
                                    font.pixelSize: Theme.fontSm
                                    font.bold: true
                                }
                                BusyIndicator {
                                    running: !!compareRunBtn.dt && compareRunBtn.dt.comparing
                                    visible: !!compareRunBtn.dt && compareRunBtn.dt.comparing
                                    width: 14; height: 14
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                            }
                        }
                    }
                }

                // ── BOX 2: Context-Sensitive Rail Panel ────────────────────
                StackLayout {
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    Layout.minimumHeight: 120
                    currentIndex: root.selectedTabIndex === 0 ? 0 : 1

                    // ── Status tab: Hardware Actions + Log Actions ──────────
                    StatusActionsPanel {
                        Layout.fillWidth: true
                        Layout.fillHeight: true
                    }

                    // ── Map tab: Source file browser ──────────────
                    Rectangle {
                        radius: Theme.sidePanelRadius
                        border.color: Theme.sideBorder
                        border.width: 1
                        color: Theme.sidePanelBackground

                        SourcePanel {
                            anchors.fill: parent
                            anchors.margins: 14
                            selectedTabIndex: root.selectedTabIndex
                            dataTab: dataTabLoader.item
                        }
                    }
                }

                // ── BOX 3: Hardware Status Footer ──────────────────────────
                ConnectionFooter {
                    Layout.fillWidth: true
                }

                // ── BOX 4: Utility Buttons ─────────────────────────────────
                UtilityFooter {
                    id: utilFooter
                    Layout.fillWidth: true
                    settingsPopup: settingsPopup
                    aboutDialog: aboutDialog
                }
            }
        }

        // ── WORKSPACE ─────────────────────────────────────────────────────
        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Theme.workspaceBackground

            StackLayout {
                anchors.fill: parent
                anchors.margins: Theme.mainAreaPadding
                currentIndex: root.selectedTabIndex

                Loader {
                    id: statusTabLoader
                    source: "Tabs/StatusTab.qml"
                    active: StackLayout.index === root.selectedTabIndex
                }
                Loader {
                    id: dataTabLoader
                    source: "Tabs/DataTab.qml"
                    active: StackLayout.index === root.selectedTabIndex
                    // Component is still recreated lazily on every tab switch
                    // (memory-cheap, matches Status/Map/Graph) — what survives
                    // is just the two filenames (root.dataCompareFileA/B),
                    // restored here so Run A/B picks and their two-color
                    // highlight aren't lost on return. Computed DTW results
                    // are NOT re-run automatically — re-fetching is a real
                    // (if fast) backend call, so the user re-clicks "Run DTW
                    // Comparison" rather than it silently firing on tab entry.
                    onLoaded: {
                        item.compareFileA = root.dataCompareFileA
                        item.compareFileB = root.dataCompareFileB
                        item.activeBox = root.dataCompareFileB !== "" ? 0
                            : root.dataCompareFileA !== "" ? 2 : 1
                    }
                }
                Loader {
                    source: "Tabs/WaferMapTab.qml"
                    active: StackLayout.index === root.selectedTabIndex && licenseModel.licenses.length > 0
                }
                Loader {
                    source: "Tabs/GraphTab.qml"
                    active: StackLayout.index === root.selectedTabIndex
                }
            }
        }
    }
}
