NovaStar H2 
    
        
        
        
        
            
                
                    
                    
                        NovaStar H2 
                        
                        
                        
                        
                        
                        
                        
                        
                        
                            $669
                            β In Stock - Ships Same Day (if ordered before 3PM EST)
                         
                        
                        
                        π‘οΈ 3-Year NovaStar Warranty
                        
                            π Pricing Notice:  Pricing shown above is MAP pricing. Please 
contact us  if you're a reseller.
                        
 
                     
                    
                    
                    
                 
             
         
        
        
        
        
        
        
        
        
        
    
        
            βοΈ Modular Configuration 
            The H2 features a modular design - configure it with your choice of input and output cards
            
            
                
                    π₯ Input Slots: 
                    4 
                
                
                    π€ Output Slots: 
                    2 
                
                
                    π¬ Layers per Output: 
                    16 
                
                
             
            
            
                π οΈ Configure Your System
             
            
            
                Available Input Cards (20) 
                
                    
                        H_1x DP1.2 Input Card
                        1x DisplayPort 1.2
                        $1,127
                     
                    
                        H_2x DP1.1 Input Card
                        2x DisplayPort 1.1
                        $845
                     
                    
                        H_1x HDMI2.0 Input Card
                        1x HDMI 2.0
                        $1,127
                     
                    
                        H_2x HDMI2.0 Input Card
                        2x HDMI 2.0
                        $1,644
                     
                    
                        H_4x HDMI Input Card
                        4x HDMI 1.3 (or 2x HDMI 1.4)
                        $845
                     
                    
                        H_1xHDMI2.0+1xDP1.2 Input Card
                        1x HDMI 2.0 + 1x DisplayPort 1.2
                        $1,798
                     
                    
                        H_2xHDMI2.0+2xDP1.2 Input Card
                        2x HDMI 2.0 + 2x DisplayPort 1.2
                        $2,320
                     
                    
                        H_1xHDMI2.1+1xDP1.4 (8K@30Hz) Input Card
                        1x HDMI 2.1 + 1x DisplayPort 1.4 (8K@30Hz)
                        $3,184
                     
                    
                        H_1x 12G SDI Input Card
                        1x 12G-SDI
                        $1,825
                     
                    
                        H_4x 3G SDI Input Card
                        4x 3G-SDI
                        $1,644
                     
                    
                        H_4x DVI Input Card
                        4x DVI
                        $845
                     
                    
                        H_4x HDBaseT Input Card
                        4x HDBaseT
                        $1,759
                     
                    
                        H_1x NDI Input Card
                        1x NDI
                        $1,399
                     
                    
                        H_4x Fiber Input Card
                        4x 10G Fiber (OPT)
                        $2,999
                     
                    
                        H_1xST2110 Input Card
                        1x SMPTE ST 2110 (25G Fiber)
                        $3,999
                     
                    
                        H_2x IP Input Card
                        2x IP Streams (RTSP, GB28181, ONVIF)
                        $1,479
                     
                    
                        H_STD I/O Card
                        Standard I/O Control Card
                        $649
                     
                    
                        H_2ΓAudio input +2ΓAudio Output Card
                        2x Audio Input + 2x Audio Output
                        $854
                     
                    
                        H_2xRJ45+1xHDMI1.3 Preview Card (MVR)
                        2x RJ45 + 1x HDMI 1.3 Multi-Viewer Preview
                        $1,233
                     
                    
                        H_2x IP Input Card (RTSP, GB28181, ONVIF)
                        2x IP Streams (RTSP, GB28181, ONVIF)
                        $1,479
                     
                 
                
                Available Output Cards (6) 
                
                    
                        H_16xRJ45+2xFiber Output Card
                        16x Gigabit Ethernet + 2x 10G Fiber
                        $1,644
                     
                    
                        H_20xRJ45 Output Card
                        20x Gigabit Ethernet
                        $1,703
                     
                    
                        H_4x Fiber Output Card
                        4x 10G Fiber
                        $3,218
                     
                    
                        H_1 HDMI2.0 Output Card
                        1x HDMI 2.0
                        $1,127
                     
                    
                        H_4x HDMI Output Card
                        4x HDMI
                        $915
                     
                    
                        H_4 HDBaseT Output Card
                        4x HDBaseT
                        $1,759
                     
                 
             
         
     
        
        
    
        
            Technical Specifications 
            Video Processing Layers 32 Maximum Pixels 41.6 million pixels 
π HDMI 2.1 Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            30 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            30 
                         
                            YCbCr 
                            4:2:2 
                            8bit 
                            30 
                         
                            RGB 
                            4:4:4 
                            10bit 
                            30 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            30 
                         
                            YCbCr 
                            4:2:2 
                            10bit 
                            30 
                         
π HDMI 2.0 Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            8bit 
                            60 
                         
                        
                             
                     
                            RGB 
                            4:4:4 
                            10bit 
                            30 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            30 
                         
                        
                             
                     
                            YCbCr 
                            4:2:2 
                            10bit 
                            60 
                         
π DP 1.4 Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            30 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            30 
                         
                        
                             
                     
                            YCbCr 
                            4:2:2 
                            8bit 
                            30 
                         
                            YCbCr 
                            4:2:2 
                            10bit 
                            30 
                         
                        
                             
                     
                            RGB 
                            4:4:4 
                            10bit 
                            24 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            24 
                         
