(function () {
    'use strict';

    var state = {
        data: [],
        endpoint: null,
        flexmonster: null,
        librariesAttempted: false,
        pivotInitialised: false
    };

    var CDN = {
        d3: 'https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js',
        c3: 'https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.js',
        plotly: 'https://cdn.plot.ly/plotly-2.35.2.min.js',
        html2canvas: 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js',
        jspdf: 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js',
        qrious: 'https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js'
    };

    function onReady(callback) {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', callback);
        } else {
            callback();
        }
    }

    function assetUrl(path) {
        path = String(path || '').replace(/^\/+/, '');
        var base = (window.NGBVD_REPORT_CONFIG && window.NGBVD_REPORT_CONFIG.assetBase) || '';
        return base ? base.replace(/\/+$/, '') + '/' + path : '/' + path;
    }

    function loadScriptOnce(src, testFn) {
        return new Promise(function (resolve) {
            try {
                if (typeof testFn === 'function' && testFn()) { resolve(true); return; }
                var existing = document.querySelector('script[data-ngbvd-src="' + src + '"]') || document.querySelector('script[src="' + src + '"]');
                if (existing) {
                    existing.addEventListener('load', function(){ resolve(true); });
                    existing.addEventListener('error', function(){ resolve(false); });
                    setTimeout(function(){ resolve(typeof testFn === 'function' ? !!testFn() : true); }, 1500);
                    return;
                }
                var script = document.createElement('script');
                script.src = src;
                script.async = false;
                script.setAttribute('data-ngbvd-src', src);
                script.onload = function () { resolve(typeof testFn === 'function' ? !!testFn() : true); };
                script.onerror = function () { resolve(false); };
                document.head.appendChild(script);
            } catch (e) { resolve(false); }
        });
    }

    function safeText(value) {
        if (value === null || value === undefined) { return ''; }
        return String(value).replace(/\s+/g, ' ').trim();
    }

    function escapeHtml(value) {
        return safeText(value).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
    }

    function feedback(message, type) {
        type = type || 'info';
        var box = document.getElementById('pivotjs-export-feedback') || document.querySelector('.ngbvd-export-feedback');
        if (!box) {
            if (type === 'danger' || type === 'warning') { window.alert(message); }
            return;
        }
        box.innerHTML = '<div class="alert alert-' + type + '" role="alert">' + escapeHtml(message) + '</div>';
        if (type !== 'danger') { setTimeout(function(){ if (box) { box.innerHTML = ''; } }, 6000); }
    }

    function normalisePayload(payload) {
        if (Array.isArray(payload)) { return payload; }
        if (payload && Array.isArray(payload.data)) { return payload.data; }
        if (payload && payload.data && Array.isArray(payload.data.records)) { return payload.data.records; }
        if (payload && Array.isArray(payload.records)) { return payload.records; }
        return [];
    }

    function readLegacyDataset() {
        if (Array.isArray(window.analysisData)) { return window.analysisData; }
        if (Array.isArray(window.pivotData)) { return window.pivotData; }
        if (Array.isArray(window.ngbvdAnalysisData)) { return window.ngbvdAnalysisData; }
        var raw = document.getElementById('pivotdata') || document.getElementById('analysisData') || document.querySelector('[data-analysis-json]');
        if (raw) {
            try { return normalisePayload(JSON.parse(raw.value || raw.textContent || raw.getAttribute('data-analysis-json') || '[]')); }
            catch (e) { return []; }
        }
        return [];
    }

    function getEndpoint() {
        var workbench = document.querySelector('[data-report-workbench]');
        if (workbench && workbench.getAttribute('data-endpoint')) { return workbench.getAttribute('data-endpoint'); }
        if (window.NGBVD_REPORT_CONFIG && window.NGBVD_REPORT_CONFIG.endpoint) { return window.NGBVD_REPORT_CONFIG.endpoint; }
        var legacyUrl = document.getElementById('urlconfig');
        if (legacyUrl && legacyUrl.value) { return legacyUrl.value; }
        return null;
    }

    function fetchAnalysisData() {
        state.endpoint = getEndpoint();
        if (!state.endpoint) {
            state.data = readLegacyDataset();
            return Promise.resolve(state.data);
        }
        return fetch(state.endpoint, { headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'same-origin' })
            .then(function (response) {
                if (!response.ok) { throw new Error('Analysis data endpoint returned HTTP ' + response.status); }
                return response.json();
            })
            .then(function (payload) { state.data = normalisePayload(payload); return state.data; })
            .catch(function () { state.data = readLegacyDataset(); return state.data; });
    }

    function loadPivotGraphRenderers() {
        if (state.librariesAttempted) { return Promise.resolve(); }
        state.librariesAttempted = true;
        var tasks = [];
        tasks.push(loadScriptOnce(CDN.d3, function(){ return !!window.d3; })
            .then(function(){ return loadScriptOnce(CDN.c3, function(){ return !!window.c3; }); })
            .then(function(){
                if (window.c3 && window.d3 && window.jQuery && window.jQuery.pivotUtilities) {
                    return loadScriptOnce(assetUrl('pivotjs/c3_renderers.js'), function(){ return !!(window.jQuery.pivotUtilities && window.jQuery.pivotUtilities.c3_renderers); });
                }
                return false;
            }));
        tasks.push(loadScriptOnce(CDN.plotly, function(){ return !!window.Plotly; })
            .then(function(){
                if (window.Plotly && window.jQuery && window.jQuery.pivotUtilities) {
                    return loadScriptOnce(assetUrl('pivotjs/plotly_renderers.js'), function(){ return !!(window.jQuery.pivotUtilities && window.jQuery.pivotUtilities.plotly_renderers); });
                }
                return false;
            }));
        return Promise.all(tasks).then(function(){ return true; });
    }

    function collectPivotRenderers() {
        if (!window.jQuery || !window.jQuery.pivotUtilities) { return {}; }
        var utils = window.jQuery.pivotUtilities;
        var renderers = window.jQuery.extend({}, utils.renderers || {}, utils.export_renderers || {});
        if (utils.c3_renderers) { renderers = window.jQuery.extend(renderers, utils.c3_renderers); }
        if (utils.plotly_renderers) { renderers = window.jQuery.extend(renderers, utils.plotly_renderers); }
        return renderers;
    }

    function fieldExists(rows, field) { return rows.length > 0 && Object.prototype.hasOwnProperty.call(rows[0], field); }
    function firstExistingField(rows, fields) { for (var i=0;i<fields.length;i++) { if (fieldExists(rows, fields[i])) { return fields[i]; } } return null; }

    function initPivotJS(rows) {
        var container = window.jQuery ? window.jQuery('#pivotjsContainer') : null;
        if (!container || !container.length) { preparePivotUiForExport(); return; }
        if (!window.jQuery.fn || typeof window.jQuery.fn.pivotUI !== 'function' || !window.jQuery.pivotUtilities) {
            feedback('PivotJS library is not available. Confirm pivot.js is loaded.', 'warning'); return;
        }
        if (!rows.length) { feedback('No records are available for the current analysis scope.', 'warning'); return; }

        loadPivotGraphRenderers().then(function(){
            try {
                var incidentField = firstExistingField(rows, ['Incident', 'Incident Type', 'Category']);
                var dateField = firstExistingField(rows, ['Interview Date', 'Incident Date', 'Incident Year']);
                var genderField = firstExistingField(rows, ['Gender', 'Sex']);
                var config = {
                    renderers: collectPivotRenderers(),
                    rendererName: 'Table',
                    aggregatorName: 'Count',
                    rows: [],
                    cols: [],
                    unusedAttrsVertical: 500,
                    onRefresh: function () { preparePivotUiForExport(); }
                };
                if (incidentField) { config.rows.push(incidentField); }
                if (dateField && !incidentField) { config.rows.push(dateField); }
                if (genderField) { config.cols.push(genderField); }
                container.empty().pivotUI(rows, config, true);
                state.pivotInitialised = true;
                preparePivotUiForExport();
                feedback('PivotJS is ready. Use the renderer dropdown to select tables, heatmaps and available charts.', 'success');
            } catch (error) { feedback('Unable to render PivotJS: ' + (error.message || error), 'danger'); }
        });
    }

    function reloadPivotJs(evt) {
        stop(evt);
        var rows = state.data.length ? state.data : readLegacyDataset();
        if (!rows.length) { fetchAnalysisData().then(initPivotJS); } else { initPivotJS(rows); }
        return false;
    }

    function getPivotHolder() {
        return document.querySelector('#pivotjsContainer') || document.querySelector('#output') || document.querySelector('.pivotHolder') || document.querySelector('.pvtUi');
    }
    function getRendererArea() {
        var holder = getPivotHolder();
        if (!holder) { return null; }
        return holder.querySelector('.pvtRendererArea') || holder.querySelector('table.pvtTable') || holder.querySelector('.plotly') || holder.querySelector('.c3') || holder;
    }

    function preparePivotUiForExport() {
        var holder = getPivotHolder(); if (!holder) { return; }
        holder.querySelectorAll('select').forEach(function (select) { if (!select.classList.contains('form-control')) { select.classList.add('form-control'); } });
        var table = holder.querySelector('table.pvtTable'); if (table) { table.setAttribute('data-current-pivot-table','true'); }
    }

    function findVisiblePivotTable() {
        var candidates = ['#pivotjsContainer table.pvtTable', '#output table.pvtTable', '.pivotHolder table.pvtTable', 'table.pvtTable'];
        for (var i=0;i<candidates.length;i++) {
            var tables = document.querySelectorAll(candidates[i]);
            for (var j=0;j<tables.length;j++) { if (tables[j].offsetParent !== null || tables[j].getClientRects().length > 0) { return tables[j]; } }
        }
        return null;
    }

    function tableToMatrix(table) {
        var matrix=[], occupied=[];
        Array.prototype.forEach.call(table.rows, function(row, rowIndex){
            matrix[rowIndex] = matrix[rowIndex] || []; occupied[rowIndex] = occupied[rowIndex] || [];
            var colIndex=0;
            Array.prototype.forEach.call(row.cells, function(cell){
                while (occupied[rowIndex][colIndex]) { colIndex++; }
                var text=safeText(cell.textContent); var rowSpan=parseInt(cell.getAttribute('rowspan')||'1',10); var colSpan=parseInt(cell.getAttribute('colspan')||'1',10);
                for (var r=0;r<rowSpan;r++) { matrix[rowIndex+r]=matrix[rowIndex+r]||[]; occupied[rowIndex+r]=occupied[rowIndex+r]||[]; for (var c=0;c<colSpan;c++) { matrix[rowIndex+r][colIndex+c]=(r===0&&c===0)?text:''; occupied[rowIndex+r][colIndex+c]=true; } }
                colIndex += colSpan;
            });
        });
        var maxCols = matrix.reduce(function(max,row){return Math.max(max,row.length);},0);
        return matrix.map(function(row){ var clean=[]; for(var i=0;i<maxCols;i++){ clean.push(row[i]||''); } return clean; });
    }

    function datasetToMatrix(rows) {
        if (!rows.length) { return []; }
        var keys = Object.keys(rows[0] || {});
        return [keys].concat(rows.map(function(row){ return keys.map(function(key){ return row[key]; }); }));
    }

    function matrixToCsv(matrix) {
        return matrix.map(function(row){ return row.map(function(value){ value=safeText(value); return /[",\r\n]/.test(value) ? '"' + value.replace(/"/g,'""') + '"' : value; }).join(','); }).join('\r\n');
    }

    function timestamp() { var d=new Date(), p=function(v){return String(v).padStart(2,'0');}; return d.getFullYear()+p(d.getMonth()+1)+p(d.getDate())+'_'+p(d.getHours())+p(d.getMinutes())+p(d.getSeconds()); }
    function downloadText(filename, content, mimeType) { var blob=new Blob([content],{type:mimeType||'text/csv;charset=utf-8;'}); downloadBlob(filename, blob); }
    function downloadBlob(filename, blob) { var url=URL.createObjectURL(blob); var link=document.createElement('a'); link.href=url; link.download=filename; document.body.appendChild(link); link.click(); setTimeout(function(){ document.body.removeChild(link); URL.revokeObjectURL(url); },100); }

    function exportPivotCsv(evt) {
        stop(evt);
        var table=findVisiblePivotTable();
        if (!table) { feedback('No generated PivotJS table was found. Generate a table first, then export.', 'warning'); return false; }
        var matrix=tableToMatrix(table);
        if (!matrix.length) { feedback('The current PivotJS table is empty.', 'warning'); return false; }
        downloadText('ngbvd-pivot-table-'+timestamp()+'.csv', matrixToCsv([['NGBVD PivotJS Export'],['Generated Pivot Table'],['Exported At', new Date().toISOString()],[]].concat(matrix)), 'text/csv;charset=utf-8;');
        feedback('Pivot CSV exported successfully.', 'success');
        return false;
    }

    function exportDatasetCsv(evt) {
        stop(evt);
        var rows = state.data.length ? state.data : readLegacyDataset();
        if (!rows.length) { feedback('No dataset is available to export.', 'warning'); return false; }
        downloadText('ngbvd-analysis-dataset-'+timestamp()+'.csv', matrixToCsv(datasetToMatrix(rows)), 'text/csv;charset=utf-8;');
        feedback('Dataset CSV exported successfully.', 'success');
        return false;
    }

    function ensureCaptureLibraries() {
        return loadScriptOnce(CDN.html2canvas, function(){ return typeof window.html2canvas === 'function'; })
            .then(function(ok1){ return loadScriptOnce(CDN.jspdf, function(){ return !!(window.jspdf && window.jspdf.jsPDF); }).then(function(ok2){ return ok1 && ok2; }); });
    }

    function ensureQrLibrary() {
        if (window.QRious) {
            return Promise.resolve(true);
        }

        return loadScriptOnce(CDN.qrious, function(){ return !!window.QRious; });
    }

    function authenticityUrl() {
        var url = window.location.href.split('#')[0];
        var separator = url.indexOf('?') === -1 ? '?' : '&';
        return url + separator + 'verify_export=1';
    }

    function makeQrCanvas(value, size) {
        size = size || 132;
        var canvas = document.createElement('canvas');
        canvas.width = size;
        canvas.height = size;

        if (window.QRious) {
            try {
                new window.QRious({
                    element: canvas,
                    value: value || authenticityUrl(),
                    size: size,
                    level: 'H',
                    background: 'white',
                    foreground: '#0b1f3a'
                });
                return canvas;
            } catch (e) {}
        }

        var ctx = canvas.getContext('2d');
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, size, size);
        ctx.strokeStyle = '#0b1f3a';
        ctx.lineWidth = 4;
        ctx.strokeRect(3, 3, size - 6, size - 6);
        ctx.fillStyle = '#0b1f3a';
        ctx.font = 'bold 18px Arial, sans-serif';
        ctx.textAlign = 'center';
        ctx.fillText('QR', size / 2, size / 2);
        return canvas;
    }

    function exportCss() {
        return [
            '.ngbvd-export-capture-area{background:#ffffff!important;color:#0f172a!important;opacity:1!important;filter:none!important;text-shadow:none!important;}',
            '.ngbvd-export-capture-area *{opacity:1!important;filter:none!important;text-shadow:none!important;}',
            '.ngbvd-export-capture-area table{border-collapse:collapse!important;background:#ffffff!important;color:#0f172a!important;}',
            '.ngbvd-export-capture-area .pvtTable{font-size:24px!important;line-height:1.45!important;background:#ffffff!important;color:#0f172a!important;}',
            '.ngbvd-export-capture-area .pvtTable th,.ngbvd-export-capture-area .pvtTable td{color:#0f172a!important;-webkit-text-fill-color:#0f172a!important;background:#ffffff!important;border:2px solid #94a3b8!important;font-weight:800!important;padding:12px 16px!important;}',
            '.ngbvd-export-capture-area .pvtTable th,.ngbvd-export-capture-area .pvtAxisLabel,.ngbvd-export-capture-area .pvtColLabel,.ngbvd-export-capture-area .pvtRowLabel{background:#e8eef3!important;color:#0b1f3a!important;-webkit-text-fill-color:#0b1f3a!important;font-weight:800!important;}',
            '.ngbvd-export-capture-area .pvtTotal,.ngbvd-export-capture-area .pvtGrandTotal{background:#fff3cd!important;color:#0b1f3a!important;-webkit-text-fill-color:#0b1f3a!important;font-weight:900!important;}',
            '.ngbvd-export-capture-area svg text{fill:#0f172a!important;opacity:1!important;font-weight:700!important;font-size:18px!important;}',
            '.ngbvd-export-capture-area .c3 text,.ngbvd-export-capture-area .plot-container text{fill:#0f172a!important;color:#0f172a!important;opacity:1!important;}',
            '.ngbvd-export-capture-area canvas{background:#ffffff!important;}'
        ].join('\n');
    }

    function getBrandContext() {
        var title = 'National Gender Based Violence Database';
        var subtitle = 'Ministry of Gender, Labour and Social Development';
        var report = 'PivotJS Analysis Export';

        var heading = document.querySelector('.ngbvd-page-title, .page-title, h1, h2');
        if (heading && safeText(heading.textContent)) {
            report = safeText(heading.textContent);
        }

        var activeTab = document.querySelector('.nav-link.active, .ngbvd-tab.active, [data-toggle="tab"].active, .btn.active');
        if (activeTab && safeText(activeTab.textContent)) {
            report = safeText(activeTab.textContent) + ' - PivotJS Analysis Export';
        }

        var user = '';
        var userBox = document.querySelector('.ngbvd-user-name, .user-name, .profile-name, .signed-in-user');
        if (userBox && safeText(userBox.textContent)) {
            user = safeText(userBox.textContent);
        }

        return {
            title: title,
            subtitle: subtitle,
            report: report,
            user: user,
            exportedAt: new Date().toLocaleString()
        };
    }

    function roundRect(ctx, x, y, width, height, radius) {
        radius = Math.min(radius || 0, width / 2, height / 2);
        ctx.beginPath();
        ctx.moveTo(x + radius, y);
        ctx.arcTo(x + width, y, x + width, y + height, radius);
        ctx.arcTo(x + width, y + height, x, y + height, radius);
        ctx.arcTo(x, y + height, x, y, radius);
        ctx.arcTo(x, y, x + width, y, radius);
        ctx.closePath();
    }

    function cropCanvasToContent(sourceCanvas, options) {
        options = options || {};
        var tolerance = options.tolerance || 248;
        var padding = options.padding || 24;
        var minWidth = options.minWidth || 360;
        var minHeight = options.minHeight || 220;
        var ctx = sourceCanvas.getContext('2d');
        var width = sourceCanvas.width;
        var height = sourceCanvas.height;

        try {
            var pixels = ctx.getImageData(0, 0, width, height).data;
            var minX = width, minY = height, maxX = 0, maxY = 0;
            var found = false;

            function isContent(index) {
                var r = pixels[index], g = pixels[index + 1], b = pixels[index + 2], a = pixels[index + 3];
                if (a < 8) { return false; }

                // Treat white / very light grey as background. Everything else is report content.
                return !(r >= tolerance && g >= tolerance && b >= tolerance);
            }

            var step = Math.max(1, Math.floor(Math.min(width, height) / 900));
            for (var y = 0; y < height; y += step) {
                for (var x = 0; x < width; x += step) {
                    var i = (y * width + x) * 4;
                    if (isContent(i)) {
                        found = true;
                        if (x < minX) { minX = x; }
                        if (x > maxX) { maxX = x; }
                        if (y < minY) { minY = y; }
                        if (y > maxY) { maxY = y; }
                    }
                }
            }

            if (!found) {
                return sourceCanvas;
            }

            minX = Math.max(0, minX - padding);
            minY = Math.max(0, minY - padding);
            maxX = Math.min(width, maxX + padding);
            maxY = Math.min(height, maxY + padding);

            var cropWidth = Math.max(minWidth, maxX - minX);
            var cropHeight = Math.max(minHeight, maxY - minY);

            // Do not return a crop bigger than the source.
            cropWidth = Math.min(cropWidth, width - minX);
            cropHeight = Math.min(cropHeight, height - minY);

            var output = document.createElement('canvas');
            output.width = cropWidth;
            output.height = cropHeight;
            var out = output.getContext('2d');
            out.fillStyle = '#ffffff';
            out.fillRect(0, 0, cropWidth, cropHeight);
            out.drawImage(sourceCanvas, minX, minY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
            return output;
        } catch (e) {
            return sourceCanvas;
        }
    }

    function findActualPivotOutputElement() {
        var holder = getRendererArea();
        if (!holder) {
            return null;
        }

        var selectors = [
            'table.pvtTable',
            '.js-plotly-plot',
            '.plot-container',
            '.c3',
            'svg',
            'canvas'
        ];

        for (var i = 0; i < selectors.length; i++) {
            var nodes = holder.querySelectorAll(selectors[i]);
            for (var j = 0; j < nodes.length; j++) {
                var node = nodes[j];
                var rect = node.getBoundingClientRect();
                if (rect.width > 40 && rect.height > 40) {
                    return node;
                }
            }
        }

        return holder;
    }

    function buildBrandedCanvas(capturedCanvas) {
        capturedCanvas = cropCanvasToContent(capturedCanvas, { padding: 28, tolerance: 248, minWidth: 420, minHeight: 240 });

        var brand = getBrandContext();
        var contentRatio = capturedCanvas.width / Math.max(1, capturedCanvas.height);
        var padding = 52;
        var headerHeight = 156;
        var footerHeight = 76;

        /*
         * Fit the export to the actual pivot output, not to the full browser/container width.
         * This removes the large white empty space seen on graph exports.
         */
        var maxCanvasWidth = contentRatio > 1.35 ? 1800 : 1280;
        var minCanvasWidth = contentRatio > 1.35 ? 980 : 760;
        var naturalWidth = capturedCanvas.width + (padding * 2);
        var canvasWidth = Math.max(minCanvasWidth, Math.min(maxCanvasWidth, naturalWidth));
        var availableWidth = canvasWidth - (padding * 2);

        var scale = Math.min(1, availableWidth / capturedCanvas.width);
        var renderedWidth = Math.floor(capturedCanvas.width * scale);
        var renderedHeight = Math.floor(capturedCanvas.height * scale);

        var contentX = padding + Math.floor((availableWidth - renderedWidth) / 2);
        var contentY = headerHeight + 28;
        var canvasHeight = headerHeight + renderedHeight + footerHeight + 64;

        var canvas = document.createElement('canvas');
        canvas.width = canvasWidth;
        canvas.height = canvasHeight;

        var ctx = canvas.getContext('2d');
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = 'high';

        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Header
        ctx.fillStyle = '#0b1f3a';
        ctx.fillRect(0, 0, canvas.width, headerHeight);
        ctx.fillStyle = '#f9a602';
        ctx.fillRect(0, headerHeight - 10, canvas.width, 10);

        // Brand mark
        ctx.fillStyle = '#f9a602';
        roundRect(ctx, padding, 28, 58, 58, 13);
        ctx.fill();
        ctx.fillStyle = '#0b1f3a';
        ctx.font = 'bold 26px Arial, sans-serif';
        ctx.textAlign = 'center';
        ctx.fillText('N', padding + 29, 67);

        ctx.textAlign = 'left';
        ctx.fillStyle = '#ffffff';
        ctx.font = 'bold 24px Arial, sans-serif';
        ctx.fillText(brand.title, padding + 76, 44);

        ctx.font = '15px Arial, sans-serif';
        ctx.fillStyle = '#dbe7f3';
        ctx.fillText(brand.subtitle, padding + 76, 70);

        ctx.font = 'bold 16px Arial, sans-serif';
        ctx.fillStyle = '#ffffff';
        ctx.fillText(brand.report, padding + 76, 100);

        // Authenticity QR
        var qrSize = 74;
        var qrX = canvas.width - padding - qrSize;
        var qrY = 26;
        var qrCanvas = makeQrCanvas(authenticityUrl(), qrSize);
        ctx.fillStyle = '#ffffff';
        roundRect(ctx, qrX - 6, qrY - 6, qrSize + 12, qrSize + 12, 8);
        ctx.fill();
        ctx.drawImage(qrCanvas, qrX, qrY, qrSize, qrSize);

        ctx.textAlign = 'right';
        ctx.font = '12px Arial, sans-serif';
        ctx.fillStyle = '#dbe7f3';
        ctx.fillText('Generated: ' + brand.exportedAt, qrX - 16, 48);
        if (brand.user) {
            ctx.fillText('User: ' + brand.user, qrX - 16, 68);
        }
        ctx.font = 'bold 11px Arial, sans-serif';
        ctx.fillStyle = '#ffffff';
        ctx.fillText('Scan to verify', qrX - 16, 92);

        // Content card - tight around actual output
        ctx.fillStyle = '#ffffff';
        ctx.shadowColor = 'rgba(15, 23, 42, 0.14)';
        ctx.shadowBlur = 14;
        ctx.shadowOffsetY = 5;
        roundRect(ctx, contentX - 14, contentY - 14, renderedWidth + 28, renderedHeight + 28, 14);
        ctx.fill();
        ctx.shadowColor = 'transparent';
        ctx.shadowBlur = 0;
        ctx.shadowOffsetY = 0;

        ctx.strokeStyle = '#cbd5e1';
        ctx.lineWidth = 1.5;
        roundRect(ctx, contentX - 14, contentY - 14, renderedWidth + 28, renderedHeight + 28, 14);
        ctx.stroke();

        ctx.drawImage(capturedCanvas, contentX, contentY, renderedWidth, renderedHeight);

        // Footer
        var footerTop = contentY + renderedHeight + 28;
        ctx.fillStyle = '#f8fafc';
        ctx.fillRect(0, footerTop, canvas.width, footerHeight);
        ctx.fillStyle = '#334155';
        ctx.font = '13px Arial, sans-serif';
        ctx.textAlign = 'left';
        ctx.fillText('Data visibility follows the signed-in user’s assigned data scope.', padding, footerTop + 28);
        ctx.font = '11px Arial, sans-serif';
        ctx.fillStyle = '#64748b';
        ctx.fillText('Verification URL: ' + authenticityUrl(), padding, footerTop + 52);
        ctx.fillStyle = '#334155';
        ctx.font = 'bold 13px Arial, sans-serif';
        ctx.textAlign = 'right';
        ctx.fillText('Confidential - authorised use only', canvas.width - padding, footerTop + 28);

        return canvas;
    }

    function capturePivotArea(callback) {
        ensureCaptureLibraries().then(function(ok){
            if (!ok) { feedback('Image/PDF export libraries could not load. Check internet access or host html2canvas and jsPDF locally.', 'danger'); return; }
            var target=getRendererArea();
            if (!target) { feedback('No generated PivotJS output was found to export.', 'warning'); return; }

            target.classList.add('ngbvd-export-capture-area');

            var previousInline = {
                background: target.style.background,
                color: target.style.color,
                opacity: target.style.opacity,
                filter: target.style.filter
            };

            target.style.background = '#ffffff';
            target.style.color = '#0f172a';
            target.style.opacity = '1';
            target.style.filter = 'none';

            window.html2canvas(target, {
                backgroundColor:'#ffffff',
                scale:4,
                useCORS:true,
                allowTaint:false,
                logging:false,
                scrollX:0,
                scrollY:-window.scrollY,
                windowWidth:Math.max(target.scrollWidth + 80, target.getBoundingClientRect().width + 80),
                windowHeight:Math.max(target.scrollHeight + 80, target.getBoundingClientRect().height + 80),
                onclone:function(clonedDocument){
                    var style = clonedDocument.createElement('style');
                    style.type = 'text/css';
                    style.appendChild(clonedDocument.createTextNode(exportCss()));
                    clonedDocument.head.appendChild(style);
                }
            })
                .then(function(canvas){
                    target.classList.remove('ngbvd-export-capture-area');
                    target.style.background = previousInline.background;
                    target.style.color = previousInline.color;
                    target.style.opacity = previousInline.opacity;
                    target.style.filter = previousInline.filter;
                    callback(canvas);
                })
                .catch(function(error){
                    target.classList.remove('ngbvd-export-capture-area');
                    target.style.background = previousInline.background;
                    target.style.color = previousInline.color;
                    target.style.opacity = previousInline.opacity;
                    target.style.filter = previousInline.filter;
                    feedback('The PivotJS output could not be captured: '+(error.message||error), 'danger');
                });
        });
    }

    function exportPivotImage(evt) {
        stop(evt);
        ensureQrLibrary().then(function(){
            capturePivotArea(function(canvas){
                var brandedCanvas = buildBrandedCanvas(canvas);
                brandedCanvas.toBlob(function(blob){
                    if(!blob){ feedback('Image export failed.', 'danger'); return; }
                    downloadBlob('ngbvd-branded-pivot-output-'+timestamp()+'.png', blob);
                    feedback('Branded Pivot image exported successfully.', 'success');
                }, 'image/png', 1);
            });
        });
        return false;
    }

    function exportPivotPdf(evt) {
        stop(evt);
        ensureQrLibrary().then(function(){
            capturePivotArea(function(canvas){
                var brandedCanvas = buildBrandedCanvas(canvas);
                var jsPDF=window.jspdf.jsPDF;

                var orientation = brandedCanvas.width >= brandedCanvas.height ? 'landscape' : 'portrait';
                var pdf=new jsPDF({ orientation: orientation, unit:'pt', format:'a4', compress:true });
                var pageWidth=pdf.internal.pageSize.getWidth();
                var pageHeight=pdf.internal.pageSize.getHeight();
                var margin=16;
                var usableWidth=pageWidth-margin*2;
                var usableHeight=pageHeight-margin*2;

                /*
                 * Fit to A4 width. If too tall, paginate vertically. This avoids
                 * overstretched landscape exports with massive white space.
                 */
                var imgWidth=usableWidth;
                var imgHeight=brandedCanvas.height*imgWidth/brandedCanvas.width;

                if (imgHeight <= usableHeight) {
                    var y = margin + Math.max(0, (usableHeight - imgHeight) / 2);
                    pdf.addImage(brandedCanvas.toDataURL('image/png', 1),'PNG',margin,y,imgWidth,imgHeight, undefined, 'FAST');
                } else {
                    var sliceHeightPx=Math.floor((usableHeight/imgWidth)*brandedCanvas.width);
                    var position=0;
                    var page=0;

                    while(position<brandedCanvas.height) {
                        if(page>0) { pdf.addPage(); }
                        var sliceCanvas=document.createElement('canvas');
                        sliceCanvas.width=brandedCanvas.width;
                        sliceCanvas.height=Math.min(sliceHeightPx, brandedCanvas.height-position);
                        var ctx=sliceCanvas.getContext('2d');
                        ctx.fillStyle='#ffffff';
                        ctx.fillRect(0,0,sliceCanvas.width,sliceCanvas.height);
                        ctx.drawImage(brandedCanvas,0,position,brandedCanvas.width,sliceCanvas.height,0,0,brandedCanvas.width,sliceCanvas.height);
                        pdf.addImage(sliceCanvas.toDataURL('image/png', 1),'PNG',margin,margin,imgWidth,sliceCanvas.height*imgWidth/sliceCanvas.width, undefined, 'FAST');
                        position += sliceHeightPx;
                        page++;
                    }
                }

                pdf.save('ngbvd-branded-pivot-output-'+timestamp()+'.pdf');
                feedback('Branded Pivot PDF exported successfully.', 'success');
            });
        });
        return false;
    }

    function renderGrid(rows) {
        var table=document.getElementById('analysisGridTable'); if(!table || !rows.length){ return; }
        var keys=Object.keys(rows[0]||{}), thead=table.querySelector('thead'), tbody=table.querySelector('tbody'); if(!thead||!tbody){ return; }
        thead.innerHTML='<tr>'+keys.map(function(key){return '<th>'+escapeHtml(key)+'</th>';}).join('')+'</tr>';
        tbody.innerHTML=rows.slice(0,500).map(function(row){return '<tr>'+keys.map(function(key){return '<td>'+escapeHtml(row[key])+'</td>';}).join('')+'</tr>';}).join('');
    }

    function initFlexmonster(rows) {
        var container=document.getElementById('flexmonsterContainer'); if(!container){ return; }
        if(typeof window.Flexmonster!=='function'){ container.innerHTML='<div class="alert alert-info" style="margin:16px;">Flexmonster is not available. Use PivotJS, Grid Data or Visual Summary.</div>'; return; }
        try { state.flexmonster=new window.Flexmonster({ container:'flexmonsterContainer', toolbar:true, componentFolder:'/flexmonster/', licenseKey:(window.NGBVD_REPORT_CONFIG&&window.NGBVD_REPORT_CONFIG.flexmonsterLicense)||'', report:{ dataSource:{ data:rows }, options:{ grid:{ type:'flat', showHeaders:true } } } }); }
        catch(error){ container.innerHTML='<div class="alert alert-warning" style="margin:16px;">Flexmonster could not be initialized. Use PivotJS or Grid Data instead.</div>'; }
    }

    function initWorkbench() {
        bindActions();
        fetchAnalysisData().then(function(rows){
            var modern=!!document.querySelector('[data-report-workbench]');
            if(modern) { initFlexmonster(rows); initPivotJS(rows); renderGrid(rows); }
            else { preparePivotUiForExport(); }
        });
    }

    function bindActions() {
        document.addEventListener('click', function(event){
            var clicked=event.target.closest ? event.target.closest('[data-report-action], a.rawasexcel, .rawasexcel') : null;
            if(!clicked){ return true; }
            var action=clicked.getAttribute('data-report-action');
            if(!action && (clicked.classList.contains('rawasexcel') || clicked.matches('a.rawasexcel'))) { action='export-pivot-csv'; }
            var map={ 'export-pivot-table':exportPivotCsv, 'export-pivot-csv':exportPivotCsv, 'export-pivot-pdf':exportPivotPdf, 'export-pivot-image':exportPivotImage, 'export-dataset-csv':exportDatasetCsv, 'export-grid':exportDatasetCsv, 'reload-pivotjs':reloadPivotJs };
            if(map[action]) { return map[action](event); }
            return true;
        }, true);
    }

    function stop(evt) { if(evt){ evt.preventDefault(); evt.stopPropagation(); if(typeof evt.stopImmediatePropagation==='function'){ evt.stopImmediatePropagation(); } } }

    window.NGBVDPivotExport = {
        reloadPivotJs: reloadPivotJs,
        exportPivotCsv: exportPivotCsv,
        exportPivotPdf: exportPivotPdf,
        exportPivotImage: exportPivotImage,
        exportDatasetCsv: exportDatasetCsv,
        _state: state
    };

    onReady(initWorkbench);
})();
