<!doctype html>
<html lang="en" data-theme="{% if default_theme == 'light' %}light-blend{% elif default_theme == 'dark' %}dark{% else %}light-blend{% endif %}">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
    <title>MTG Deckbuilder</title>
    <script src="https://unpkg.com/htmx.org@1.9.12" onerror="var s=document.createElement('script');s.src='/static/vendor/htmx-1.9.12.min.js';document.head.appendChild(s);"></script>
    <script>
      (function(){
        // Pre-CSS theme bootstrapping to avoid flash/mismatch on first paint
        try{
          var root = document.documentElement;
          var KEY = 'mtg:theme';
          var SERVER_DEFAULT = '{{ default_theme }}';
          var params = new URLSearchParams(window.location.search || '');
          var urlTheme = (params.get('theme') || '').toLowerCase();
          var stored = localStorage.getItem(KEY);
          function resolveSystem(){
            var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
            return prefersDark ? 'dark' : 'light-blend';
          }
          function mapTheme(v){
            var x = (v || 'system').toLowerCase();
            if (x === 'system') return resolveSystem();
            if (x === 'light') return 'light-blend';
            return x;
          }
          var initial = urlTheme || ((stored && stored.trim()) ? stored : (SERVER_DEFAULT || 'system'));
          root.setAttribute('data-theme', mapTheme(initial));
        }catch(_){ }
      })();
    </script>
    <script>
      window.__telemetryEndpoint = '/telemetry/events';
    </script>
  <link rel="stylesheet" href="/static/styles.css?v=20260319-3" />
  <link rel="stylesheet" href="/static/shared-components.css?v=20251021-1" />
  <style>
    /* Disable all transitions until page is loaded to prevent sidebar flash */
    .no-transition,
    .no-transition *,
    .no-transition *::before,
    .no-transition *::after {
      transition: none !important;
      animation: none !important;
    }
  </style>
  <!-- Performance hints -->
  <link rel="preconnect" href="https://api.scryfall.com" crossorigin>
  <link rel="dns-prefetch" href="https://api.scryfall.com">
  <!-- Favicon -->
  <link rel="icon" type="image/png" href="/static/favicon.png" />
  <link rel="shortcut icon" href="/favicon.ico" />
  <link rel="apple-touch-icon" href="/static/favicon.png" />
  {% if enable_pwa %}
  <link rel="manifest" href="/static/manifest.webmanifest" />
  {% endif %}
  </head>
  <body class="no-transition" data-diag="{% if show_diagnostics %}1{% else %}0{% endif %}" data-virt="{% if virtualize %}1{% else %}0{% endif %}">
    <header class="top-banner">
      <div class="top-inner">
        <div class="flex-row banner-left">
          <button type="button" id="nav-toggle" class="btn" aria-controls="sidebar" aria-expanded="true" title="Show/Hide navigation" style="background: transparent; color: var(--surface-banner-text); border:1px solid var(--border); flex-shrink: 0;">
            ☰ Menu
          </button>
          <h1 style="margin:0; white-space: nowrap;">MTG Deckbuilder</h1>
        </div>
        <div id="banner-status" class="banner-status" role="status" aria-live="polite"></div>
      </div>
    </header>
    <div class="layout">
      <aside id="sidebar" class="sidebar" aria-label="Primary navigation">
        <div class="brand">
          <div class="mana-dots" aria-hidden="true">
            <span class="dot green"></span>
            <span class="dot blue"></span>
            <span class="dot red"></span>
            <span class="dot white"></span>
            <span class="dot black"></span>
          </div>
        </div>
        <nav class="nav" id="primary-nav">
          <a href="/">Home</a>
          <a href="/build">Build</a>
          <a href="/configs">Build from JSON</a>
          {% if show_setup %}<a href="/setup">Setup/Tag</a>{% endif %}
          <a href="/owned">Owned Library</a>
          <a href="/cards">All Cards</a>
          {% if show_commanders %}<a href="/commanders">Commanders</a>{% endif %}
          <a href="/decks">Finished Decks</a>
          <a href="/themes/">Themes</a>
          {% if random_ui %}<a href="/random">Random</a>{% endif %}
          {% if show_diagnostics %}<a href="/diagnostics">Diagnostics</a>{% endif %}
          {% if show_logs %}<a href="/logs">Logs</a>{% endif %}
        </nav>
        {% if enable_themes %}
        <div class="sidebar-theme" role="group" aria-label="Theme">
          <label class="sidebar-theme-label" for="theme-select">Theme</label>
          <div class="sidebar-theme-row">
            <select id="theme-select" aria-label="Theme selector">
              <option value="system">System</option>
              <option value="light">Light</option>
              <option value="dark">Dark</option>
              <option value="high-contrast">High contrast</option>
              <option value="cb-friendly">Color-blind</option>
            </select>
            <button type="button" id="theme-reset" class="btn btn-ghost" title="Reset theme preference">Reset</button>
          </div>
        </div>
        {% endif %}
      </aside>
  <main class="content" data-error-surface>
        {% block content %}{% endblock %}
      </main>
    </div>
    <footer class="site-footer" role="contentinfo">
      Card images and data provided by
      <a href="https://scryfall.com" target="_blank" rel="noopener">Scryfall</a>.
      This website is not produced by, endorsed by, supported by, or affiliated with Scryfall or Wizards of the Coast.
      {% set _pba = _price_cache_ts() %}{% if _pba %}<br><span class="muted" style="font-size:.8em;">Prices as of {{ _pba }} — for live pricing visit <a href="https://scryfall.com" target="_blank" rel="noopener">Scryfall</a>.</span>{% endif %}
      <br><span class="muted" style="font-size:.8em;">
        <a href="https://github.com/mwisnowski/mtg_python_deckbuilder" target="_blank" rel="noopener">GitHub</a>
        &middot;
        <a href="https://github.com/mwisnowski/mtg_python_deckbuilder/issues" target="_blank" rel="noopener">Report an Issue</a>
        &middot;
        <a href="https://github.com/mwisnowski/mtg_python_deckbuilder/issues/new?template=other-feature-request-.md" target="_blank" rel="noopener">Request a Feature</a>
        &middot;
        <a href="https://hub.docker.com/repository/docker/mwisnowski/mtg-python-deckbuilder/general" target="_blank" rel="noopener">DockerHub</a>
      </span>
    </footer>
    <!-- Card hover, theme badges, and DFC toggle styles moved to tailwind.css 2025-10-21 -->
    <style>
      .nav a.active { font-weight:600; position:relative; }
      .nav a.active::after { content:''; position:absolute; left:0; bottom:2px; width:100%; height:2px; background:var(--accent, #38bdf8); border-radius:2px; }
    </style>
    <script>
      (function(){
        // Sidebar toggle and persistence
        try{
          var BODY = document.body;
          var SIDEBAR = document.getElementById('sidebar');
          var TOGGLE = document.getElementById('nav-toggle');
          var KEY = 'mtg:navCollapsed';
          
          function apply(collapsed){
            if (collapsed){
              BODY.classList.add('nav-collapsed');
              TOGGLE && TOGGLE.setAttribute('aria-expanded', 'false');
              SIDEBAR && SIDEBAR.setAttribute('aria-hidden', 'true');
            } else {
              BODY.classList.remove('nav-collapsed');
              TOGGLE && TOGGLE.setAttribute('aria-expanded', 'true');
              SIDEBAR && SIDEBAR.setAttribute('aria-hidden', 'false');
            }
          }
          // Initial state: respect saved pref, else collapse on small screens
          var saved = localStorage.getItem(KEY);
          var initialCollapsed = (saved === '1') || (saved === null && (window.innerWidth || 0) < 900);
          apply(initialCollapsed);
          
          // Re-enable transitions after page is fully loaded
          // Use longer delay for pages with heavy content (like card browser)
          var enableTransitions = function(){
            BODY.classList.remove('no-transition');
          };
          
          if (document.readyState === 'complete') {
            // Already loaded
            setTimeout(enableTransitions, 150);
          } else {
            window.addEventListener('load', function(){
              setTimeout(enableTransitions, 150);
            });
          }
          
          if (TOGGLE){
            TOGGLE.addEventListener('click', function(){
              var isCollapsed = BODY.classList.contains('nav-collapsed');
              apply(!isCollapsed);
              try{ localStorage.setItem(KEY, (!isCollapsed) ? '1' : '0'); }catch(_){ }
            });
          }
          // Keep ARIA in sync on resize for first-load default when no pref yet
          window.addEventListener('resize', function(){
            // Do not override if user has an explicit preference saved
            if (localStorage.getItem(KEY) !== null) return;
            apply((window.innerWidth || 0) < 900);
          });
        }catch(_){ }

        // Suppress sidebar transitions during HTMX partial updates (not full page loads)
        document.addEventListener('htmx:beforeRequest', function(evt){
          // Only suppress for small partial updates (identified by specific IDs)
          var target = evt.detail && evt.detail.target;
          if (target && target.id) {
            var targetId = target.id;
            // List of partial update containers that should suppress sidebar transitions
            var partialContainers = ['similar-cards-container', 'card-list', 'theme-list'];
            if (partialContainers.indexOf(targetId) !== -1 || targetId.indexOf('-container') !== -1) {
              document.body.classList.add('htmx-settling');
            }
          }
        });
        document.addEventListener('htmx:afterSettle', function(){
          document.body.classList.remove('htmx-settling');
        });

        // Setup/Tagging status poller moved to app.ts (initSetupStatusPoller)
        
        // Expose normalizeCardName for cardImages module
        window.__normalizeCardName = function(raw){
          if(!raw) return raw;
          var m = /(.*?)(\s*-\s*Synergy\s*\(.*\))$/i.exec(raw);
          if(m){ return m[1].trim(); }
          return raw;
        };
      })();
    </script>
    <script>
      // Overlay flip button + persistence + accessibility for double-faced cards
      (function(){
        var FACE_ATTR = 'data-current-face';
        var LS_PREFIX = 'mtg:face:';
        var DEBOUNCE_MS = 120; // prevent rapid flip spamming / extra fetches
        var lastFlip = 0;
        var normalize = (window.__normalizeCardName) ? window.__normalizeCardName : function(raw){
          if(!raw) return raw;
          var m = /(.*?)(\s*-\s*Synergy\s*\(.*\))$/i.exec(raw);
          if(m){ return m[1].trim(); }
          return raw;
        };
        window.__normalizeCardName = normalize;
        function getCardData(card, attr){
          if(!card) return '';
          var val = card.getAttribute(attr);
          if(val) return val;
          var node = card.querySelector && card.querySelector('['+attr+']');
          return node ? node.getAttribute(attr) : '';
        }
        function hasTwoFaces(card){
          if(!card) return false;
          
          // Check if card has a layout attribute - this is the source of truth
          var layout = card.getAttribute('data-layout') || '';
          if(layout) {
            // Only these layouts are actual flippable double-faced cards
            var flippableLayouts = ['modal_dfc', 'transform', 'reversible_card', 'flip', 'meld'];
            return flippableLayouts.indexOf(layout) > -1;
          }
          
          // Fallback: If no layout data, check if name has // (backwards compatibility)
          // This shouldn't happen if templates properly pass data-layout
          var name = normalize(getCardData(card, 'data-card-name')) + ' ' + normalize(getCardData(card, 'data-original-name'));
          return name.indexOf('//') > -1;
        }
        window.__dfcHasTwoFaces = hasTwoFaces;  // Expose globally for popup hover panel
        function keyFor(card){
          var nm = normalize(getCardData(card, 'data-card-name') || getCardData(card, 'data-original-name') || '').toLowerCase();
          return LS_PREFIX + nm;
        }
        function applyStoredFace(card){
          try {
            var k = keyFor(card);
            var val = localStorage.getItem(k);
            if(val === 'front' || val === 'back') card.setAttribute(FACE_ATTR, val);
          } catch(_){}
        }
        function storeFace(card, face){
          try { localStorage.setItem(keyFor(card), face); } catch(_){}
        }
        function announce(face, card){
          var live = document.getElementById('dfc-live');
          if(!live){
            live = document.createElement('div');
            live.id = 'dfc-live'; live.className='sr-only'; live.setAttribute('aria-live','polite');
            document.body.appendChild(live);
          }
          var nm = normalize(getCardData(card, 'data-card-name')||'').split('//')[0].trim();
          live.textContent = 'Showing ' + (face==='front'?'front face':'back face') + ' of ' + nm;
        }
        function updateButton(btn, face){
          btn.setAttribute('data-face', face);
          btn.setAttribute('aria-label', face==='front' ? 'Flip to back face' : 'Flip to front face');
          btn.innerHTML = '<span class="icon" aria-hidden="true" style="font-size:18px;">⥮</span>';
        }
        window.__dfcUpdateButton = updateButton;
        function ensureButton(card){
          if(!hasTwoFaces(card)) return;
          if(card.querySelector('.dfc-toggle')) return;
          card.classList.add('dfc-host');
          var resolvedName = getCardData(card, 'data-card-name');
          var resolvedOriginal = getCardData(card, 'data-original-name');
          if(resolvedName && !card.hasAttribute('data-card-name')) card.setAttribute('data-card-name', resolvedName);
          if(resolvedOriginal && !card.hasAttribute('data-original-name')) card.setAttribute('data-original-name', resolvedOriginal);
          applyStoredFace(card);
          var face = card.getAttribute(FACE_ATTR) || 'front';
          var btn = document.createElement('button');
          btn.type='button';
          // Mobile: flip in popup only (flex below md). Desktop: flip in thumbnails only (hidden at md+)
          var inPopup = card.closest && card.closest('#hover-card-panel');
          btn.className = inPopup ? 'dfc-toggle flex md:hidden' : 'dfc-toggle hidden md:flex';
          btn.setAttribute('aria-pressed','false');
          btn.setAttribute('tabindex','0');
          btn.addEventListener('click', function(ev){ ev.stopPropagation(); flip(card, btn); });
          btn.addEventListener('keydown', function(ev){ if(ev.key==='Enter' || ev.key===' ' || ev.key==='f' || ev.key==='F'){ ev.preventDefault(); flip(card, btn); }});
          updateButton(btn, face);
          if(card.classList.contains('list-row')){
            btn.classList.add('dfc-toggle-inline');
            var slot = card.querySelector('.flip-slot');
            if(slot){
              slot.innerHTML='';
              slot.appendChild(btn);
              slot.removeAttribute('aria-hidden');
            } else {
              var anchor = card.querySelector('.dfc-anchor');
              if(anchor){ anchor.insertAdjacentElement('afterend', btn); }
              else if(card.lastElementChild){ card.insertBefore(btn, card.lastElementChild); }
              else { card.appendChild(btn); }
            }
          } else {
            card.insertBefore(btn, card.firstChild);
          }
        }
        window.__dfcEnsureButton = ensureButton;  // Expose for hover panel use
        function flip(card, btn){
          var now = Date.now();
            if(now - lastFlip < DEBOUNCE_MS) return;
            lastFlip = now;
          var cur = card.getAttribute(FACE_ATTR) || 'front';
          var next = cur === 'front' ? 'back' : 'front';
          card.setAttribute(FACE_ATTR, next);
          storeFace(card, next);
          if(btn) updateButton(btn, next);
          // visual cue
          card.style.outline='2px solid var(--accent)'; setTimeout(function(){ card.style.outline=''; }, 160);
          announce(next, card);
          // retrigger hover update under pointer if applicable
          if(window.__hoverShowCard){ window.__hoverShowCard(card); }
          if(window.__dfcNotifyHover){ try{ window.__dfcNotifyHover(card, next); }catch(_){ } }
        }
        window.__dfcFlipCard = function(card){ if(!card) return; flip(card, card.querySelector('.dfc-toggle')); };
        window.__dfcGetFace = function(card){ if(!card) return 'front'; return card.getAttribute(FACE_ATTR) || 'front'; };
        function scan(){
          document.querySelectorAll('.card-sample, .commander-cell, .commander-thumb, .commander-card, .card-tile, .candidate-tile, .stack-card, .card-preview, .owned-row, .list-row').forEach(ensureButton);
        }
        document.addEventListener('pointermove', function(e){ window.__lastPointerEvent = e; }, { passive:true });
        document.addEventListener('DOMContentLoaded', scan);
        document.addEventListener('htmx:afterSwap', scan);
        // Expose for debugging
        window.__dfcScan = scan;
        // MutationObserver to re-inject buttons if card tiles are replaced (e.g., HTMX swaps, dynamic filtering)
        var moDebounce = null;
        var observer = new MutationObserver(function(muts){
          if(moDebounce) cancelAnimationFrame(moDebounce);
          moDebounce = requestAnimationFrame(function(){ scan(); });
        });
        try { observer.observe(document.body, { childList:true, subtree:true }); } catch(_){ }
      })();
    </script>
    <!-- Active nav highlighter moved to app.ts (initActiveNavHighlighter) -->
  <script src="/static/js/components.js?v=20251028-1"></script>
  <script src="/static/js/app.js?v=20251029-2"></script>
  <script src="/static/js/cardImages.js?v=20251029-1"></script>
  <script src="/static/js/cardHover.js?v=20251028-1"></script>
  {% if enable_themes %}
  <script>
    // Theme selector logic moved to app.ts (initThemeSelector)
    // Call it with server-injected values on DOMContentLoaded
    document.addEventListener('DOMContentLoaded', function(){
      if (window.__initThemeSelector) {
        window.__initThemeSelector({{ enable_themes|lower }}, '{{ default_theme }}');
      }
    });
  </script>
  {% endif %}
  {% if not enable_themes %}
  <script>
    // Theme env-only logic moved to app.ts (initThemeEnvOnly)
    // Call it with server-injected values on DOMContentLoaded
    document.addEventListener('DOMContentLoaded', function(){
      if (window.__initThemeEnvOnly) {
        window.__initThemeEnvOnly({{ enable_themes|lower }}, '{{ default_theme }}');
      }
    });
  </script>
  {% endif %}
  {% if enable_pwa %}
  <script>
    // Service worker registration moved to app.ts (initServiceWorker)
    // Call it with server-injected values on DOMContentLoaded
    document.addEventListener('DOMContentLoaded', function(){
      if (window.__initServiceWorker) {
        window.__initServiceWorker({{ enable_pwa|lower }}, '{{ catalog_hash|default("dev") }}');
      }
    });
  </script>
  {% endif %}
  <!-- Toast after reload, setup poller, nav highlighter moved to app.ts -->
  <!-- Hover card panel system moved to cardHover.ts -->
  <!-- Price tooltip: lightweight fetch on mouseenter for .card-name-price-hover -->
  <script>
  (function(){
    var _priceCache = {};
    var _tip = null;
    function _showTip(el, text) {
      if (!_tip) {
        _tip = document.createElement('div');
        _tip.className = 'card-price-tip';
        document.body.appendChild(_tip);
      }
      _tip.textContent = text;
      var r = el.getBoundingClientRect();
      _tip.style.left = (r.left + r.width/2 + window.scrollX) + 'px';
      _tip.style.top  = (r.top + window.scrollY - 4) + 'px';
      _tip.style.transform = 'translate(-50%, -100%)';
      _tip.style.display = 'block';
    }
    function _hideTip() { if (_tip) _tip.style.display = 'none'; }
    document.addEventListener('mouseover', function(e) {
      var el = e.target && e.target.closest && e.target.closest('.card-name-price-hover');
      if (!el) return;
      var name = el.dataset.cardName || el.textContent.trim();
      if (!name) return;
      if (_priceCache[name] !== undefined) {
        _showTip(el, _priceCache[name]);
        return;
      }
      _showTip(el, 'Loading price...');
      fetch('/api/price/' + encodeURIComponent(name))
        .then(function(r){ return r.ok ? r.json() : null; })
        .then(function(d){
          var label = (d && d.found && d.price != null) ? ('$' + parseFloat(d.price).toFixed(2)) : 'Price unavailable';
          _priceCache[name] = label;
          _showTip(el, label);
        })
        .catch(function(){ _priceCache[name] = 'Price unavailable'; });
    });
    document.addEventListener('mouseout', function(e) {
      var el = e.target && e.target.closest && e.target.closest('.card-name-price-hover');
      if (el) _hideTip();
    });
  })();
  </script>
  <!-- Budget price display: injects prices on card tiles and list rows, tracks running total -->
  <script>
  (function(){
    var BASIC_LANDS = new Set([
      'Plains','Island','Swamp','Mountain','Forest','Wastes',
      'Snow-Covered Plains','Snow-Covered Island','Snow-Covered Swamp',
      'Snow-Covered Mountain','Snow-Covered Forest'
    ]);
    var _priceNum = {}; // card name -> float|null
    var _deckPrices = {}; // accumulated across build stages: card name -> float
    var _buildToken = null;
    function _fetchNum(name) {
      if (_priceNum.hasOwnProperty(name)) return Promise.resolve(_priceNum[name]);
      return fetch('/api/price/' + encodeURIComponent(name))
        .then(function(r){ return r.ok ? r.json() : null; })
        .then(function(d){
          var p = (d && d.found && d.price != null) ? parseFloat(d.price) : null;
          _priceNum[name] = p; return p;
        }).catch(function(){ _priceNum[name] = null; return null; });
    }
    function _getBuildToken() {
      var el = document.querySelector('[data-build-id]');
      return el ? el.getAttribute('data-build-id') : null;
    }
    function _cfg() { return window._budgetCfg || null; }
    function initPriceDisplay() {
      var tok = _getBuildToken();
      if (tok !== null && tok !== _buildToken) { _buildToken = tok; _deckPrices = {}; }
      var cfg = _cfg();
      var ceiling = cfg && cfg.card_ceiling ? parseFloat(cfg.card_ceiling) : null;
      var totalCap = cfg && cfg.total ? parseFloat(cfg.total) : null;

      function updateRunningTotal(prevTotal) {
        var chip = document.getElementById('budget-running');
        if (!chip) return;
        var total = Object.values(_deckPrices).reduce(function(s,p){ return s + (p||0); }, 0);
        chip.textContent = total.toFixed(2);
        var chipWrap = chip.closest('.chip');
        if (chipWrap && totalCap !== null) chipWrap.classList.toggle('chip-warn', total > totalCap);
        if (prevTotal !== undefined) {
          var stepAdded = total - prevTotal;
          var stepEl = document.getElementById('budget-step');
          if (stepEl && stepAdded > 0.005) {
            stepEl.textContent = '+$' + stepAdded.toFixed(2) + ' this step';
            stepEl.style.display = '';
          }
        }
      }

      var overlays = document.querySelectorAll('.card-price-overlay[data-price-for]');
      var inlines = document.querySelectorAll('.card-price-inline[data-price-for]');
      var toFetch = new Set();
      overlays.forEach(function(el){ var n = el.dataset.priceFor; if (n && !BASIC_LANDS.has(n)) toFetch.add(n); });
      inlines.forEach(function(el){ var n = el.dataset.priceFor; if (n && !BASIC_LANDS.has(n)) toFetch.add(n); });

      // Always refresh the running total chip even when there's nothing new to fetch
      updateRunningTotal();
      if (!toFetch.size) return;

      var prevTotal = Object.values(_deckPrices).reduce(function(s,p){ return s + (p||0); }, 0);
      var promises = [];
      toFetch.forEach(function(name){ promises.push(_fetchNum(name).then(function(p){ return {name:name,price:p}; })); });
      Promise.all(promises).then(function(results){
        var map = {};
        var prevTotal2 = Object.values(_deckPrices).reduce(function(s,p){ return s + (p||0); }, 0);
        results.forEach(function(r){ map[r.name] = r.price; if (r.price !== null) _deckPrices[r.name] = r.price; });
        overlays.forEach(function(el){
          var name = el.dataset.priceFor;
          if (!name || BASIC_LANDS.has(name)) { el.style.display='none'; return; }
          var p = map[name];
          el.textContent = p !== null ? ('$' + p.toFixed(2)) : '';
          if (ceiling !== null && p !== null && p > ceiling) {
            var tile = el.closest('.card-tile,.stack-card');
            if (tile) tile.classList.add('over-budget');
          }
        });
        inlines.forEach(function(el){
          var name = el.dataset.priceFor;
          if (!name || BASIC_LANDS.has(name)) { el.style.display='none'; return; }
          var p = map[name];
          el.textContent = p !== null ? ('$' + p.toFixed(2)) : '';
          if (ceiling !== null && p !== null && p > ceiling) {
            var row = el.closest('.list-row');
            if (row) row.classList.add('over-budget');
          }
        });
        // Update running total chip with per-step delta
        updateRunningTotal(prevTotal2);
        // Update summary budget bar
        var bar = document.getElementById('budget-summary-bar');
        if (bar) {
          var allNames = new Set();
          var sumTotal = 0;
          document.querySelectorAll('.card-price-overlay[data-price-for],.card-price-inline[data-price-for]').forEach(function(el){
            var n = el.dataset.priceFor;
            if (n && !BASIC_LANDS.has(n) && !allNames.has(n)) {
              allNames.add(n);
              sumTotal += (map[n] || 0);
            }
          });
          if (totalCap !== null) {
            var over = sumTotal > totalCap;
            bar.textContent = 'Estimated deck cost: $' + sumTotal.toFixed(2) + ' / $' + totalCap.toFixed(2) + (over ? ' — over budget' : ' — under budget');
            bar.className = over ? 'budget-price-bar over' : 'budget-price-bar under';
          } else {
            bar.textContent = 'Estimated deck cost: $' + sumTotal.toFixed(2) + ' (basic lands excluded)';
            bar.className = 'budget-price-bar';
          }
        }
      });
    }
    document.addEventListener('DOMContentLoaded', function(){ initPriceDisplay(); });
    document.addEventListener('htmx:afterSwap', function(){ setTimeout(initPriceDisplay, 80); });
  })();
  </script>
  </body>
  </html>