π DP 1.2 Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            8bit 
                            60 
                         
                        
                             
                     
                            RGB 
                            4:4:4 
                            10bit 
                            30 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            30 
                         
                        
                             
                     
                            YCbCr 
                            4:2:2 
                            10bit 
                            60 
                         
π HDMI 1.4 / DP 1.1 Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            8bit 
                            60 
                         
                        
                             
                     
                            RGB 
                            4:4:4 
                            10bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            60 
                         
                        
                             
                     
                            YCbCr 
                            4:2:2 
                            10bit 
                            60 
                         
π HDMI 1.3 Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            8bit 
                            60 
                         
                            RGB 
                            4:4:4 
                            10bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            10bit 
                            60 
                         
π NDI Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            YCbCr 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:4:0 
                            8bit 
                            60 
                         
π ST 2110 (25G OPT port) Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            8bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            8bit 
                            60 
                         
                            RGB 
                            4:4:4 
                            10bit 
                            60 
                         
                            YCbCr 
                            4:4:4 
                            10bit 
                            60 
                         
                            YCbCr 
                            4:2:2 
                            10bit 
                            60 
                         
π 12G-SDI Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            YCbCr 
                            4:2:2 
                            8bit/10bit/12bit 
                            60 
                         
π 3G-SDI Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            YCbCr 
                            4:2:2 
                            8bit/10bit 
                            60 
                         
π DL-DVI Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
π SL-DVI Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
π VGA Resolution & Format Capabilities 
                    
                        Resolution 
                        Color Space 
                        Sampling 
                        Bit Depth 
                        Supported Frame Rates (Hz) 
                     
                 
                        
                             
                     
                            RGB 
                            4:4:4 
                            8bit 
                            60 
                         
