(function() {
    'use strict';
    // window.app is initialized in init.js. Reference shape (read-only docs):
    //   directories[], groupingFolders[], transmittalFolders[], files[],
    //   filteredFiles[], selectedFiles:Set, sourceMode ('local'|'http'),
    //   isScanning, scanProgress,
    //   columnFilters {trackingNumber,title,revisions}, columnFilterASTs {...},
    //   groupingFilter, transmittalFilter,
    //   enabledFolderTypes:Set('issued','received'),
    //   sortField ('trackingNumber'), sortDirection ('asc'|'desc'),
    //   selectedGroupingFolders:Set, selectedTransmittalFolders:Set,
    //   collapsedDateGroups:Set, collapsedGroupingFolders:Set,
    //   selectAllGroupingFolders:bool, selectAllTransmittals:bool,
    //   availableModifiers:Set, selectedModifiers:Set, showSelectedOnly:bool

    // Parse search terms from filter string
    function parseSearchTerms(filter) {
        if (!filter || !filter.trim()) return [];
        return filter.trim().toLowerCase().split(/\s+/);
    }

    // Check if text matches all search terms (AND logic)
    function matchesSearchTerms(text, terms) {
        if (!terms || terms.length === 0) return true;
        return terms.every(term => text.includes(term));
    }

    // Initialize application
    function initApp() {
        // Detect source mode from protocol
        window.app.sourceMode = (location.protocol === 'file:') ? 'local' : 'http';
    
        if (window.app.sourceMode === 'local') {
            // Check File System Access API support (local mode only)
            if (!('showDirectoryPicker' in window)) {
                showUnsupportedBrowserMessage();
                return;
            }
        }
    
        // Set up event listeners
        window.app.modules.events.setupEventListeners();
    
        // Set up file link handlers (event delegation)
        window.app.modules.table.setupFileLinkHandlers();
    
        // Apply source-mode-specific UI adjustments
        applySourceModeUI();
    
        // Restore filter/sort state from URL query string
        window.app.modules.urlState.restore();
    
        // Initialize UI
        updateUI();
    
        // Show initial sort indicator
        window.app.modules.table.updateSortIndicators();
    
        if (window.app.sourceMode === 'http') {
            // Auto-connect to the server in HTTP mode
            autoConnectHttpSource();
        } else {
            // Show empty state if no directories (local mode)
            if (window.app.directories.length === 0) {
                showEmptyState();
            }
        }
    }

    // Apply UI differences based on source mode
    function applySourceModeUI() {
        // "Add Local Directory" button is always visible in both modes —
        // in HTTP mode the user can augment the online archive with local directories.
    }

    // Auto-connect to the HTTP server
    // Derives the base URL from the current page's location.
    //
    // Two URL shapes can serve the archive tool:
    //   (a) Filename-style:   /Project-1/Archive/PartyA/archive.html
    //   (b) Auto-served:      /Project-1/Archive/PartyA/Issued
    //
    // For (a) the URL ends with a filename (`*.html`) and the
    // archive's scan root is the parent directory. For (b) the URL
    // path IS the directory; trying to strip the last segment would
    // wrongly scan the parent. Detect (b) by checking whether the
    // path's last segment looks like a file (has a dot + extension).
    async function autoConnectHttpSource() {
        var href = window.location.href;
        // Strip query string and fragment
        href = href.split('?')[0].split('#')[0];
        var baseUrl;
        if (href.endsWith('/')) {
            // Directory URL, e.g. /Project-1/archive/ or /
            baseUrl = href;
        } else {
            var lastSlash = href.lastIndexOf('/');
            var lastSegment = lastSlash >= 0 ? href.substring(lastSlash + 1) : href;
            // A "filename" has a dot in its last segment. A bare
            // directory name (e.g. "Issued") doesn't. Don't be fooled
            // by dots in domain names — lastSlash is past the host.
            if (lastSegment.indexOf('.') >= 0) {
                baseUrl = href.substring(0, lastSlash + 1);
            } else {
                // Auto-served at a directory URL with no trailing
                // slash. Treat the whole path as the scan root.
                baseUrl = href + '/';
            }
        }

        // Multi-project mode is opt-in via ?projects= in the URL.
        //   ?projects= absent       → not multi-project; scan whatever the URL
        //                             points to (single-project or in-archive
        //                             mode). The server's project list, if any,
        //                             stays out of view.
        //   ?projects= empty        → multi-project; include every project the
        //                             server says the user can access.
        //   ?projects=A,B           → multi-project; include only the listed
        //                             projects (intersected with server access).
        // The archive never sees projects beyond this scope — the visibility
        // dropdown only narrows what's already in availableProjects.
        var urlParams = new URLSearchParams(location.search);
        var projectsParamPresent = urlParams.has('projects');

        if (projectsParamPresent) {
            window.app.isMultiProject = true;

            // Fetch the server's ACL-filtered project list so we can drop any
            // listed names the user doesn't actually have access to (and so
            // the empty-projects= "include everything" mode has a list to use).
            // ProjectInfo carries an optional `title` field sourced from each
            // project's .zddc — capture it so the dropdown can show the
            // human-friendly label instead of the folder name.
            var serverNames = null;
            try {
                var resp = await fetch(baseUrl, { headers: { 'Accept': 'application/json' } });
                if (resp.ok) {
                    var serverProjects = await resp.json();
                    if (Array.isArray(serverProjects) && serverProjects.length > 0
                        && serverProjects[0] && typeof serverProjects[0].name === 'string') {
                        serverNames = new Set(serverProjects.map(function(p) { return p.name; }));
                        var titles = {};
                        serverProjects.forEach(function (p) {
                            if (p && typeof p.title === 'string' && p.title) {
                                titles[p.name] = p.title;
                            }
                        });
                        window.app.projectTitles = titles;
                    }
                }
            } catch (e) {
                // Plain Caddy or proxy-stripped — trust the URL list as-is.
            }

            if (window.app.projectFilter && window.app.projectFilter.size > 0) {
                // Listed names: intersect with server access, warn about misses.
                if (serverNames) {
                    var accessible = new Set();
                    var missing = [];
                    window.app.projectFilter.forEach(function(p) {
                        if (serverNames.has(p)) accessible.add(p);
                        else missing.push(p);
                    });
                    window.app.projectFilter = accessible;
                    if (missing.length > 0) showProjectWarning(missing);
                }
                window.app.availableProjects = Array.from(window.app.projectFilter).sort();
            } else if (serverNames) {
                // Empty ?projects= — scan everything the user can access.
                window.app.projectFilter = new Set(serverNames);
                window.app.availableProjects = Array.from(serverNames).sort();
            }
            // else: ?projects= empty AND no server list — leave projectFilter
            // empty; source.js will fall through to in-archive mode.
        }

        // visibleProjects: default to projectFilter (everything visible), or
        // honor an explicit ?show= from the URL (intersected with projectFilter
        // to drop names that aren't in scope). An empty ?show= means "hide
        // everything" — distinct from "no ?show= at all".
        var showInUrl = urlParams.has('show');
        if (showInUrl) {
            var inScope = new Set();
            (window.app.visibleProjects || new Set()).forEach(function(n) {
                if (window.app.projectFilter.has(n)) inScope.add(n);
            });
            window.app.visibleProjects = inScope;
        } else {
            window.app.visibleProjects = new Set(window.app.projectFilter);
        }

        await addHttpSource(baseUrl);
    }

    // Add an HTTP source root (analogous to addDirectory() for local mode)
    async function addHttpSource(baseUrl) {
        // Derive a display name from the URL path
        var urlPath = baseUrl.replace(/\/$/, '');
        var rootName = urlPath.substring(urlPath.lastIndexOf('/') + 1) || urlPath;
    
        // Check if already added
        var exists = window.app.directories.some(function(d) { return d.url === baseUrl; });
        if (exists) return;
    
        window.app.directories.push({
            handle: null,
            name: rootName,
            path: rootName,
            url: baseUrl
        });
    
        if (window.app.directories.length === 1) {
            hideEmptyState();
        }
    
        await scanHttpSource(baseUrl, rootName);
        updateUI();
    }

    // Scan an HTTP source root
    async function scanHttpSource(baseUrl, rootName) {
        window.app.isScanning = true;
        window.app.scanProgress = 'Connecting to server...';
        updateStatusBar();
    
        var source = window.app.modules.source.createSource('http', { baseUrl: baseUrl });
    
        var fileCount = 0;
        var callbacks = {
            onGroupingFolder: function(folder) {
                window.app.groupingFolders.push(folder);
            },
            onTransmittalFolder: function(folder) {
                window.app.transmittalFolders.push(folder);
            },
            onFile: function(file) {
                window.app.files.push(file);
                fileCount++;
                // Throttled progress update — don't update DOM on every file
                if (fileCount % 10 === 0) {
                    window.app.scanProgress = 'Scanning\u2026 ' + fileCount + ' files found';
                    updateStatusBar();
                }
            },
            onProgress: function() { /* no-op: parallel scan — spinner is enough */ }
        };
    
        try {
            await source.scan(baseUrl, callbacks);
        
            // Auto-select top-level party folders (shallowest depth). Keyed by
            // party NAME so duplicate third-party folders across projects merge.
            var groupingDepths = window.app.groupingFolders.map(function(f) { return f.path.split('/').length; });
            var minGroupingDepth = groupingDepths.length > 0 ? Math.min.apply(null, groupingDepths) : 1;
            window.app.groupingFolders.forEach(function(folder) {
                if (folder.path.split('/').length === minGroupingDepth) {
                    window.app.selectedGroupingFolders.add(folder.name);
                }
            });
        
            window.app.transmittalFolders.forEach(function(folder) {
                if (!isUnderHiddenFolderType(folder.path)) {
                    window.app.selectedTransmittalFolders.add(folder.path);
                }
            });
        
            ensureOutstandingTransmittal();
            // Auto-select Outstanding if selectAllTransmittals is active
            if (window.app.selectAllTransmittals) {
                window.app.selectedTransmittalFolders.add('__outstanding__');
            }
        
            collectModifiers();
            updateUI();
            window.app.modules.filtering.applyFilters();
            if (window.app.modules.presets) {
                window.app.modules.presets.init();
            }
        } catch (err) {
            console.error('Error scanning HTTP source:', err);
            showHttpErrorState(err.message);
        } finally {
            window.app.isScanning = false;
            window.app.scanProgress = '';
            updateStatusBar();
        }
    }

    // Ensure the Outstanding virtual transmittal exists if there are any outstanding files.
    // Called after each scan completes. Idempotent — safe to call multiple times.
    function ensureOutstandingTransmittal() {
        const hasOutstanding = window.app.files.some(f => f.folderPath === '__outstanding__');
        const alreadyExists = window.app.transmittalFolders.some(f => f.path === '__outstanding__');
        if (hasOutstanding && !alreadyExists) {
            window.app.transmittalFolders.push({
                name: 'Outstanding',
                path: '__outstanding__',
                displayPath: 'Outstanding',
                handle: null,
                url: null,
                isVirtual: true
            });
        }
    }

    // Show error state when HTTP server is unreachable
    function showHttpErrorState(message) {
        var el = document.getElementById('noDirectoryMessage');
        if (!el) return;
        var content = el.querySelector('.empty-state__inner');
        if (content) {
            content.innerHTML =
                '<h2>Could not connect to server</h2>' +
                '<p>The archive browser could not retrieve the directory listing from the server.</p>' +
                '<p><strong>Error:</strong> ' + escapeHtml(message || 'Unknown error') + '</p>' +
                '<p>Ensure the server is running, CORS is not blocking the request, and Caddy\'s file browsing is enabled.</p>';
        }
        el.classList.remove('hidden');
    }

    // Show a warning banner listing projects in the URL filter that the user cannot access
    function showProjectWarning(missingProjects) {
        var el = document.getElementById('projectWarningBanner');
        if (!el || missingProjects.length === 0) return;
        var list = missingProjects.map(function(p) { return escapeHtml(p); }).join(', ');
        el.querySelector('.project-warning-text').innerHTML =
            'This link includes projects you don\'t have access to: <strong>' + list + '</strong>';
        el.classList.remove('hidden');
    }

    function dismissProjectWarning() {
        var el = document.getElementById('projectWarningBanner');
        if (el) el.classList.add('hidden');
    }

    // Show unsupported browser message
    function showUnsupportedBrowserMessage() {
        const app = document.getElementById('appContainer');
        app.innerHTML = `
            <div class="empty-state empty-state--overlay">
                <div class="empty-state__inner empty-state__inner--centered">
                    <h2>Browser Not Supported</h2>
                    <p>This application requires a Chromium-based browser (Chrome, Edge, Brave) with File System Access API support.</p>
                    <p>Please use one of these browsers to access the Archive Browser.</p>
                </div>
            </div>
        `;
    }

    // Show empty state
    function showEmptyState() {
        document.getElementById('noDirectoryMessage').classList.remove('hidden');
        document.querySelector('.main-container').style.display = 'none';
        // Keep header visible
        document.querySelector('.app-header').style.display = '';
        var refreshBtn = document.getElementById('refreshHeaderBtn');
        if (refreshBtn) { refreshBtn.classList.add('hidden'); }
    }

    // Hide empty state
    function hideEmptyState() {
        document.getElementById('noDirectoryMessage').classList.add('hidden');
        document.querySelector('.main-container').style.display = '';
        var refreshBtn = document.getElementById('refreshHeaderBtn');
        if (refreshBtn) { refreshBtn.classList.remove('hidden'); }
    }

    // Update UI based on current state
    function updateUI() {
        renderFolderTypeBar();
        renderFolderLists();
        window.app.modules.table.updateFileTable();
        updateStatusBar();
    }

    // Render folder lists (rebuilds DOM)
    function renderFolderLists() {
        renderGroupingFolders();
        renderTransmittalFolders();
    }

    // Check if a folder path is under a hidden folder type
    // Returns true if any path segment is a known folder type that is NOT currently enabled
    function isUnderHiddenFolderType(path) {
        const parts = path.toLowerCase().split('/');
        return parts.some(part =>
            window.app.FOLDER_TYPE_NAMES.includes(part) && !window.app.enabledFolderTypes.has(part)
        );
    }

    // Get filtered grouping folders (single source of truth for filtering logic)
    function getFilteredGroupingFolders() {
        const filter = window.app.groupingFilter;
    
        return window.app.groupingFolders.filter(folder => {
            if (isUnderHiddenFolderType(folder.path)) {
                return false;
            }
        
            if (!filter) return true;
        
            const terms = parseSearchTerms(filter);
            return matchesSearchTerms(folder.name.toLowerCase(), terms);
        });
    }

    // Render grouping folders as a flat list of unique party names. Same-named
    // third-party folders across multiple projects collapse to one row.
    // selectedGroupingFolders is a Set of party NAMES (not paths) so toggling
    // affects every project occurrence at once.
    function renderGroupingFolders() {
        const container = document.getElementById('groupingFoldersList');

        // Get filtered grouping folders (uses shared filtering logic)
        const filteredFolders = getFilteredGroupingFolders();

        // Only show top-level party folders (the shallowest depth among all grouping folders)
        const allDepths = window.app.groupingFolders.map(f => f.path.split('/').length);
        const minDepth = allDepths.length > 0 ? Math.min(...allDepths) : 1;
        const partyFolders = filteredFolders.filter(f => f.path.split('/').length === minDepth);

        // Dedupe by name (keep first occurrence per name). In multi-project mode,
        // skip parties whose every occurrence is under a hidden project — if at
        // least one occurrence is in a visible project, the party stays.
        const seen = new Set();
        const uniqueParties = [];
        for (const f of partyFolders) {
            if (seen.has(f.name)) continue;
            if (window.app.isMultiProject) {
                const hasVisible = partyFolders.some(p =>
                    p.name === f.name && pathIsInVisibleProject(p.path)
                );
                if (!hasVisible) continue;
            }
            seen.add(f.name);
            uniqueParties.push(f);
        }
        uniqueParties.sort((a, b) => a.name.localeCompare(b.name));

        const partyNames = new Set(uniqueParties.map(f => f.name));

        // If "Select All" mode is active, auto-select all visible party names.
        if (window.app.selectAllGroupingFolders) {
            window.app.selectedGroupingFolders.clear();
            uniqueParties.forEach(f => window.app.selectedGroupingFolders.add(f.name));
        } else {
            // Remove selections for names that are no longer visible.
            for (const selectedName of window.app.selectedGroupingFolders) {
                if (!partyNames.has(selectedName)) {
                    window.app.selectedGroupingFolders.delete(selectedName);
                }
            }
        }

        // Sync checkbox state
        const checkbox = document.getElementById('selectAllGroupingCheckbox');
        if (checkbox) checkbox.checked = window.app.selectAllGroupingFolders;

        if (uniqueParties.length === 0 && window.app.groupingFilter) {
            container.innerHTML = '<div class="folder-list-empty">No parties match your filter</div>';
            updateFolderSelectionState('groupingFoldersList');
            return;
        }

        container.innerHTML = uniqueParties.map(folder => `
            <div class="folder-item ${window.app.selectedGroupingFolders.has(folder.name) ? 'selected' : ''}"
                 data-path="${escapeHtml(folder.name)}"
                 data-folder-type="grouping">
                <span class="folder-item-name" title="${escapeHtml(folder.name)}">${escapeHtml(folder.name)}</span>
            </div>
        `).join('');

        updateFolderSelectionState('groupingFoldersList');
    }

    // Render the global folder type toggle bar
    function renderFolderTypeBar() {
        const bar = document.getElementById('folderTypeBar');
        if (!bar) return;

        const FOLDER_TYPE_LABELS = { mdl: 'MDL', incoming: 'Incoming', issued: 'Issued', received: 'Received' };
        bar.innerHTML = window.app.FOLDER_TYPE_NAMES.map(type => {
            const active = window.app.enabledFolderTypes.has(type);
            const label = FOLDER_TYPE_LABELS[type] || (type.charAt(0).toUpperCase() + type.slice(1));
            return `<button class="folder-type-toggle ${active ? 'active' : ''}"
                            data-type="${type}"
                            title="Toggle ${label} folders">${label}</button>`;
        }).join('');
    }

    // Toggle a folder type on/off globally.
    // Off->on triggers a refresh because source.js skips listings for disabled folder types
    // entirely (no listing fetched), so newly-enabled types need a rescan to surface their data.
    function toggleFolderType(type) {
        const wasEnabled = window.app.enabledFolderTypes.has(type);
        if (wasEnabled) {
            window.app.enabledFolderTypes.delete(type);
        } else {
            window.app.enabledFolderTypes.add(type);
        }
        renderFolderTypeBar();
        renderGroupingFolders();
        renderTransmittalFolders();
        window.app.modules.filtering.applyFilters();
        window.app.modules.urlState.push();

        if (!wasEnabled && window.app.directories.length > 0) {
            window.app.modules.directory.refreshDirectories();
        }
    }

    // In multi-project mode, returns true if the path contains a segment matching
    // a checked project in the picker. Single-project mode always returns true
    // (no project segment to match against).
    function pathIsInVisibleProject(path) {
        if (!window.app.isMultiProject) return true;
        if (!window.app.visibleProjects || window.app.visibleProjects.size === 0) return false;
        return path.split('/').some(seg => window.app.visibleProjects.has(seg));
    }

    // Returns true if an outstanding file's actualPath has a path segment matching
    // any selected party name and is not under a hidden folder type. Segment-equality
    // (not prefix) so the same party name selected across projects matches all
    // occurrences regardless of project ID prefix.
    function outstandingFileIsVisible(file) {
        const selectedGrouping = window.app.selectedGroupingFolders;
        if (selectedGrouping.size === 0) return false;
        if (isUnderHiddenFolderType(file.actualPath)) return false;
        if (!pathIsInVisibleProject(file.actualPath)) return false;
        return file.actualPath.split('/').some(seg => selectedGrouping.has(seg));
    }

    // Returns true if any outstanding (non-transmittal) files exist under the currently
    // selected and visible grouping folders.
    function hasVisibleOutstandingFiles() {
        return window.app.files.some(function(f) {
            if (f.folderPath !== '__outstanding__') return false;
            return outstandingFileIsVisible(f);
        });
    }

    // Returns true if the transmittal folder's path satisfies all three cascade
    // layers: project visibility, party selection (any path segment matches a
    // selected party name), and folder-type enablement (no segment is a
    // folder-type marker that's currently disabled, regardless of where in the
    // path it sits). Segment-equality matching means a party "BM" selected
    // matches every "<...>/BM/<...>" path regardless of the prefix; and the
    // folder-type check covers BOTH the canonical "<party>/Issued/<txn>" layout
    // AND nested layouts like "<party>/<sub>/Issued/<txn>" — a deeper folder-
    // type marker still triggers the cascade.
    function transmittalIsUnderVisibleParty(folder) {
        if (!pathIsInVisibleProject(folder.path)) return false;
        if (isUnderHiddenFolderType(folder.path)) return false;
        return folder.path.split('/').some(seg =>
            window.app.selectedGroupingFolders.has(seg)
        );
    }

    // Render transmittal folders (rebuilds DOM)
    function renderTransmittalFolders() {
        const container = document.getElementById('transmittalFoldersList');
        const filter = window.app.transmittalFilter;
    
        // Filter transmittal folders based on grouping selection and name filter
        const filteredFolders = window.app.transmittalFolders.filter(folder => {
            // Outstanding virtual transmittal: include if there are visible outstanding files
            if (folder.path === '__outstanding__') {
                if (!hasVisibleOutstandingFiles()) return false;
                // Apply name filter to "Outstanding" label too
                if (filter && filter.trim()) {
                    const terms = parseSearchTerms(filter.trim());
                    if (!matchesSearchTerms('outstanding', terms)) return false;
                }
                return true;
            }
        
            // Check name filter
            let matchesFilter = true;
            if (filter && filter.trim()) {
                const terms = parseSearchTerms(filter.trim());
                const folderText = folder.name.toLowerCase();
                matchesFilter = matchesSearchTerms(folderText, terms);
            }
        
            // If no grouping folders exist at all, show all transmittal folders (flat structure)
            if (window.app.groupingFolders.length === 0) {
                return matchesFilter;
            }
        
            // If grouping folders exist but none are selected, show nothing
            if (window.app.selectedGroupingFolders.size === 0) {
                return false;
            }
        
            // Check party + folder type visibility
            return matchesFilter && transmittalIsUnderVisibleParty(folder);
        });
    
        // Sort regular transmittal folders by date (newest first); Outstanding handled separately
        const regularFolders = filteredFolders.filter(f => f.path !== '__outstanding__');
        regularFolders.sort((a, b) => b.name.localeCompare(a.name));
    
        const showOutstanding = filteredFolders.some(f => f.path === '__outstanding__');
    
        // Build set of visible folder paths (for Select All and deselection logic)
        const filteredPaths = new Set(filteredFolders.map(f => f.path));
    
        // If "Select All" mode is active, auto-select all visible transmittal folders
        if (window.app.selectAllTransmittals) {
            window.app.selectedTransmittalFolders.clear();
            filteredFolders.forEach(f => window.app.selectedTransmittalFolders.add(f.path));
        } else {
            // Remove selections for folders that are now filtered out
            for (const selectedPath of window.app.selectedTransmittalFolders) {
                if (!filteredPaths.has(selectedPath)) {
                    window.app.selectedTransmittalFolders.delete(selectedPath);
                }
            }
        }
    
        // Sync checkbox state
        const checkbox = document.getElementById('selectAllTransmittalsCheckbox');
        if (checkbox) checkbox.checked = window.app.selectAllTransmittals;
    
        // Group regular folders by date
        const foldersByDate = new Map();
        regularFolders.forEach(folder => {
            const match = folder.name.match(/^(\d{4}-\d{2}-\d{2})/);
            const date = match ? match[1] : 'Unknown';
            if (!foldersByDate.has(date)) {
                foldersByDate.set(date, []);
            }
            foldersByDate.get(date).push(folder);
        });
    
        // Build HTML
        let html = '';
    
        // Outstanding virtual transmittal — pinned at top
        if (showOutstanding) {
            const isSelected = window.app.selectedTransmittalFolders.has('__outstanding__');
            html += `
                <div class="folder-item outstanding-transmittal ${isSelected ? 'selected' : ''}"
                     data-path="__outstanding__"
                     data-folder-type="transmittal"
                     title="Files in non-transmittal folders under selected grouping folders">
                    <div class="transmittal-folder-content">
                        <div class="transmittal-first-line outstanding-label">⋯ Outstanding</div>
                    </div>
                </div>
            `;
        }
    
        // Regular date-grouped folders
        for (const [date, folders] of foldersByDate) {
            const isCollapsed = window.app.collapsedDateGroups.has(date);
            const folderCount = folders.length;
        
            html += `
                <div class="date-group-header" data-date="${escapeHtml(date)}">
                    <span class="date-group-toggle">${isCollapsed ? '▶' : '▼'}</span>
                    <span class="date-group-date">${escapeHtml(date)}</span>
                    <span class="date-group-count">(${folderCount})</span>
                </div>
            `;
        
            if (!isCollapsed) {
                for (const folder of folders) {
                    const match = folder.name.match(/^\d{4}-\d{2}-\d{2}_([^_\s]+)\s*\(([^)]+)\)\s*-\s*(.+)$/);
                    let firstLine = folder.name;
                    let secondLine = '';
                
                    if (match) {
                        const [, tracking, status, title] = match;
                        firstLine = `${tracking} • ${status}`;
                        secondLine = title;
                    }
                
                    html += `
                        <div class="folder-item ${window.app.selectedTransmittalFolders.has(folder.path) ? 'selected' : ''}"
                             data-path="${escapeHtml(folder.path)}"
                             data-folder-type="transmittal"
                             title="${escapeHtml(folder.path)}">
                            <div class="transmittal-folder-content">
                                <div class="transmittal-first-line">${escapeHtml(firstLine)}</div>
                                ${secondLine ? `<div class="transmittal-second-line">${escapeHtml(secondLine)}</div>` : ''}
                            </div>
                        </div>
                    `;
                }
            }
        }
    
        if (filteredFolders.length === 0 && window.app.transmittalFilter) {
            container.innerHTML = '<div class="folder-list-empty">No folders match your filter</div>';
            updateFolderSelectionState('transmittalFoldersList');
            window.app.modules.events.updateToggleAllIcon();
            return;
        }

        container.innerHTML = html;
    
        // Ensure selection state is visually reflected after DOM rebuild
        updateFolderSelectionState('transmittalFoldersList');
    
        // Update the toggle all icon to reflect current state
        window.app.modules.events.updateToggleAllIcon();
    }


    // Update status bar
    function updateStatusBar() {
        const fileCountEl = document.getElementById('fileCount');
        const selectedCountEl = document.getElementById('selectedCount');

        // Before any directory is loaded, show a hint instead of "0 files"
        if (window.app.directories.length === 0 && !window.app.isScanning) {
            fileCountEl.textContent = 'Select a directory to begin';
            selectedCountEl.textContent = '';
            document.getElementById('scanStatus').textContent = '';
            var spinner2 = document.getElementById('scanSpinner');
            if (spinner2) spinner2.classList.add('hidden');
            document.getElementById('downloadSelectedBtn').disabled = true;
            document.getElementById('exportCsvBtn').disabled = true;
            return;
        }

        // Count unique tracking numbers
        const trackingNumbers = new Set(window.app.filteredFiles.map(f => f.trackingNumber));
        const trackingCount = trackingNumbers.size;
        const fileCount = window.app.filteredFiles.length;
    
        // Count files with path errors
        const pathErrorCount = window.app.filteredFiles.filter(f => f.hasPathError).length;
    
        // Format: "X tracking numbers, Y files" + optional path error warning
        let countText = `${trackingCount} tracking number${trackingCount !== 1 ? 's' : ''}, ${fileCount} file${fileCount !== 1 ? 's' : ''}`;
        if (pathErrorCount > 0) {
            countText += ` (⚠️ ${pathErrorCount} inaccessible)`;
        }
    
        fileCountEl.textContent = countText;
        selectedCountEl.textContent = `${window.app.selectedFiles.size} selected`;
        document.getElementById('scanStatus').textContent = window.app.scanProgress;
        var spinner = document.getElementById('scanSpinner');
        if (spinner) { spinner.classList.toggle('hidden', !window.app.isScanning); }

        // Disable action buttons when nothing is selected
        const noneSelected = window.app.selectedFiles.size === 0;
        document.getElementById('downloadSelectedBtn').disabled = noneSelected;
        document.getElementById('exportCsvBtn').disabled = noneSelected;
    }

    // Escape HTML for safe insertion
    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    /**
     * Update folder selection visual state without rebuilding DOM
     * This is more efficient than re-rendering when only selection changes
     * @param {string} containerId - 'groupingFoldersList' or 'transmittalFoldersList'
     */
    function updateFolderSelectionState(containerId) {
        const container = document.getElementById(containerId);
        if (!container) {
            console.warn(`Container not found: ${containerId}`);
            return;
        }
    
        const selectedSet = containerId === 'groupingFoldersList' ? 
            window.app.selectedGroupingFolders : 
            window.app.selectedTransmittalFolders;
    
        // Update selected class on existing elements
        container.querySelectorAll('.folder-item').forEach(item => {
            const path = item.getAttribute('data-path');
            if (path) {
                item.classList.toggle('selected', selectedSet.has(path));
            }
        });
    }

    // Extract modifier type from revision string (e.g., "2+B1" -> "+B", "2" -> "base")
    function getModifierType(revision) {
        if (!revision) return 'base';
        const match = revision.match(/\+([A-Za-z])/);
        return match ? '+' + match[1].toUpperCase() : 'base';
    }

    // Collect all unique modifiers from files
    function collectModifiers() {
        window.app.availableModifiers.clear();

        window.app.files.forEach(file => {
            const modType = getModifierType(file.revision);
            window.app.availableModifiers.add(modType);
        });

        // Default selection: 'base' (un-modified revisions) plus '+C' (comment
        // markups against base). Other modifier types (+B, +D, …) are
        // available in the dropdown but hidden by default — users opt them in
        // via the Modifiers dropdown when they want to see scratch / draft /
        // hold-style markups. Falls back to selecting whatever is available
        // when neither default exists, so the table never goes empty out of
        // the gate.
        const defaults = ['base', '+C'];
        const selected = new Set();
        defaults.forEach(d => {
            if (window.app.availableModifiers.has(d)) selected.add(d);
        });
        if (selected.size === 0) {
            window.app.availableModifiers.forEach(m => selected.add(m));
        }
        window.app.selectedModifiers = selected;

        // Update the dropdown UI
        renderModifierDropdown();
    }

    // Render the modifier dropdown options
    function renderModifierDropdown() {
        const list = document.getElementById('modifierFilterList');
        if (!list) return;
    
        // Sort modifiers: "base" first, then alphabetically
        const sorted = Array.from(window.app.availableModifiers).sort((a, b) => {
            if (a === 'base') return -1;
            if (b === 'base') return 1;
            return a.localeCompare(b);
        });
    
        let html = '';
        sorted.forEach(mod => {
            const checked = window.app.selectedModifiers.has(mod) ? 'checked' : '';
            const label = mod === 'base' ? 'Base (no modifier)' : mod;
            const labelClass = mod === 'base' ? 'modifier-base' : 'modifier-type';
            html += `
                <div class="modifier-filter-item">
                    <label>
                        <input type="checkbox" 
                               data-modifier="${mod}" 
                               ${checked}
                               onchange="toggleModifierFilter('${mod}')">
                        <span class="${labelClass}">${label}</span>
                    </label>
                </div>
            `;
        });
    
        list.innerHTML = html;
        updateModifierSelectAll();
        updateModifierButtonLabel();
    }

    // Toggle a specific modifier filter
    function toggleModifierFilter(mod) {
        if (window.app.selectedModifiers.has(mod)) {
            window.app.selectedModifiers.delete(mod);
        } else {
            window.app.selectedModifiers.add(mod);
        }
        updateModifierSelectAll();
        updateModifierButtonLabel();
        window.app.modules.filtering.applyFilters();
    }

    // Toggle all modifiers
    function toggleAllModifiers(selectAll) {
        if (selectAll) {
            window.app.selectedModifiers = new Set(window.app.availableModifiers);
        } else {
            window.app.selectedModifiers.clear();
        }
        renderModifierDropdown();
        window.app.modules.filtering.applyFilters();
    }

    // Update the "Select All" checkbox state
    function updateModifierSelectAll() {
        const selectAllCheckbox = document.getElementById('modifierSelectAll');
        if (selectAllCheckbox) {
            selectAllCheckbox.checked = window.app.selectedModifiers.size === window.app.availableModifiers.size;
            selectAllCheckbox.indeterminate = window.app.selectedModifiers.size > 0 && 
                                              window.app.selectedModifiers.size < window.app.availableModifiers.size;
        }
    }

    // Update button label to show filter status
    function updateModifierButtonLabel() {
        const btn = document.getElementById('modifierFilterBtn');
        if (!btn) return;
    
        const total = window.app.availableModifiers.size;
        const selected = window.app.selectedModifiers.size;
    
        if (selected === total) {
            btn.textContent = 'Modifiers ▼';
        } else if (selected === 0) {
            btn.textContent = 'Modifiers (none) ▼';
        } else {
            btn.textContent = `Modifiers (${selected}/${total}) ▼`;
        }
    }

    // Toggle modifier dropdown visibility
    function toggleModifierDropdown() {
        const dropdown = document.getElementById('modifierFilterDropdown');
        dropdown.classList.toggle('hidden');
    }

    // Update the Folders icon button state based on active visibility toggles
    function updateFolderVisibilityBtnLabel() {
        // replaced by renderFolderTypeBar()
    }

    // Check if a file passes the modifier filter
    function filePassesModifierFilter(file) {
        const modType = getModifierType(file.revision);
        return window.app.selectedModifiers.has(modType);
    }

    // Toggle filter to show only selected files
    function toggleFilterSelected() {
        window.app.showSelectedOnly = !window.app.showSelectedOnly;
    
        // Update button visual state and label
        const btn = document.getElementById('filterSelectedBtn');
        if (window.app.showSelectedOnly) {
            btn.classList.add('btn-active');
            btn.textContent = 'Show All';
        } else {
            btn.classList.remove('btn-active');
            btn.textContent = 'Filter Selected';
        }
    
        window.app.modules.filtering.applyFilters();
    }
    // Register with module system
    window.app.modules.app = {
        updateUI,
        updateStatusBar,
        escapeHtml,
        updateFolderSelectionState,
        getModifierType,
        collectModifiers,
        renderModifierDropdown,
        toggleModifierFilter,
        toggleAllModifiers,
        updateModifierSelectAll,
        updateModifierButtonLabel,
        toggleModifierDropdown,
        updateFolderVisibilityBtnLabel,
        filePassesModifierFilter,
        toggleFilterSelected,
        isUnderHiddenFolderType,
        ensureOutstandingTransmittal,
        showHttpErrorState,
        showUnsupportedBrowserMessage,
        showProjectWarning,
        dismissProjectWarning,
        showEmptyState,
        hideEmptyState,
        addHttpSource,
        scanHttpSource,
        renderGroupingFolders,
        renderTransmittalFolders,
        renderFolderTypeBar,
        toggleFolderType,
        outstandingFileIsVisible,
        hasVisibleOutstandingFiles,
        transmittalIsUnderVisibleParty,
        pathIsInVisibleProject,
        renderFolderLists,
        getFilteredGroupingFolders,
        showProjectWarning,
        dismissProjectWarning,
    };

    // Expose key functions on window for inline HTML handlers
    window.initApp = initApp;
    window.toggleFileSelection = function(id) { window.app.modules.table.toggleFileSelection(id); };
    window.sortTable = function(f) { window.app.modules.table.sortTable(f); };
    window.confirmTransmittal = function() { window.app.modules.dragDrop.confirmTransmittal(); };
    window.toggleModifierFilter = toggleModifierFilter;
    window.toggleFilterSelected = toggleFilterSelected;
    window.toggleFolderType = toggleFolderType;
    window.toggleGroupingFolder = function(p, r) { window.app.modules.events.toggleGroupingFolder(p, r); };
    window.toggleDateGroup = function(d) { window.app.modules.events.toggleDateGroup(d); };
    window.toggleAllDateGroups = function() { window.app.modules.events.toggleAllDateGroups(); };
    window.selectAllVisibleFolders = function(t) { window.app.modules.events.selectAllVisibleFolders(t); };
    window.removeDirectory = function(n) { window.app.modules.directory.removeDirectory(n); };
    window.dismissProjectWarning = dismissProjectWarning;
    window.verifyFileIntegrity = function(id) { window.app.modules.hash.verifyFileIntegrity(id); };
    window.showProjectWarning = showProjectWarning;
    window.dismissProjectWarning = dismissProjectWarning;

    // Initialize on DOM ready
    document.addEventListener('DOMContentLoaded', initApp);

})();