Physical Specifications Packing box 660 mm Γ 570 mm Γ 210 mm (26.0" Γ 22.4" Γ 8.3") 
  
        
        
        
            
                Why Choose the H2? 
                
    
                    
                        β All-in-One Design 
                        Integrated solution saves rack space and simplifies setup
                     
        
                    
                        β Professional Features 
                        Advanced processing with low latency and pixel-level calibration
                     
        
                    
                        β Maximum Reliability 
                        Backed by 3-year NovaStar warranty plus Olympian LED support
                     
        
                    
                        β Proven Performance 
                        Trusted by production companies and rental houses nationwide
                     
        
                 
             
         
    
        
        
        
    
        
        
            
                Frequently Asked Questions 
                
    
                    
                        Q: What warranty is included? 
                        All NovaStar products include a full 3-year manufacturer warranty when purchased from authorized distributors like Olympian LED.
                     
        
                    
                        Q: Do you offer volume pricing? 
                        Yes! As the largest NovaStar distributor in the USA, we offer special pricing for bulk orders and repeat customers. Contact our sales team for a custom quote.
                     
        
                    
                        Q: Do you have this in stock? 
                        We maintain over $2 million in inventory at our Titusville, Florida warehouse. Most orders ship same-day. Contact us to confirm current availability.
                     
        
                    
                        Q: Can I get NET 30 payment terms? 
                        NET 30 terms are available for qualified businesses and established customers. Please contact our sales team to discuss payment options.
                     
        
                 
             
         
    
        
        
        
            
                Frequently Purchased Together 
                Customers who bought this product also purchased:
                
    
                    
                        
                            H Series 
                            H_4x HDMI Input Card 
                            4x HDMI 1.3 (or 2x HDMI 1.4)...
                            
                         
                     
        
                 
             
         
    
        
            
                Why Buy from Olympian LED? 
                
                    
                        π
                        Largest US Distributor 
                        Best pricing guaranteed as the largest NovaStar distributor in America
                     
                    
                        π¦
                        Ships Same Day 
                        Over $2M in stock at our Florida warehouse for immediate delivery
                     
                    
                        π³
                        Flexible Terms 
                        NET 30 payment terms and volume discounts for qualified businesses
                     
                    
                        π οΈ
                        Expert Support 
                        Technical support from our team of LED display specialists
                     
                 
             
         
    
        
        
        
     
    
    
    
    
    
    
    
    
    
    
    
        β
     
    
        
        
            
                
                
                    Input Slots (4) 
                    
                    Output Slots (2) 
                    
                 
                
                    Configuration Summary 
                    
                    
                        Total Price: 
                        $669 
                    
                    π Add Configuration to Cart 
                    π Share Configuration Link 
                    π Export Configuration Report 
                    ποΈ Clear Configuration 
                 
             
         
     
 
    // HDMI connector (icon_030.png)
    hdmi: '
            ${card.name.replace('H_', '')}
            ${card.port_badges_html || ''}
            
         
    `;
    }).join('');
    
    outputDiv.innerHTML = hSeriesConfig.outputCards.map((card, i) => {
        const slug = card.name.toLowerCase().replace(/ /g, '-').replace(/[_+()\u00d7]/g, c => {
            if (c === '_') return '-';
            if (c === '+') return 'plus';
            if (c === '\u00d7') return 'x';
            return '';
        }).replace(/,/g, '');
        return `
        
            ${card.name.replace('H_', '')}
            ${card.port_badges_html || ''}
            
         
    `;
    }).join('');
    
    if (accessDiv && hSeriesConfig.accessories.length > 0) {
        accessDiv.innerHTML = hSeriesConfig.accessories.map((acc, i) => {
            const slug = acc.name.toLowerCase().replace(/ /g, '-').replace(/[_+()\u00d7]/g, c => {
                if (c === '_') return '-';
                if (c === '+') return 'plus';
                if (c === '\u00d7') return 'x';
                return '';
            }).replace(/,/g, '');
            return `
            
                ${acc.name}
                ${generatePortBadges(acc.name)}
                
             
        `;
        }).join('');
    }
    
    } catch (error) {
        console.error('β Error in renderAvailableCards:', error);
        console.error('Stack:', error.stack);
    }
}
function renderSlots() {
    try {
    // Check if we have a grid layout (like H2)
    if (hSeriesConfig.layoutGrid) {
        renderGridLayout();
    } else {
        renderListLayout();
    }
    } catch (error) {
        console.error('β Error in renderSlots:', error);
        console.error('Stack:', error.stack);
    }
}
function renderGridLayout() {
    try {
    const inputDiv = document.getElementById('inputSlots');
    const outputDiv = document.getElementById('outputSlots');
    
    // Hide the output div and its heading (but not the parent panel)
    outputDiv.style.display = 'none';
    const outputHeading = outputDiv.previousElementSibling;
    if (outputHeading && outputHeading.tagName === 'H3') {
        outputHeading.style.display = 'none';
    }
    
    // Change the input heading
    const inputHeading = inputDiv.previousElementSibling;
    if (inputHeading && inputHeading.tagName === 'H3') {
        inputHeading.textContent = 'Slot Configuration';
    }
    
    // Change inputDiv display from grid to block to allow custom layout
    inputDiv.style.display = 'block';
    
    // Check if it's a column-based layout (H5+) or row-based layout (H2)
    const isColumnLayout = hSeriesConfig.layoutGrid.some(cell => cell.column);
    
    if (isColumnLayout) {
        // Separate into columns
        const leftColumn = hSeriesConfig.layoutGrid.filter(cell => cell.column === 'left');
        const rightColumn = hSeriesConfig.layoutGrid.filter(cell => cell.column === 'right');
        
        const htmlContent = `
            
                ${leftColumn.map(cell => renderGridCell(cell)).join('')}
            
            
                ${rightColumn.map(cell => renderGridCell(cell)).join('')}
            
         `;
        
        inputDiv.innerHTML = htmlContent;
    } else {
        // Row-based layout (H2)
        inputDiv.innerHTML = `
            ${hSeriesConfig.layoutGrid.map((row, rowIdx) => `
                
                    ${row.map((cell, colIdx) => renderGridCell(cell, rowIdx, colIdx)).join('')}
                
            `).join('')}
        
${cell.label}
β¬οΈ Occupied by
            ${cell.label}
            ${card ? `
${card.port_badges_html || ''}
Γ ` : `
${isPending ? 'Slot selected β' : 'Optional MVR'}
`}
        
 `;
    }
    
    const card = selectedCards[cell.type][cell.slot];
    const slotClass = card ? 'h-slot-filled' : 'h-slot-empty';
    const isPending = pendingCardSelection && pendingCardSelection.type === cell.type && pendingCardSelection.slot === cell.slot;
    const pendingClass = isPending ? 'h-slot-pending' : '';
    
    // Check if this is a double-slot card (for visual badge)
    const doubleSlotClass = (card && card.slots_required === 2) ? 'h-slot-double' : '';
    const doubleSlotBadge = (card && card.slots_required === 2) ? '2 SLOTS ' : '';
    
    return `
        ${cell.label}
        ${card ? `
${card.port_badges_html || ''}
${doubleSlotBadge}
Γ ` : `
${isPending ? 'Slot selected β' : 'Click to select'}
`}
    
 `;
}
function getSlotLabel(type, slot) {
    if (hSeriesConfig.layoutGrid) {
        const cell = hSeriesConfig.layoutGrid.find(c => c.type === type && c.slot === slot);
        return cell ? cell.label : `${type.toUpperCase()}-${slot + 1}`;
    }
    if (hSeriesConfig.slotLabels) {
        return hSeriesConfig.slotLabels[type][slot];
    }
    return `${type.toUpperCase()}-${slot + 1}`;
}
function renderListLayout() {
    const inputDiv = document.getElementById('inputSlots');
    const outputDiv = document.getElementById('outputSlots');
    
    const inputLabels = hSeriesConfig.slotLabels ? hSeriesConfig.slotLabels.input : Array(hSeriesConfig.inputSlots).fill(0).map((_, i) => `I-${i+1}`);
    const outputLabels = hSeriesConfig.slotLabels ? hSeriesConfig.slotLabels.output : Array(hSeriesConfig.outputSlots).fill(0).map((_, i) => `O-${i+1}`);
    
    inputDiv.innerHTML = inputLabels.map((label, i) => {
        const card = selectedCards.input[i];
        const isPending = pendingCardSelection && pendingCardSelection.type === 'input' && pendingCardSelection.slot === i;
        const pendingClass = isPending ? 'h-slot-pending' : '';
        return `
            ${label}
            ${card ? `
${card.port_badges_html || ''}
Γ ` : `
${isPending ? 'Slot selected β' : 'Click to select'}
`}
        
 `;
    }).join('');
    
    outputDiv.innerHTML = outputLabels.map((label, i) => {
        const card = selectedCards.output[i];
        const isPending = pendingCardSelection && pendingCardSelection.type === 'output' && pendingCardSelection.slot === i;
        const pendingClass = isPending ? 'h-slot-pending' : '';
        return `
            ${label}
            ${card ? `
${card.port_badges_html || ''}
Γ ` : `
${isPending ? 'Slot selected β' : 'Click to select'}
`}
        
 `;
    }).join('');
}
function promptCardSelection(type, slot) {
    pendingCardSelection = { type, slot };
    
    // Highlight the selected slot
    renderSlots();
    
    // Highlight and scroll to the appropriate card selection panel
    const inputPanel = document.getElementById('inputCardsPanel');
    const outputPanel = document.getElementById('outputCardsPanel');
    
    // Remove previous highlights
    if (inputPanel) inputPanel.classList.remove('h-cards-panel-highlight');
    if (outputPanel) outputPanel.classList.remove('h-cards-panel-highlight');
    
    // Add highlight to the relevant panel
    const targetPanel = type === 'input' ? inputPanel : outputPanel;
    if (targetPanel) {
        targetPanel.classList.add('h-cards-panel-highlight');
        targetPanel.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }
    
    // Show a message
    const slotLabel = getSlotLabel(type, slot);
}
let pendingCardSelection = null;
function clearCardPanelHighlights() {
    const inputPanel = document.getElementById('inputCardsPanel');
    const outputPanel = document.getElementById('outputCardsPanel');
    if (inputPanel) inputPanel.classList.remove('h-cards-panel-highlight');
    if (outputPanel) outputPanel.classList.remove('h-cards-panel-highlight');
}
function addCardToSlot(type, cardIndex) {
    const card = type === 'input' ? hSeriesConfig.inputCards[cardIndex] : hSeriesConfig.outputCards[cardIndex];
    
    // If we have a pending slot selection, use that
    if (pendingCardSelection && pendingCardSelection.type === type) {
        selectedCards[type][pendingCardSelection.slot] = card;
        pendingCardSelection = null;
        clearCardPanelHighlights();
        renderSlots();
        updateSummary();
        saveConfiguration();
        return;
    }
    
    // Special handling for MVR cards - find the MVR slot
    if (card.name.includes('MVR') || card.name.includes('Preview')) {
        let mvrSlot = -1;
        
        // Check layoutGrid first
        if (hSeriesConfig.layoutGrid) {
            const mvrCell = hSeriesConfig.layoutGrid.find(cell => 
                cell.type === type && cell.label && cell.label.includes('MVR')
            );
            if (mvrCell) {
                mvrSlot = mvrCell.slot;
            }
        }
        
        // Fallback to slotLabels
        if (mvrSlot === -1 && hSeriesConfig.slotLabels) {
            const labels = type === 'input' ? hSeriesConfig.slotLabels.input : hSeriesConfig.slotLabels.output;
            mvrSlot = labels.findIndex(label => label && label.includes('MVR'));
        }
        
        // If found MVR slot, use it (even if occupied - will alert user)
        if (mvrSlot !== -1) {
            if (selectedCards[type][mvrSlot]) {
                if (!confirm(`MVR slot is occupied by ${selectedCards[type][mvrSlot].name}. Replace it?`)) {
                    return;
                }
            }
            selectedCards[type][mvrSlot] = card;
            clearCardPanelHighlights();
            renderSlots();
            updateSummary();
            saveConfiguration();
            return;
        }
    }
    
    // Otherwise find first empty slot
    const emptySlot = selectedCards[type].findIndex(s => !s);
    if (emptySlot === -1) {
        alert(`All ${type} slots are full! Click on a specific empty slot first to replace a card.`);
        return;
    }
    selectedCards[type][emptySlot] = card;
    clearCardPanelHighlights();
    renderSlots();
    updateSummary();
    saveConfiguration();
}
function removeCard(type, slotIndex) {
    selectedCards[type][slotIndex] = null;
    renderSlots();
    updateSummary();
    saveConfiguration();
}
let draggedCard = null;
function handleDragStart(event, type, slot) {
    draggedCard = {
        type: type,
        slot: slot,
        card: selectedCards[type][slot]
    };
    event.dataTransfer.effectAllowed = 'move';
    event.dataTransfer.setData('text/html', event.target.innerHTML);
    event.target.style.opacity = '0.5';
    
    // Highlight only valid drop zones (same type)
    document.querySelectorAll('.h-slot').forEach(slotEl => {
        const slotType = slotEl.getAttribute('data-type');
        const isFixed = slotEl.classList.contains('h-slot-fixed');
        const isBlank = slotEl.classList.contains('h-slot-blank');
        
        // Only highlight slots of the same type that aren't fixed or blank
        if (slotType === type && !isFixed && !isBlank) {
            slotEl.classList.add('h-slot-drop-zone');
        }
    });
}
function handleDragOver(event) {
    if (event.preventDefault) {
        event.preventDefault();
    }
    event.dataTransfer.dropEffect = 'move';
    return false;
}
function handleDrop(event, targetType, targetSlot) {
    if (event.stopPropagation) {
        event.stopPropagation();
    }
    event.preventDefault();
    
    if (!draggedCard) {
        return false;
    }
    
    // Prevent dropping input cards in output slots and vice versa
    if (draggedCard.type !== targetType) {
        alert(`Cannot place an ${draggedCard.type} card into an ${targetType} slot!`);
        
        // Remove drop zone highlights
        document.querySelectorAll('.h-slot-drop-zone').forEach(slot => {
            slot.classList.remove('h-slot-drop-zone');
        });
        
        draggedCard = null;
        return false;
    }
    
    // Swap or move cards
    const sourceCard = draggedCard.card;
    const targetCard = selectedCards[targetType][targetSlot];
    
    // Move source card to target
    selectedCards[targetType][targetSlot] = sourceCard;
    
    // Clear source slot (or put target card there if swapping)
    selectedCards[draggedCard.type][draggedCard.slot] = targetCard;
    
    // Remove drop zone highlights
    document.querySelectorAll('.h-slot-drop-zone').forEach(slot => {
        slot.classList.remove('h-slot-drop-zone');
    });
    
    draggedCard = null;
    renderSlots();
    updateSummary();
    saveConfiguration();
    
    return false;
}
function toggleAccessory(accIndex) {
    const acc = hSeriesConfig.accessories[accIndex];
    const idx = selectedCards.accessories.findIndex(a => a && a.name === acc.name);
    if (idx > -1) {
        selectedCards.accessories.splice(idx, 1);
    } else {
        selectedCards.accessories.push(acc);
    }
    updateSummary();
    saveConfiguration();
}
function updateSummary() {
    let total = hSeriesConfig.framePrice;
    let inputSubtotal = 0;
    let outputSubtotal = 0;
    let accessoriesSubtotal = 0;
    
    let summary = `Frame:  ${hSeriesConfig.frameName} - $${hSeriesConfig.framePrice.toLocaleString()}
`;
    
    // Input cards
    const inputCards = selectedCards.input.filter(c => c);
    if (inputCards.length > 0) {
        summary += `Input Cards (${inputCards.length}): 
`;
        inputCards.forEach(card => {
            inputSubtotal += card.price;
            summary += `${card.name} - $${card.price.toLocaleString()}
`;
        });
        summary += `Subtotal: $${inputSubtotal.toLocaleString()}
`;
    }
    
    // Output cards
    const outputCards = selectedCards.output.filter(c => c);
    if (outputCards.length > 0) {
        summary += `Output Cards (${outputCards.length}): 
`;
        outputCards.forEach(card => {
            outputSubtotal += card.price;
            summary += `${card.name} - $${card.price.toLocaleString()}
`;
        });
        summary += `Subtotal: $${outputSubtotal.toLocaleString()}
`;
    }
    
    // Accessories
    if (selectedCards.accessories.length > 0) {
        summary += `Accessories (${selectedCards.accessories.length}): 
`;
        selectedCards.accessories.forEach(acc => {
            accessoriesSubtotal += acc.price;
            summary += `${acc.name} - $${acc.price.toLocaleString()}
`;
        });
        summary += `Subtotal: $${accessoriesSubtotal.toLocaleString()}
`;
    }
    
    // Capacity info
    const inputSlotsUsed = selectedCards.input.filter(c => c).length;
    const outputSlotsUsed = selectedCards.output.filter(c => c).length;
    const totalLayers = outputSlotsUsed * (hSeriesConfig.layersPerOutput || 16);
    
    // Calculate total pixel capacity from output cards
    let totalPixelCapacity = 0;
    outputCards.forEach(card => {
        // Parse RJ45 (Ethernet) ports: 650,000 pixels per port
        const rj45Match = card.name.match(/(\d+)x\s*RJ45/i);
        if (rj45Match) {
            totalPixelCapacity += parseInt(rj45Match[1]) * 650000;
        }
        
        // Parse Fiber ports: 5,200,000 pixels per 10G fiber port
        const fiberMatch = card.name.match(/(\d+)x\s*Fiber/i);
        if (fiberMatch) {
            totalPixelCapacity += parseInt(fiberMatch[1]) * 5200000;
        }
        
        // Parse HDMI outputs (assume 4K max: 8.3M pixels)
        const hdmiMatch = card.name.match(/(\d+)x\s*HDMI/i) || card.name.match(/HDMI.*Output.*Card/i);
        if (hdmiMatch) {
            const hdmiCount = hdmiMatch[1] ? parseInt(hdmiMatch[1]) : 1;
            const hdmiPixels = hdmiCount * 8300000;
            totalPixelCapacity += hdmiPixels;
        }
        
        // Parse HDBaseT outputs (assume 1080p: 2.1M pixels)
        const hdbasetMatch = card.name.match(/(\d+)x?\s*HDBaseT/i);
        if (hdbasetMatch) {
            const hdbasetCount = parseInt(hdbasetMatch[1]);
            const hdbasetPixels = hdbasetCount * 2100000;
            totalPixelCapacity += hdbasetPixels;
        }
    });
    
    summary += `Capacity: 
`;
    summary += `Input Slots: ${inputSlotsUsed} / ${hSeriesConfig.inputSlots}
`;
    summary += `Output Slots: ${outputSlotsUsed} / ${hSeriesConfig.outputSlots}
`;
    if (outputSlotsUsed > 0) {
        summary += `Total Layers: ${totalLayers}
`;
    }
    if (totalPixelCapacity > 0) {
        summary += `Pixel Capacity: ${(totalPixelCapacity / 1000000).toFixed(1)}M pixels
`;
    }
    
    total = hSeriesConfig.framePrice + inputSubtotal + outputSubtotal + accessoriesSubtotal;
    
    document.getElementById('configSummary').innerHTML = summary;
    document.getElementById('totalPrice').textContent = `$${total.toLocaleString()}`;
}
function addConfigurationToCart() {
    const totalPrice = parseInt(document.getElementById('totalPrice').textContent.replace(/[$,]/g, ''));
    
    if (totalPrice === hSeriesConfig.framePrice) {
        alert('Please add at least one card to your configuration before adding to cart.');
        return;
    }
    
    const config = {
        name: `${hSeriesConfig.frameName} - Custom Configuration`,
        price: totalPrice,
        quantity: 1,
        configuration: {
            frame: hSeriesConfig.frameName,
            inputCards: selectedCards.input.filter(c => c),
            outputCards: selectedCards.output.filter(c => c),
            accessories: selectedCards.accessories
        }
    };
    
    // Use the global addToCart function
    addToCart(config.name, config.price, 1, JSON.stringify(config.configuration));
    
    alert('β
 Configuration added to cart!');
    closeHSeriesCustomizer();
}
function exportConfiguration() {
    const totalPrice = parseInt(document.getElementById('totalPrice').textContent.replace(/[$,]/g, ''));
    
    if (totalPrice === hSeriesConfig.framePrice) {
        alert('Please configure at least one card before exporting.');
        return;
    }
    
    if (!window.jspdf || !window.jspdf.jsPDF) {
        alert('PDF library not loaded. Please try again.');
        return;
    }
    
    const { jsPDF } = window.jspdf;
    const doc = new jsPDF();
    
    // Calculate totals and capacity
    const inputSlotsUsed = selectedCards.input.filter(c => c).length;
    const outputSlotsUsed = selectedCards.output.filter(c => c).length;
    const totalLayers = outputSlotsUsed * (hSeriesConfig.layersPerOutput || 16);
    
    let inputSubtotal = 0;
    let outputSubtotal = 0;
    let accessoriesSubtotal = 0;
    
    // Calculate pixel capacity
    let totalPixelCapacity = 0;
    const outputCards = selectedCards.output.filter(c => c);
    outputCards.forEach(card => {
        // Check if card has max loading capacity in description
        if (card.description && card.description.includes('10,400,000')) {
            totalPixelCapacity += 10400000;
        } else if (card.description && card.description.includes('13,000,000')) {
            totalPixelCapacity += 13000000;
        } else if (card.description && card.description.includes('20,800,000')) {
            totalPixelCapacity += 20800000;
        } else {
            // Fallback to port-based calculation
            const rj45Match = card.name.match(/(\d+)x\s*RJ45/i);
            if (rj45Match) {
                totalPixelCapacity += parseInt(rj45Match[1]) * 650000;
            }
            const fiberMatch = card.name.match(/(\d+)x\s*Fiber/i);
            if (fiberMatch) {
                totalPixelCapacity += parseInt(fiberMatch[1]) * 5200000;
            }
        }
    });
    
    // Load and add logo
    const logo = new Image();
    logo.onload = function() {
        let yPos = 15;
        
        // Add logo
        try {
            doc.addImage(logo, 'PNG', 15, yPos, 40, 10);
        } catch (e) {
            // Fallback to text if logo fails
            doc.setFontSize(16);
            doc.setFont(undefined, 'bold');
            doc.text('Olympian LED', 15, yPos + 5);
        }
        
        // Header info
        doc.setFontSize(10);
        doc.setFont(undefined, 'normal');
        doc.text(`Generated: ${new Date().toLocaleString()}`, 200, yPos + 5, { align: 'right' });
        
        yPos += 20;
        
        // Title
        doc.setFontSize(18);
        doc.setFont(undefined, 'bold');
        doc.setTextColor(220, 53, 69);
        doc.text('H SERIES CONFIGURATION REPORT', 15, yPos);
        doc.setTextColor(0, 0, 0);
        
        yPos += 15;
        
        // Frame Configuration Box
        doc.setFillColor(240, 240, 240);
        doc.rect(15, yPos - 5, 180, 45, 'F');
        
        doc.setFontSize(12);
        doc.setFont(undefined, 'bold');
        doc.text('Frame Configuration', 20, yPos);
        
        yPos += 8;
        doc.setFontSize(10);
        doc.setFont(undefined, 'normal');
        doc.text(`Model: ${hSeriesConfig.frameName}`, 20, yPos);
        yPos += 6;
        doc.text(`Rack Size: ${hSeriesConfig.frameName.replace('H', '') + 'U'}`, 20, yPos);
        yPos += 6;
        doc.text(`Input Slots: ${inputSlotsUsed} / ${hSeriesConfig.inputSlots} used`, 20, yPos);
        yPos += 6;
        doc.text(`Output Slots: ${outputSlotsUsed} / ${hSeriesConfig.outputSlots} used`, 20, yPos);
        yPos += 6;
        if (outputSlotsUsed > 0) {
            doc.text(`Total Layers: ${totalLayers}`, 20, yPos);
            yPos += 6;
        }
        if (totalPixelCapacity > 0) {
            doc.setFont(undefined, 'bold');
            doc.setTextColor(220, 53, 69);
            doc.text(`Pixel Capacity: ${(totalPixelCapacity / 1000000).toFixed(1)}M pixels`, 20, yPos);
            doc.setTextColor(0, 0, 0);
            doc.setFont(undefined, 'normal');
        }
        
        yPos += 12;
        
        // Input Cards Section
        if (selectedCards.input.filter(c => c).length > 0) {
            doc.setFontSize(12);
            doc.setFont(undefined, 'bold');
            doc.text(`Input Cards (${selectedCards.input.filter(c => c).length})`, 15, yPos);
            yPos += 2;
            
            const inputData = [];
            selectedCards.input.filter(c => c).forEach(card => {
                inputSubtotal += card.price;
                inputData.push([
                    card.name.replace('H_', ''),
                    card.description.substring(0, 50) + (card.description.length > 50 ? '...' : ''),
                    `$${card.price.toLocaleString()}`
                ]);
            });
            
            doc.autoTable({
                startY: yPos,
                head: [['Card Name', 'Description', 'Price']],
                body: inputData,
                theme: 'grid',
                headStyles: { fillColor: [220, 53, 69], fontSize: 9 },
                bodyStyles: { fontSize: 9 },
                margin: { left: 15, right: 15 },
                columnStyles: {
                    0: { cellWidth: 60 },
                    1: { cellWidth: 90 },
                    2: { cellWidth: 30, halign: 'right' }
                }
            });
            
            yPos = doc.lastAutoTable.finalY + 3;
            doc.setFontSize(10);
            doc.setFont(undefined, 'bold');
            doc.text(`Subtotal: $${inputSubtotal.toLocaleString()}`, 195, yPos, { align: 'right' });
            yPos += 8;
        }
        
        // Output Cards Section
        if (selectedCards.output.filter(c => c).length > 0) {
            doc.setFontSize(12);
            doc.setFont(undefined, 'bold');
            doc.text(`Output Cards (${selectedCards.output.filter(c => c).length})`, 15, yPos);
            yPos += 2;
            
            const outputData = [];
            selectedCards.output.filter(c => c).forEach(card => {
                outputSubtotal += card.price;
                let desc = card.description.substring(0, 40);
                // Add pixel capacity to description if it's a sending card
                if (card.description.includes('Gigabit Ethernet') || card.description.includes('Fiber')) {
                    const cap = card.description.match(/([\d,]+)\s*pixels/);
                    if (cap) desc += ` (Max: ${cap[1]})`;
                }
                outputData.push([
                    card.name.replace('H_', ''),
                    desc + (card.description.length > 40 ? '...' : ''),
                    `$${card.price.toLocaleString()}`
                ]);
            });
            
            doc.autoTable({
                startY: yPos,
                head: [['Card Name', 'Description', 'Price']],
                body: outputData,
                theme: 'grid',
                headStyles: { fillColor: [220, 53, 69], fontSize: 9 },
                bodyStyles: { fontSize: 9 },
                margin: { left: 15, right: 15 },
                columnStyles: {
                    0: { cellWidth: 60 },
                    1: { cellWidth: 90 },
                    2: { cellWidth: 30, halign: 'right' }
                }
            });
            
            yPos = doc.lastAutoTable.finalY + 3;
            doc.setFontSize(10);
            doc.setFont(undefined, 'bold');
            doc.text(`Subtotal: $${outputSubtotal.toLocaleString()}`, 195, yPos, { align: 'right' });
            yPos += 8;
        }
        
        // Accessories Section
        if (selectedCards.accessories.length > 0) {
            doc.setFontSize(12);
            doc.setFont(undefined, 'bold');
            doc.text(`Accessories (${selectedCards.accessories.length})`, 15, yPos);
            yPos += 2;
            
            const accessoryData = [];
            selectedCards.accessories.forEach(acc => {
                accessoriesSubtotal += acc.price;
                accessoryData.push([
                    acc.name,
                    acc.description.substring(0, 50) + (acc.description.length > 50 ? '...' : ''),
                    `$${acc.price.toLocaleString()}`
                ]);
            });
            
            doc.autoTable({
                startY: yPos,
                head: [['Accessory Name', 'Description', 'Price']],
                body: accessoryData,
                theme: 'grid',
                headStyles: { fillColor: [220, 53, 69], fontSize: 9 },
                bodyStyles: { fontSize: 9 },
                margin: { left: 15, right: 15 },
                columnStyles: {
                    0: { cellWidth: 60 },
                    1: { cellWidth: 90 },
                    2: { cellWidth: 30, halign: 'right' }
                }
            });
            
            yPos = doc.lastAutoTable.finalY + 3;
            doc.setFontSize(10);
            doc.setFont(undefined, 'bold');
            doc.text(`Subtotal: $${accessoriesSubtotal.toLocaleString()}`, 195, yPos, { align: 'right' });
            yPos += 8;
        }
        
        // Price Summary Box
        yPos += 5;
        doc.setFillColor(220, 53, 69);
        doc.rect(15, yPos - 5, 180, 30, 'F');
        
        doc.setTextColor(255, 255, 255);
        doc.setFontSize(11);
        doc.setFont(undefined, 'bold');
        doc.text('PRICE SUMMARY', 20, yPos);
        
        yPos += 6;
        doc.setFontSize(10);
        doc.setFont(undefined, 'normal');
        doc.text(`Frame:`, 20, yPos);
        doc.text(`$${hSeriesConfig.framePrice.toLocaleString()}`, 190, yPos, { align: 'right' });
        yPos += 5;
        if (inputSubtotal > 0) {
            doc.text(`Input Cards:`, 20, yPos);
            doc.text(`$${inputSubtotal.toLocaleString()}`, 190, yPos, { align: 'right' });
            yPos += 5;
        }
        if (outputSubtotal > 0) {
            doc.text(`Output Cards:`, 20, yPos);
            doc.text(`$${outputSubtotal.toLocaleString()}`, 190, yPos, { align: 'right' });
            yPos += 5;
        }
        if (accessoriesSubtotal > 0) {
            doc.text(`Accessories:`, 20, yPos);
            doc.text(`$${accessoriesSubtotal.toLocaleString()}`, 190, yPos, { align: 'right' });
            yPos += 5;
        }
        
        doc.setDrawColor(255, 255, 255);
        doc.line(20, yPos, 190, yPos);
        yPos += 5;
        
        doc.setFontSize(12);
        doc.setFont(undefined, 'bold');
        doc.text(`TOTAL:`, 20, yPos);
        doc.text(`$${totalPrice.toLocaleString()}`, 190, yPos, { align: 'right' });
        
        doc.setTextColor(0, 0, 0);
        
        // Footer
        doc.setFontSize(8);
        doc.setFont(undefined, 'normal');
        doc.text('For more information visit: novastarled.com', 105, 285, { align: 'center' });
        doc.text('Contact: sales@olympianled.com | 321-747-3220', 105, 290, { align: 'center' });
        
        // Save PDF
        doc.save(`${hSeriesConfig.frameName.replace(/ /g, '_')}_Configuration.pdf`);
    };
    
    logo.onerror = function() {
        // Continue without logo
        alert('Logo failed to load, generating PDF without logo...');
        logo.onload();
    };
    
    logo.src = '/logos/LOGO_MSB_BLKBack.png';
}
function shareConfiguration() {
    const totalPrice = parseInt(document.getElementById('totalPrice').textContent.replace(/[$,]/g, ''));
    
    if (totalPrice === hSeriesConfig.framePrice) {
        alert('Please configure at least one card before sharing.');
        return;
    }
    
    // Create shareable configuration object
    const shareConfig = {
        input: selectedCards.input.map((card, i) => card ? { slot: i, name: card.name } : null).filter(c => c),
        output: selectedCards.output.map((card, i) => card ? { slot: i, name: card.name } : null).filter(c => c),
        accessories: selectedCards.accessories.map(acc => acc.name)
    };
    
    // Encode configuration as base64
    const configJson = JSON.stringify(shareConfig);
    const configEncoded = btoa(configJson);
    
    // Generate shareable URL
    const currentUrl = window.location.origin + window.location.pathname;
    const shareUrl = `${currentUrl}?config=${configEncoded}`;
    
    // Copy to clipboard
    navigator.clipboard.writeText(shareUrl).then(() => {
        alert(`Configuration link copied to clipboard!\n\nShare this link:\n${shareUrl}`);
    }).catch(() => {
        // Fallback if clipboard fails
        prompt('Copy this link to share your configuration:', shareUrl);
    });
}
function loadSharedConfiguration() {
    const urlParams = new URLSearchParams(window.location.search);
    const configParam = urlParams.get('config');
    
    if (configParam) {
        try {
            const configJson = atob(configParam);
            const shareConfig = JSON.parse(configJson);
            
            // Map card names back to card objects
            shareConfig.input.forEach(item => {
                const card = hSeriesConfig.inputCards.find(c => c.name === item.name);
                if (card) {
                    selectedCards.input[item.slot] = card;
                } else {
                    console.warn(`Card not found: ${item.name}`);
                }
            });
            
            shareConfig.output.forEach(item => {
                const card = hSeriesConfig.outputCards.find(c => c.name === item.name);
                if (card) {
                    selectedCards.output[item.slot] = card;
                } else {
                    console.warn(`Card not found: ${item.name}`);
                }
            });
            
            shareConfig.accessories.forEach(name => {
                const acc = hSeriesConfig.accessories.find(a => a.name === name);
                if (acc) {
                    selectedCards.accessories.push(acc);
                }
            });
            
            return true;
        } catch (e) {
            console.error('Failed to load shared config:', e);
            return false;
        }
    }
    return false;
}
// Auto-open customizer on page load if URL parameter is set
window.addEventListener('DOMContentLoaded', () => {
    const urlParams = new URLSearchParams(window.location.search);
    const configParam = urlParams.get('config');
    
    // Open customizer if ?configure=true or if there's a shared config
    if (urlParams.get('configure') === 'true' || configParam) {
        // Small delay to ensure page is fully loaded
        setTimeout(() => {
            openHSeriesCustomizer();
            
            // Load shared configuration AFTER customizer is open
            if (configParam) {
                setTimeout(() => {
                    if (loadSharedConfiguration()) {
                        // Refresh the UI with loaded config
                        renderSlots();
                        updateSummary();
                        // Save to sessionStorage so it persists
                        saveConfiguration();
                    }
                }, 100);
            }
        }, 500);
    }
});
// Close modals on outside click
document.addEventListener('click', (e) => {
    const customizerModal = document.getElementById('hSeriesCustomizerModal');
    const detailsModal = document.getElementById('cardDetailsModal');
    
    if (e.target === customizerModal) closeHSeriesCustomizer();
    if (e.target === detailsModal) closeCardDetails();
});
// Close card details modal on Escape key
document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && document.getElementById('cardDetailsModal').style.display === 'flex') {
        closeCardDetails();
    }
});