<!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>
      // Ensure legacy hover system never initializes (set before its script executes)
      window.__disableLegacyCardHover = true;
    </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=20250911-1" />
  <!-- 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 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 style="display:flex; align-items:center; gap:.5rem; padding-left: 1rem;">
          <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);">
            ☰ Menu
          </button>
          <h1 style="margin:0;">MTG Deckbuilder</h1>
        </div>
        <div style="display:flex; align-items:center; gap:.5rem">
          <span id="health-dot" class="health-dot" title="Health"></span>
          <div id="banner-status" class="banner-status">{% block banner_subtitle %}{% endblock %}</div>
          <button type="button" id="btn-open-permalink" class="btn" title="Open a saved permalink"
            onclick="(function(){try{var token = prompt('Paste a /build/from?state=... URL or token:'); if(!token) return; var m = token.match(/state=([^&]+)/); var t = m? m[1] : token.trim(); if(!t) return; window.location.href = '/build/from?state=' + encodeURIComponent(t); }catch(_){}})()">Open Permalink…</button>
          {# Theme controls moved to sidebar #}
        </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>
          {% 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.
    </footer>
    <style>
      .card-hover { position: fixed; pointer-events: none; z-index: 9999; display: none; }
      .card-hover-inner { display:flex; gap:12px; align-items:flex-start; }
    .card-hover img { width: 320px; height: auto; display: block; border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,.55); border: 1px solid var(--border); background: var(--panel); }
    .card-hover .dual {
      display:flex; gap:12px; align-items:flex-start;
    }
  .card-meta { background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: .5rem .6rem; max-width: 320px; font-size: 13px; line-height: 1.4; box-shadow: 0 6px 18px rgba(0,0,0,.35); }
  .card-meta ul { margin:.25rem 0; padding-left: 1.1rem; list-style: disc; }
  .card-meta li { margin:.1rem 0; }
  .card-meta .themes-list { font-size: 18px; line-height: 1.35; }
  /* Global theme badge styles (moved from picker for reuse on standalone pages) */
  .theme-badge { display:inline-block; padding:2px 6px; border-radius:12px; font-size:10px; background: var(--panel-alt); border:1px solid var(--border); letter-spacing:.5px; }
  .theme-synergies { font-size:11px; opacity:.85; display:flex; flex-wrap:wrap; gap:4px; }
  .badge-fallback { background:#7f1d1d; color:#fff; }
  .badge-quality-draft { background:#4338ca; color:#fff; }
  .badge-quality-reviewed { background:#065f46; color:#fff; }
  .badge-quality-final { background:#065f46; color:#fff; font-weight:600; }
  .badge-pop-vc { background:#065f46; color:#fff; }
  .badge-pop-c { background:#047857; color:#fff; }
  .badge-pop-u { background:#0369a1; color:#fff; }
  .badge-pop-n { background:#92400e; color:#fff; }
  .badge-pop-r { background:#7f1d1d; color:#fff; }
  .badge-curated { background:#4f46e5; color:#fff; }
  .badge-enforced { background:#334155; color:#fff; }
  .badge-inferred { background:#57534e; color:#fff; }
  .theme-detail-card { background:var(--panel); padding:1rem 1.1rem; border:1px solid var(--border); border-radius:10px; box-shadow:0 2px 6px rgba(0,0,0,.25); }
  .theme-detail-card h3 { margin-top:0; margin-bottom:.4rem; }
  .theme-detail-card .desc { margin-top:0; font-size:13px; line-height:1.45; }
  .theme-detail-card h4 { margin-bottom:.35rem; margin-top:.85rem; font-size:13px; letter-spacing:.05em; text-transform:uppercase; opacity:.85; }
  .breadcrumb { font-size:12px; margin-bottom:.4rem; }
  .card-meta .label { color:#94a3b8; text-transform: uppercase; font-size: 10px; letter-spacing: .04em; display:block; margin-bottom:.15rem; }
  .card-meta .themes-label { color: var(--text); font-size: 20px; letter-spacing: .05em; }
      .card-meta .line + .line { margin-top:.35rem; }
  .site-footer { margin: 8px 16px; padding: 8px 12px; border-top: 1px solid var(--border); color: #94a3b8; font-size: 12px; text-align: center; }
      .site-footer a { color: #cbd5e1; text-decoration: underline; }
  footer.site-footer { flex-shrink: 0; }
  /* Hide hover preview on narrow screens to avoid covering content */
  @media (max-width: 900px){
    .card-hover{ display: none !important; }
  }
      .card-hover .themes-list li.overlap { color:#0ea5e9; font-weight:600; }
  .card-hover .ov-chip { display:inline-block; background:#38bdf8; color:#102746; border:1px solid #0f3a57; border-radius:12px; padding:2px 6px; font-size:11px; margin-right:4px; font-weight:600; }
  /* Two-faced: keep full single-card width; allow wrapping on narrow viewport */
  .card-hover .dual.two-faced img { width:320px; }
  .card-hover .dual.two-faced { gap:8px; }
  /* Combo (two distinct cards) keep larger but slightly reduced to fit side-by-side */
  .card-hover .dual.combo img { width:300px; }
  @media (max-width: 1100px){
    .card-hover .dual.two-faced img { width:280px; }
    .card-hover .dual.combo img { width:260px; }
  }
  /* Unified hover-card-panel styling parity */
  #hover-card-panel.is-payoff { border-color: var(--accent, #38bdf8); box-shadow:0 6px 24px rgba(0,0,0,.65), 0 0 0 1px var(--accent, #38bdf8) inset; }
  #hover-card-panel.is-payoff .hcp-img { border-color: var(--accent, #38bdf8); }
  /* Inline theme/tag list styling (unifies legacy second panel) */
  /* Two-column hover layout */
  #hover-card-panel .hcp-body { display:grid; grid-template-columns: 320px 1fr; gap:18px; align-items:start; }
  #hover-card-panel .hcp-img-wrap { grid-column:1 / 2; }
  #hover-card-panel.compact-img .hcp-body { grid-template-columns: 120px 1fr; }
  #hover-card-panel.hcp-simple { width:auto !important; max-width:min(360px, 90vw) !important; padding:12px !important; height:auto !important; max-height:none !important; overflow:hidden !important; }
  #hover-card-panel.hcp-simple .hcp-body { display:flex; flex-direction:column; gap:12px; align-items:center; }
  #hover-card-panel.hcp-simple .hcp-right { display:none !important; }
  #hover-card-panel.hcp-simple .hcp-img { max-width:100%; }
  /* Tag list as multi-column list instead of pill chips for readability */
  #hover-card-panel .hcp-taglist { columns:2; column-gap:18px; font-size:13px; line-height:1.3; margin:6px 0 6px; padding:0; list-style:none; max-height:180px; overflow:auto; }
  #hover-card-panel .hcp-taglist li { break-inside:avoid; padding:2px 0 2px 0; position:relative; }
  #hover-card-panel .hcp-taglist li.overlap { font-weight:600; color:var(--accent,#38bdf8); }
  #hover-card-panel .hcp-taglist li.overlap::before { content:'•'; color:var(--accent,#38bdf8); position:absolute; left:-10px; }
  #hover-card-panel .hcp-overlaps { font-size:10px; line-height:1.25; margin-top:2px; }
  #hover-card-panel .hcp-ov-chip { display:inline-flex; align-items:center; background:var(--accent,#38bdf8); color:#102746; border:1px solid rgba(10,54,82,.6); border-radius:9999px; padding:3px 10px; font-size:13px; margin-right:6px; margin-top:4px; font-weight:500; letter-spacing:.02em; }
  /* Hide modal-specific close button outside modal host */
  #preview-close-btn { display:none; }
  #theme-preview-modal #preview-close-btn { display:inline-flex; }
  /* Overlay flip toggle for double-faced cards */
  .dfc-host { position:relative; }
  .dfc-toggle { position:absolute; top:6px; left:6px; z-index:5; background:rgba(15,23,42,.82); color:#fff; border:1px solid #475569; border-radius:50%; width:36px; height:36px; padding:0; font-size:16px; cursor:pointer; line-height:1; display:flex; align-items:center; justify-content:center; opacity:.92; backdrop-filter: blur(3px); }
  .dfc-toggle:hover, .dfc-toggle:focus { opacity:1; box-shadow:0 0 0 2px rgba(56,189,248,.35); outline:none; }
  .dfc-toggle:active { transform: translateY(1px); }
  .dfc-toggle .icon { font-size:12px; }
  .dfc-toggle[data-face='back'] { background:rgba(76,29,149,.85); }
  .dfc-toggle[data-face='front'] { background:rgba(15,23,42,.82); }
  .dfc-toggle[aria-pressed='true'] { box-shadow:0 0 0 2px var(--accent, #38bdf8); }
  .list-row .dfc-toggle { position:static; width:auto; height:auto; border-radius:6px; padding:2px 8px; font-size:12px; opacity:1; backdrop-filter:none; margin-left:4px; }
  .list-row .dfc-toggle .icon { font-size:12px; }
  .list-row .dfc-toggle[data-face='back'] { background:rgba(76,29,149,.3); }
  .list-row .dfc-toggle[data-face='front'] { background:rgba(56,189,248,.2); }
  #hover-card-panel.mobile { left:50% !important; top:50% !important; bottom:auto !important; transform:translate(-50%, -50%); width:min(94vw, 460px) !important; max-height:88vh; overflow-y:auto; padding:20px 22px; pointer-events:auto !important; }
  #hover-card-panel.mobile .hcp-body { display:flex; flex-direction:column; gap:20px; }
  #hover-card-panel.mobile .hcp-img { width:100%; max-width:min(90vw, 420px) !important; margin:0 auto; }
  #hover-card-panel.mobile .hcp-right { width:100%; display:flex; flex-direction:column; gap:10px; align-items:flex-start; }
  #hover-card-panel.mobile .hcp-header { flex-wrap:wrap; gap:8px; align-items:flex-start; }
  #hover-card-panel.mobile .hcp-role { font-size:12px; letter-spacing:.55px; }
  #hover-card-panel.mobile .hcp-meta { font-size:13px; text-align:left; }
  #hover-card-panel.mobile .hcp-overlaps { display:flex; flex-wrap:wrap; gap:6px; width:100%; }
  #hover-card-panel.mobile .hcp-overlaps .hcp-ov-chip { margin:0; }
  #hover-card-panel.mobile .hcp-taglist { columns:1; display:flex; flex-wrap:wrap; gap:6px; margin:4px 0 2px; max-height:none; overflow:visible; padding:0; }
  #hover-card-panel.mobile .hcp-taglist li { background:rgba(37,99,235,.18); border-radius:9999px; padding:4px 10px; display:inline-flex; align-items:center; }
  #hover-card-panel.mobile .hcp-taglist li.overlap { background:rgba(37,99,235,.28); color:#dbeafe; }
  #hover-card-panel.mobile .hcp-taglist li.overlap::before { display:none; }
  #hover-card-panel.mobile .hcp-reasons { max-height:220px; width:100%; }
  #hover-card-panel.mobile .hcp-tags { word-break:normal; white-space:normal; text-align:left; width:100%; font-size:12px; opacity:.7; }
  #hover-card-panel .hcp-close { appearance:none; border:none; background:transparent; color:#9ca3af; font-size:18px; line-height:1; padding:2px 4px; cursor:pointer; border-radius:6px; display:none; }
  #hover-card-panel .hcp-close:focus { outline:2px solid rgba(59,130,246,.6); outline-offset:2px; }
  #hover-card-panel.mobile .hcp-close { display:inline-flex; }
  /* Fade transition for hover panel image */
  #hover-card-panel .hcp-img { transition: opacity .22s ease; }
  .sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
    </style>
    <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);
          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(_){ }

        // Setup/Tagging status poller
        var statusEl;
        function ensureStatusEl(){
          if (!statusEl) statusEl = document.getElementById('banner-status');
          return statusEl;
        }
    function renderSetupStatus(data){
      var el = ensureStatusEl(); if (!el) return;
      if (data && data.running) {
        var msg = (data.message || 'Preparing data...');
        var pct = (typeof data.percent === 'number') ? data.percent : null;
        // Suppress banner if we're effectively finished (>=99%) or message is purely theme catalog refreshed
        var suppress = false;
        if (pct !== null && pct >= 99) suppress = true;
        var lm = (msg || '').toLowerCase();
        if (lm.indexOf('theme catalog refreshed') >= 0) suppress = true;
        if (suppress) {
          if (el.innerHTML) { el.innerHTML=''; el.classList.remove('busy'); }
          return;
        }
        el.innerHTML = '<strong>Setup/Tagging:</strong> ' + msg + ' <a href="/setup/running" style="margin-left:.5rem;">View progress</a>';
        el.classList.add('busy');
      } else if (data && data.phase === 'done') {
        el.innerHTML = '';
        el.classList.remove('busy');
      } else if (data && data.phase === 'error') {
        el.innerHTML = '<span class="error">Setup error.</span>';
        setTimeout(function(){ el.innerHTML = ''; el.classList.remove('busy'); }, 5000);
      } else {
        if (!el.innerHTML.trim()) el.innerHTML = '';
        el.classList.remove('busy');
      }
    }
        function pollStatus(){
          try {
            fetch('/status/setup', { cache: 'no-store' })
              .then(function(r){ return r.json(); })
              .then(renderSetupStatus)
              .catch(function(){ /* noop */ });
          } catch(e) {}
        }
        // Poll every 10 seconds instead of 3 to reduce server load (only for header indicator)
        setInterval(pollStatus, 10000);
        pollStatus();

        // Health indicator poller
        var healthDot = document.getElementById('health-dot');
        function renderHealth(data){
          if (!healthDot) return;
          var ok = data && data.status === 'ok';
          healthDot.setAttribute('data-state', ok ? 'ok' : 'bad');
          if (!ok) { healthDot.title = 'Degraded'; } else { healthDot.title = 'OK'; }
        }
        function pollHealth(){
          try {
            fetch('/healthz', { cache: 'no-store' })
              .then(function(r){ return r.json(); })
              .then(renderHealth)
              .catch(function(){ renderHealth({ status: 'bad' }); });
          } catch(e){ renderHealth({ status: 'bad' }); }
        }
        setInterval(pollHealth, 5000);
        pollHealth();

  function ensureCard() {
    // Legacy large image hover kept for fallback; disabled in favor of unified hover-card-panel
    if (window.__disableLegacyCardHover) return document.getElementById('card-hover') || document.createElement('div');
          var pop = document.getElementById('card-hover');
          if (!pop) {
            pop = document.createElement('div');
            pop.id = 'card-hover';
            pop.className = 'card-hover';
            var inner = document.createElement('div');
            inner.className = 'card-hover-inner';
            var img = document.createElement('img');
            img.alt = 'Card preview';
            var img2 = document.createElement('img');
            img2.alt = 'Card preview'; img2.style.display = 'none';
            var meta = document.createElement('div');
            meta.className = 'card-meta';
            var dual = document.createElement('div');
            dual.className = 'dual';
            dual.appendChild(img);
            dual.appendChild(img2);
            inner.appendChild(dual);
            inner.appendChild(meta);
            pop.appendChild(inner);
            document.body.appendChild(pop);
          }
          return pop;
        }
  var cardPop = ensureCard();
        var PREVIEW_VERSIONS = ['normal','large'];
        function normalizeCardName(raw){
          if(!raw) return raw;
          // Strip ' - Synergy (...' annotation if present
            var m = /(.*?)(\s*-\s*Synergy\s*\(.*\))$/i.exec(raw);
            if(m){ return m[1].trim(); }
          return raw;
        }
        function buildCardUrl(name, version, nocache, face){
          name = normalizeCardName(name);
          var q = encodeURIComponent(name||'');
          var url = 'https://api.scryfall.com/cards/named?fuzzy=' + q + '&format=image&version=' + (version||'normal');
          if (face === 'back') url += '&face=back';
          if (nocache) url += '&t=' + Date.now();
          return url;
        }
        // Generic Scryfall image URL builder
        function buildScryfallImageUrl(name, version, nocache){
          name = normalizeCardName(name);
          var q = encodeURIComponent(name||'');
          var url = 'https://api.scryfall.com/cards/named?fuzzy=' + q + '&format=image&version=' + (version||'normal');
          if (nocache) url += '&t=' + Date.now();
          return url;
        }

        // Global image retry binding for any <img data-card-name>
        var IMG_FLAG = '__cardImgRetry';
        function bindCardImageRetry(img, versions){
          try {
            if (!img || img[IMG_FLAG]) return;
            var name = img.getAttribute('data-card-name') || '';
            if (!name) return;
            img[IMG_FLAG] = { vi: 0, nocache: 0, versions: versions && versions.length ? versions.slice() : ['normal','large'] };
            img.addEventListener('error', function(){
              var st = img[IMG_FLAG];
              if (!st) return;
              if (st.vi < st.versions.length - 1){
                st.vi += 1;
                img.src = buildScryfallImageUrl(name, st.versions[st.vi], false);
              } else if (!st.nocache){
                st.nocache = 1;
                img.src = buildScryfallImageUrl(name, st.versions[st.vi], true);
              }
            });
            // If the initial load already failed before binding, try the next immediately
            if (img.complete && img.naturalWidth === 0){
              // If src corresponds to the first version, move to next; else, just force a reload
              var st = img[IMG_FLAG];
              var current = img.src || '';
              var first = buildScryfallImageUrl(name, st.versions[0], false);
              if (current.indexOf(encodeURIComponent(name)) !== -1 && current.indexOf('version='+st.versions[0]) !== -1){
                st.vi = Math.min(1, st.versions.length - 1);
                img.src = buildScryfallImageUrl(name, st.versions[st.vi], false);
              } else {
                // Re-trigger current request (may succeed if transient)
                img.src = current;
              }
            }
          } catch(_){}
        }
        function bindAllCardImageRetries(){
          document.querySelectorAll('img[data-card-name]').forEach(function(img){
            // Use thumbnail fallbacks for card-thumb, otherwise preview fallbacks
            var versions = (img.classList && img.classList.contains('card-thumb')) ? ['small','normal','large'] : ['normal','large'];
            bindCardImageRetry(img, versions);
          });
        }

        function positionCard(e) {
          var x = e.clientX + 16, y = e.clientY + 16;
          cardPop.style.display = 'block';
          cardPop.style.left = x + 'px';
          cardPop.style.top = y + 'px';
          var rect = cardPop.getBoundingClientRect();
          var vw = window.innerWidth || document.documentElement.clientWidth;
          var vh = window.innerHeight || document.documentElement.clientHeight;
          if (x + rect.width + 8 > vw) cardPop.style.left = (e.clientX - rect.width - 16) + 'px';
          if (y + rect.height + 8 > vh) cardPop.style.top = (e.clientY - rect.height - 16) + 'px';
        }
    function attachCardHover() {
      if (window.__disableLegacyCardHover) return; // short-circuit legacy system
          document.querySelectorAll('[data-card-name]').forEach(function(el) {
            if (el.__cardHoverBound) return; // avoid duplicate bindings
            el.__cardHoverBound = true;
            el.addEventListener('mouseenter', function(e) {
      var img = cardPop.querySelector('.card-hover-inner img');
      var img2 = cardPop.querySelector('.card-hover-inner .dual img:nth-child(2)');
      if (img2) img2.style.display = 'none';
        var dualNode = cardPop.querySelector('.card-hover-inner .dual');
        if (img2) { img2.style.display = 'none'; }
        if (dualNode) { dualNode.classList.remove('combo','two-faced'); }
              var meta = cardPop.querySelector('.card-meta');
              var name = el.getAttribute('data-card-name') || '';
              var vi = 0; // always start at 'normal' on hover
                img.src = buildCardUrl(name, PREVIEW_VERSIONS[vi], false, 'front');
              // Bind a one-off error handler per enter to try fallbacks
              var triedNoCache = false;
              function onErr(){
                  if (vi < PREVIEW_VERSIONS.length - 1){ vi += 1; img.src = buildCardUrl(name, PREVIEW_VERSIONS[vi], false, 'front'); }
                  else if (!triedNoCache){ triedNoCache = true; img.src = buildCardUrl(name, PREVIEW_VERSIONS[vi], true, 'front'); }
                else { img.removeEventListener('error', onErr); }
              }
              img.addEventListener('error', onErr, { once:false });
              img.addEventListener('load', function onOk(){ img.removeEventListener('load', onOk); img.removeEventListener('error', onErr); });

                // Attempt to load back face (double-faced / transform). If it fails, we silently hide.
                if (img2) {
                  img2.style.display = 'none';
                  var backTriedNoCache = false;
                  var backIdx = 0;
                  function backErr(){
                    if (backIdx < PREVIEW_VERSIONS.length - 1){
                      backIdx += 1; img2.src = buildCardUrl(name, PREVIEW_VERSIONS[backIdx], false, 'back');
                    } else if (!backTriedNoCache){
                      backTriedNoCache = true; img2.src = buildCardUrl(name, PREVIEW_VERSIONS[backIdx], true, 'back');
                    } else {
                      img2.removeEventListener('error', backErr); img2.style.display='none';
                    }
                  }
                  function backOk(){ img2.removeEventListener('error', backErr); img2.removeEventListener('load', backOk); if (dualNode) dualNode.classList.add('two-faced'); img2.style.display=''; }
                  img2.addEventListener('error', backErr, { once:false });
                  img2.addEventListener('load', backOk, { once:false });
                  img2.src = buildCardUrl(name, PREVIEW_VERSIONS[0], false, 'back');
                }
              var role = el.getAttribute('data-role') || '';
              var rawTags = el.getAttribute('data-tags') || '';
              var overlapsRaw = el.getAttribute('data-overlaps') || '';
              // Clean and split tags into an array; remove brackets and quotes
              var tags = rawTags
                .replace(/[\[\]\u2018\u2019'\u201C\u201D"]/g,'')
                .split(/\s*,\s*/)
                .filter(function(t){ return t && t.trim(); });
              var overlaps = overlapsRaw.split(/\s*,\s*/).filter(function(t){ return t; });
              var overlapSet = new Set(overlaps);
              var highlightOverlapsInList = overlaps.length === 0;
              if (role || (tags && tags.length)) {
                var html = '';
                if (role) {
                  html += '<div class="line"><span class="label">Role</span>' + role.replace(/</g,'&lt;') + '</div>';
                }
                if (tags && tags.length) {
                  html += '<div class="line"><span class="label themes-label">Themes</span><ul class="themes-list">' + tags.map(function(t){ var safe=t.replace(/</g,'&lt;'); var isOverlap = overlapSet.has(t); return '<li' + ((highlightOverlapsInList && isOverlap) ? ' class="overlap"' : '') + '>' + safe + '</li>'; }).join('') + '</ul></div>';
                  if (overlaps.length){
                    html += '<div class="line" style="margin-top:4px;"><span class="label" title="Themes shared with preview selection">Overlaps</span>' + overlaps.map(function(o){ return '<span class="ov-chip">'+o.replace(/</g,'&lt;')+'</span>'; }).join(' ') + '</div>';
                  }
                }
                meta.innerHTML = html;
                meta.style.display = '';
              } else {
                meta.style.display = 'none';
                meta.innerHTML = '';
              }
              positionCard(e);
            });
            el.addEventListener('mousemove', positionCard);
            el.addEventListener('mouseleave', function() { cardPop.style.display = 'none'; });
          });
          // Dual-card hover for combo rows
          document.querySelectorAll('[data-combo-names]').forEach(function(el){
            if (el.__comboHoverBound) return; el.__comboHoverBound = true;
            el.addEventListener('mouseenter', function(e){
              var namesAttr = el.getAttribute('data-combo-names') || '';
              var parts = namesAttr.split('||');
              var a = (parts[0]||'').trim(); var b = (parts[1]||'').trim();
              if (!a || !b) return;
              var img = cardPop.querySelector('.card-hover-inner img');
              var img2 = cardPop.querySelector('.card-hover-inner .dual img:nth-child(2)');
              var meta = cardPop.querySelector('.card-meta');
              if (img2) img2.style.display = '';
              var vi1 = 0, vi2 = 0; var triedNoCache1 = false, triedNoCache2 = false;
              img.src = buildCardUrl(a, PREVIEW_VERSIONS[vi1], false);
              img.src = buildCardUrl(a, PREVIEW_VERSIONS[vi1], false, 'front');
              img2.src = buildCardUrl(b, PREVIEW_VERSIONS[vi2], false, 'front');
              var dualNode = cardPop.querySelector('.card-hover-inner .dual');
              if (dualNode){ dualNode.classList.add('combo'); dualNode.classList.remove('two-faced'); }
              function err1(){ if (vi1 < PREVIEW_VERSIONS.length - 1){ vi1 += 1; img.src = buildCardUrl(a, PREVIEW_VERSIONS[vi1], false);} else if (!triedNoCache1){ triedNoCache1 = true; img.src = buildCardUrl(a, PREVIEW_VERSIONS[vi1], true);} else { img.removeEventListener('error', err1);} }
              function err2(){ if (vi2 < PREVIEW_VERSIONS.length - 1){ vi2 += 1; img2.src = buildCardUrl(b, PREVIEW_VERSIONS[vi2], false);} else if (!triedNoCache2){ triedNoCache2 = true; img2.src = buildCardUrl(b, PREVIEW_VERSIONS[vi2], true);} else { img2.removeEventListener('error', err2);} }
              img.addEventListener('error', err1, { once:false });
              img2.addEventListener('error', err2, { once:false });
              img.addEventListener('load', function on1(){ img.removeEventListener('load', on1); img.removeEventListener('error', err1); });
              img2.addEventListener('load', function on2(){ img2.removeEventListener('load', on2); img2.removeEventListener('error', err2); });
              meta.style.display = 'none'; meta.innerHTML = '';
              positionCard(e);
            });
            el.addEventListener('mousemove', positionCard);
            el.addEventListener('mouseleave', function(){ cardPop.style.display='none'; });
          });
        }
    // Expose re-init functions globally for dynamic content
    window.attachCardHover = attachCardHover;
    window.bindAllCardImageRetries = bindAllCardImageRetries;
    attachCardHover();
    bindAllCardImageRetries();
    document.addEventListener('htmx:afterSwap', function() { attachCardHover(); bindAllCardImageRetries(); });
      })();
    </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;
          var name = normalize(getCardData(card, 'data-card-name')) + ' ' + normalize(getCardData(card, 'data-original-name'));
          return name.indexOf('//') > -1;
        }
        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';
          btn.className='dfc-toggle';
          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);
          }
        }
        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>
    <script>
      (function(){
        try {
          var path = window.location.pathname || '/';
          var nav = document.getElementById('primary-nav'); if(!nav) return;
          var links = nav.querySelectorAll('a');
          var best = null; var bestLen = -1;
            links.forEach(function(a){
              var href = a.getAttribute('href') || '';
              if(!href) return;
              // Exact match or prefix match (ignoring trailing slash)
              if(path === href || path === href + '/' || (href !== '/' && path.startsWith(href))){
                if(href.length > bestLen){ best = a; bestLen = href.length; }
              }
            });
          if(best) best.classList.add('active');
        } catch(_) {}
      })();
    </script>
  <script src="/static/app.js?v=20250826-4"></script>
  {% if enable_themes %}
  <script>
    (function(){
      try{
        var sel = document.getElementById('theme-select');
  var resetBtn = document.getElementById('theme-reset');
        var root = document.documentElement;
        var KEY = 'mtg:theme';
        var SERVER_DEFAULT = '{{ default_theme }}';
        function mapLight(v){ return v === 'light' ? 'light-blend' : v; }
        function resolveSystem(){
          var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
          return prefersDark ? 'dark' : 'light-blend';
        }
        function normalizeUiValue(v){
          var x = (v||'system').toLowerCase();
          if (x === 'light-blend' || x === 'light-slate' || x === 'light-parchment') return 'light';
          return x;
        }
        function apply(val){
          var v = (val || 'system').toLowerCase();
          if (v === 'system') v = resolveSystem();
          v = mapLight(v);
          root.setAttribute('data-theme', v);
        }
        // Optional URL override: ?theme=system|light|dark|high-contrast|cb-friendly
        var params = new URLSearchParams(window.location.search || '');
        var urlTheme = (params.get('theme') || '').toLowerCase();
        if (urlTheme) {
          // Persist the UI value, not the mapped CSS token
          localStorage.setItem(KEY, normalizeUiValue(urlTheme));
          // Clean the URL so reloads don't keep overriding
          try { var u = new URL(window.location.href); u.searchParams.delete('theme'); window.history.replaceState({}, document.title, u.toString()); } catch(_){ }
        }
        // Determine initial selection: URL -> localStorage -> server default -> system
        var stored = localStorage.getItem(KEY);
        var initial = urlTheme || ((stored && stored.trim()) ? stored : (SERVER_DEFAULT || 'system'));
        apply(initial);
        if (sel){
          sel.value = normalizeUiValue(initial);
          sel.addEventListener('change', function(){
            var v = sel.value || 'system';
            localStorage.setItem(KEY, v);
            apply(v);
          });
        }
        if (resetBtn){
          resetBtn.addEventListener('click', function(){
            try{ localStorage.removeItem(KEY); }catch(_){ }
            var v = SERVER_DEFAULT || 'system';
            apply(v);
            if (sel) sel.value = normalizeUiValue(v);
          });
        }
        // React to system changes when set to system
        if (window.matchMedia){
          var mq = window.matchMedia('(prefers-color-scheme: dark)');
          mq.addEventListener && mq.addEventListener('change', function(){
            var cur = localStorage.getItem(KEY) || (SERVER_DEFAULT || 'system');
            if (cur === 'system') apply('system');
          });
        }
      }catch(_){ }
    })();
  </script>
  {% endif %}
  {% if not enable_themes %}
  <script>
    (function(){
      try{
        // Apply THEME env even when the selector is disabled. Resolve 'system' to OS preference.
        var root = document.documentElement;
        var SERVER_DEFAULT = '{{ default_theme }}';
        function resolveSystem(){
          var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
          return prefersDark ? 'dark' : 'light-blend';
        }
        var v = (SERVER_DEFAULT || 'system').toLowerCase();
        if (v === 'system') v = resolveSystem();
        if (v === 'light') v = 'light-blend';
        root.setAttribute('data-theme', v);
        // Track OS changes when using system
        if ((SERVER_DEFAULT||'system').toLowerCase() === 'system' && window.matchMedia){
          var mq = window.matchMedia('(prefers-color-scheme: dark)');
          mq.addEventListener && mq.addEventListener('change', function(){ root.setAttribute('data-theme', resolveSystem()); });
        }
      }catch(_){ }
    })();
  </script>
  {% endif %}
  {% if enable_pwa %}
  <script>
    (function(){
      try{
        if ('serviceWorker' in navigator){
          var ver = '{{ catalog_hash|default("dev") }}';
          var url = '/static/sw.js?v=' + encodeURIComponent(ver);
          navigator.serviceWorker.register(url).then(function(reg){
            window.__pwaStatus = { registered: true, scope: reg.scope, version: ver };
            // Listen for updates (new worker installing)
            if(reg.waiting){ reg.waiting.postMessage({ type: 'SKIP_WAITING' }); }
            reg.addEventListener('updatefound', function(){
              try {
                var nw = reg.installing; if(!nw) return;
                nw.addEventListener('statechange', function(){
                  if(nw.state === 'installed' && navigator.serviceWorker.controller){
                    // New version available; reload silently for freshness
                    try { sessionStorage.setItem('mtg:swUpdated','1'); }catch(_){ }
                    window.location.reload();
                  }
                });
              }catch(_){ }
            });
          }).catch(function(){ window.__pwaStatus = { registered: false }; });
        }
      }catch(_){ }
    })();
  </script>
  {% endif %}
  <script>
    // Show pending toast after full page reloads when actions replace the whole document
    (function(){
      try{
        var raw = sessionStorage.getItem('mtg:toastAfterReload');
        if (raw){
          sessionStorage.removeItem('mtg:toastAfterReload');
          var data = JSON.parse(raw);
          if (data && data.msg){ window.toast && window.toast(data.msg, data.type||''); }
        }
      }catch(_){ }
    })();
  </script>
  <script>
    // Global delegated hover card panel initializer (ensures functionality after HTMX swaps)
    (function(){
      // Disable legacy multi-element hover in favor of single unified panel
      window.__disableLegacyCardHover = true;
      // Global delegated curated-only & reasons controls (works after HTMX swaps and inline render)
      function findPreviewRoot(el){ return el.closest('.preview-modal-content.theme-preview-expanded') || el.closest('.preview-modal-content'); }
      function applyCuratedFor(root){
        var checkbox = root.querySelector('#curated-only-toggle');
        var status = root.querySelector('#preview-status');
        if(!checkbox) return;
        // persist
        try{ localStorage.setItem('mtg:preview.curatedOnly', checkbox.checked ? '1':'0'); }catch(_){ }
        var curatedOnly = checkbox.checked;
        var hidden=0;
        root.querySelectorAll('.card-sample').forEach(function(card){
          var role = card.getAttribute('data-role');
          var isCurated = role==='example'|| role==='curated_synergy' || role==='synthetic';
          if(curatedOnly && !isCurated){ card.style.display='none'; hidden++; } else { card.style.display=''; }
        });
        if(status) status.textContent = curatedOnly ? ('Hid '+hidden+' sampled cards') : '';
      }
      function applyReasonsFor(root){
        var cb = root.querySelector('#reasons-toggle'); if(!cb) return;
        try{ localStorage.setItem('mtg:preview.showReasons', cb.checked ? '1':'0'); }catch(_){ }
        var show = cb.checked;
        root.querySelectorAll('[data-reasons-block]').forEach(function(el){ el.style.display = show ? '' : 'none'; });
      }
      document.addEventListener('change', function(e){
        if(e.target && e.target.id === 'curated-only-toggle'){
          var root = findPreviewRoot(e.target); if(root) applyCuratedFor(root);
        }
      });
      document.addEventListener('change', function(e){
        if(e.target && e.target.id === 'reasons-toggle'){
          var root = findPreviewRoot(e.target); if(root) applyReasonsFor(root);
        }
      });
      document.addEventListener('htmx:afterSwap', function(ev){
        var frag = ev.target;
        if(frag && frag.querySelector){
          if(frag.querySelector('#curated-only-toggle')) applyCuratedFor(frag);
          if(frag.querySelector('#reasons-toggle')) applyReasonsFor(frag);
        }
      });
      document.addEventListener('DOMContentLoaded', function(){
        document.querySelectorAll('.preview-modal-content').forEach(function(root){
          // restore persisted states before applying
          try {
            var cVal = localStorage.getItem('mtg:preview.curatedOnly');
            if(cVal !== null){ var cb = root.querySelector('#curated-only-toggle'); if(cb){ cb.checked = cVal === '1'; } }
            var rVal = localStorage.getItem('mtg:preview.showReasons');
            if(rVal !== null){ var rb = root.querySelector('#reasons-toggle'); if(rb){ rb.checked = rVal === '1'; } }
          }catch(_){ }
          if(root.querySelector('#curated-only-toggle')) applyCuratedFor(root);
          if(root.querySelector('#reasons-toggle')) applyReasonsFor(root);
        });
      });
      function createPanel(){
        var panel = document.createElement('div');
        panel.id = 'hover-card-panel';
        panel.setAttribute('role','dialog');
        panel.setAttribute('aria-label','Card detail hover panel');
        panel.setAttribute('aria-hidden','true');
  panel.style.cssText = 'display:none;position:fixed;z-index:9999;width:560px;max-width:98vw;background:#1f2937;border:1px solid #374151;border-radius:12px;padding:18px;box-shadow:0 16px 42px rgba(0,0,0,.75);color:#f3f4f6;font-size:14px;line-height:1.45;pointer-events:none;';
  panel.innerHTML = ''+
   '<div class="hcp-header" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;gap:6px;">'+
  '<div class="hcp-name" style="font-weight:600;font-size:16px;flex:1;padding-right:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">&nbsp;</div>'+ 
  '<div class="hcp-rarity" style="font-size:11px;text-transform:uppercase;letter-spacing:.5px;opacity:.75;"></div>'+
  '<button type="button" class="hcp-close" aria-label="Close card details"><span aria-hidden="true">✕</span></button>'+
   '</div>'+
   '<div class="hcp-body">'+
     '<div class="hcp-img-wrap" style="text-align:center;display:flex;flex-direction:column;gap:12px;">'+
  '<img class="hcp-img" alt="Card image" style="max-width:320px;width:100%;height:auto;border-radius:10px;border:1px solid #475569;background:#0b0d12;opacity:1;" />'+
     '</div>'+
     '<div class="hcp-right" style="display:flex;flex-direction:column;min-width:0;">'+
       '<div style="display:flex;align-items:center;gap:6px;margin:0 0 4px;flex-wrap:wrap;">'+
         '<div class="hcp-role" style="display:inline-block;padding:3px 8px;font-size:11px;letter-spacing:.65px;border:1px solid #475569;border-radius:12px;background:#243044;text-transform:uppercase;">&nbsp;</div>'+
         '<div class="hcp-overlaps" style="flex:1;min-height:14px;"></div>'+
       '</div>'+
       '<ul class="hcp-taglist" aria-label="Themes"></ul>'+
  '<div class="hcp-meta" style="font-size:12px;opacity:.85;margin:2px 0 6px;"></div>'+
  '<ul class="hcp-reasons" style="list-style:disc;margin:4px 0 8px 18px;padding:0;font-size:11px;line-height:1.35;"></ul>'+
  '<div class="hcp-tags" style="font-size:11px;opacity:.55;word-break:break-word;"></div>'+
     '</div>'+
   '</div>';
        document.body.appendChild(panel);
        return panel;
      }
      function ensurePanel(){
        var panel = document.getElementById('hover-card-panel');
        if (panel) return panel;
        // Auto-create for direct theme pages where fragment-specific markup not injected
        return createPanel();
      }
      function setup(){
        var panel = ensurePanel();
        if(!panel || panel.__hoverInit) return;
        panel.__hoverInit = true;
    var imgEl = panel.querySelector('.hcp-img');
    var nameEl = panel.querySelector('.hcp-name');
    var rarityEl = panel.querySelector('.hcp-rarity');
    var metaEl = panel.querySelector('.hcp-meta');
    var reasonsList = panel.querySelector('.hcp-reasons');
    var tagsEl = panel.querySelector('.hcp-tags');
  var bodyEl = panel.querySelector('.hcp-body');
  var rightCol = panel.querySelector('.hcp-right');
        var coarseQuery = window.matchMedia('(pointer: coarse)');
        function isMobileMode(){ return (coarseQuery && coarseQuery.matches) || window.innerWidth <= 768; }
        function refreshPosition(){ if(panel.style.display==='block'){ move(window.__lastPointerEvent); } }
        if(coarseQuery){
          var handler = function(){ refreshPosition(); };
          if(coarseQuery.addEventListener){ coarseQuery.addEventListener('change', handler); }
          else if(coarseQuery.addListener){ coarseQuery.addListener(handler); }
        }
        window.addEventListener('resize', refreshPosition);
        var closeBtn = panel.querySelector('.hcp-close');
        if(closeBtn && !closeBtn.__bound){
          closeBtn.__bound = true;
          closeBtn.addEventListener('click', function(ev){ ev.preventDefault(); hide(); });
        }
        function positionPanel(evt){
          if(isMobileMode()){
            panel.classList.add('mobile');
            panel.style.bottom = 'auto';
            panel.style.left = '50%';
            panel.style.top = '50%';
            panel.style.right = 'auto';
            panel.style.transform = 'translate(-50%, -50%)';
            panel.style.pointerEvents = 'auto';
          } else {
            panel.classList.remove('mobile');
            panel.style.pointerEvents = 'none';
            panel.style.transform = 'none';
            var pad=18; var x=evt.clientX+pad, y=evt.clientY+pad;
            var vw=window.innerWidth, vh=window.innerHeight; var r=panel.getBoundingClientRect();
            if(x + r.width + 8 > vw) x = evt.clientX - r.width - pad;
            if(y + r.height + 8 > vh) y = evt.clientY - r.height - pad;
            if(x < 8) x = 8;
            if(y < 8) y = 8;
            panel.style.left = x+'px'; panel.style.top = y+'px';
            panel.style.bottom = 'auto';
            panel.style.right = 'auto';
          }
        }
        function move(evt){
          if(panel.style.display==='none') return;
          if(!evt){ evt = window.__lastPointerEvent; }
          if(!evt && lastCard){
            var rect = lastCard.getBoundingClientRect();
            evt = { clientX: rect.left + rect.width/2, clientY: rect.top + rect.height/2 };
          }
          if(!evt){ evt = { clientX: window.innerWidth/2, clientY: window.innerHeight/2 }; }
          positionPanel(evt);
        }
        // Lightweight image prefetch LRU cache (size 12) (P2 UI Hover image prefetch)
        var _imgLRU=[];
        function prefetch(src){ if(!src) return; if(_imgLRU.indexOf(src)===-1){ _imgLRU.push(src); if(_imgLRU.length>12) _imgLRU.shift(); var im=new Image(); im.src=src; } }
        var activationDelay=120; // ms (P2 optional activation delay)
        var hoverTimer=null;
        function schedule(card, evt){ clearTimeout(hoverTimer); hoverTimer=setTimeout(function(){ show(card, evt); }, activationDelay); }
        function cancelSchedule(){ clearTimeout(hoverTimer); }
        var lastCard = null;
        function show(card, evt){
          if(!card) return;
          // Prefer attributes on container, fallback to child (image) if missing
          function attr(name){ return card.getAttribute(name) || (card.querySelector('[data-'+name.slice(5)+']') && card.querySelector('[data-'+name.slice(5)+']').getAttribute(name)) || ''; }
          var simpleSource = null;
          if(card.closest){
            simpleSource = card.closest('[data-hover-simple]');
          }
          var forceSimple = (card.hasAttribute && card.hasAttribute('data-hover-simple')) || !!simpleSource;
          var nm = attr('data-card-name') || attr('data-original-name') || 'Card';
          var rarity = (attr('data-rarity')||'').trim();
          var mana = (attr('data-mana')||'').trim();
          var role = (attr('data-role')||'').trim();
          var reasonsRaw = attr('data-reasons')||'';
          var tagsRaw = attr('data-tags')||'';
          var metadataTagsRaw = attr('data-metadata-tags')||'';  // M5: Extract metadata tags
          var reasonsRaw = attr('data-reasons')||'';
          var roleEl = panel.querySelector('.hcp-role');
          var hasFlip = !!card.querySelector('.dfc-toggle');
          var tagListEl = panel.querySelector('.hcp-taglist');
          var overlapsEl = panel.querySelector('.hcp-overlaps');
          var overlapsAttr = attr('data-overlaps') || '';
          function displayLabel(text){
            if(!text) return '';
            var label = String(text);
            label = label.replace(/[\u2022\-_]+/g, ' ');
            label = label.replace(/\s+/g, ' ').trim();
            return label;
          }
          function parseTagList(raw){
            if(!raw) return [];
            var trimmed = String(raw).trim();
            if(!trimmed) return [];
            var result = [];
            var candidate = trimmed;
            if(trimmed[0] === '[' && trimmed[trimmed.length-1] === ']'){
              candidate = trimmed.slice(1, -1);
            }
            // Try JSON parsing after normalizing quotes
            try {
              var normalized = trimmed;
              if(trimmed.indexOf("'") > -1 && trimmed.indexOf('"') === -1){
                normalized = trimmed.replace(/'/g, '"');
              }
              var parsed = JSON.parse(normalized);
              if(Array.isArray(parsed)){
                result = parsed;
              }
            } catch(_){ /* fall back below */ }
            if(!result || !result.length){
              result = candidate.split(/\s*,\s*/);
            }
            return result.map(function(t){ return String(t || '').trim(); }).filter(Boolean);
          }
          function deriveTagsFromReasons(reasons){
            if(!reasons) return [];
            // Reasons often include "because it overlaps X, Y" or "by <theme>"
            var out = [];
            // Grab bracketed or quoted lists first
            var m = reasons.match(/\[(.*?)\]/);
            if(m && m[1]){ out = out.concat(m[1].split(/\s*,\s*/)); }
            // Common phrasing: "overlap(s) with A, B" or "by A, B"
            var rx = /(overlap(?:s)?(?:\s+with)?|by)\s+([^.;]+)/ig;
            var r;
            while((r = rx.exec(reasons))){ out = out.concat((r[2]||'').split(/\s*,\s*/)); }
            var tagRx = /tag:\s*([^.;]+)/ig;
            var tMatch;
            while((tMatch = tagRx.exec(reasons))){ out = out.concat((tMatch[1]||'').split(/\s*,\s*/)); }
            return out.map(function(s){ return s.trim(); }).filter(Boolean);
          }
          var overlapArr = [];
          if(overlapsAttr){
            var parsedOverlaps = parseTagList(overlapsAttr);
            if(parsedOverlaps.length){ overlapArr = parsedOverlaps; }
            else { overlapArr = [String(overlapsAttr).trim()]; }
          }
          var derivedFromReasons = deriveTagsFromReasons(reasonsRaw);
          var allTags = parseTagList(tagsRaw);
          if(!allTags.length && derivedFromReasons.length){
            // Fallback: try to derive tags from reasons text when tags missing
            allTags = derivedFromReasons.slice();
          }
          if((!overlapArr || !overlapArr.length) && derivedFromReasons.length){
            var normalizedAll = (allTags||[]).map(function(t){ return { raw: t, key: t.toLowerCase() }; });
            var derivedKeys = new Set(derivedFromReasons.map(function(t){ return t.toLowerCase(); }));
            var intersect = normalizedAll.filter(function(entry){ return derivedKeys.has(entry.key); }).map(function(entry){ return entry.raw; });
            if(!intersect.length){
              intersect = derivedFromReasons.slice();
            }
            overlapArr = Array.from(new Set(intersect));
          }
          overlapArr = (overlapArr||[]).map(function(t){ return String(t||'').trim(); }).filter(Boolean);
          allTags = (allTags||[]).map(function(t){ return String(t||'').trim(); }).filter(Boolean);
          nameEl.textContent = nm;
          rarityEl.textContent = rarity;
          var roleLabel = displayLabel(role);
          var roleKey = (roleLabel || role || '').toLowerCase();
          var isCommanderRole = roleKey === 'commander';
          metaEl.textContent = [roleLabel?('Role: '+roleLabel):'', mana?('Mana: '+mana):''].filter(Boolean).join(' • ');
          reasonsList.innerHTML='';
          reasonsRaw.split(';').map(function(r){return r.trim();}).filter(Boolean).forEach(function(r){ var li=document.createElement('li'); li.style.margin='2px 0'; li.textContent=r; reasonsList.appendChild(li); });
          // Build inline tag list with overlap highlighting
          if(tagListEl){
            tagListEl.innerHTML='';
            tagListEl.style.display = 'none';
            tagListEl.setAttribute('aria-hidden','true');
          }
          if(overlapsEl){
            if(overlapArr && overlapArr.length){
              overlapsEl.innerHTML = overlapArr.map(function(o){ var label = displayLabel(o); return '<span class="hcp-ov-chip" title="Overlapping synergy">'+label+'</span>'; }).join('');
            } else {
              overlapsEl.innerHTML = '';
            }
          }
          if(tagsEl){
            if(isCommanderRole){
              tagsEl.textContent = '';
              tagsEl.style.display = 'none';
            } else {
              var tagText = allTags.map(displayLabel).join(', ');
              // M5: Temporarily append metadata tags for debugging
              if(metadataTagsRaw && metadataTagsRaw.trim()){
                var metaTags = metadataTagsRaw.split(',').map(function(t){return t.trim();}).filter(Boolean);
                if(metaTags.length){
                  var metaText = metaTags.map(displayLabel).join(', ');
                  tagText = tagText ? (tagText + ' | META: ' + metaText) : ('META: ' + metaText);
                }
              }
              tagsEl.textContent = tagText;
              tagsEl.style.display = tagText ? '' : 'none';
            }
          }
          if(roleEl){
            roleEl.textContent = roleLabel || '';
            roleEl.style.display = roleLabel ? 'inline-block' : 'none';
          }
          panel.classList.toggle('is-payoff', role === 'payoff');
          panel.classList.toggle('is-commander', isCommanderRole);
          var hasDetails = !forceSimple && (
            !!roleLabel || !!mana || !!rarity || (reasonsRaw && reasonsRaw.trim()) || (overlapArr && overlapArr.length) || (allTags && allTags.length)
          );
          panel.classList.toggle('hcp-simple', !hasDetails);
          if(rightCol){
            rightCol.style.display = hasDetails ? 'flex' : 'none';
          }
          if(bodyEl){
            if(!hasDetails){
              bodyEl.style.display = 'flex';
              bodyEl.style.flexDirection = 'column';
              bodyEl.style.alignItems = 'center';
              bodyEl.style.gap = '12px';
            } else {
              bodyEl.style.display = '';
              bodyEl.style.flexDirection = '';
              bodyEl.style.alignItems = '';
              bodyEl.style.gap = '';
            }
          }
          var fuzzy = encodeURIComponent(nm);
          var rawName = nm || '';
          var hasBack = rawName.indexOf('//')>-1 || (attr('data-original-name')||'').indexOf('//')>-1;
          if(hasBack) hasFlip = true;
          var storageKey = 'mtg:face:' + rawName.toLowerCase();
          var storedFace = (function(){ try { return localStorage.getItem(storageKey); } catch(_){ return null; } })();
          if(storedFace === 'front' || storedFace === 'back') card.setAttribute('data-current-face', storedFace);
          var chosenFace = card.getAttribute('data-current-face') || 'front';
          lastCard = card;
          function renderHoverFace(face){
            var desiredVersion='large';
            var faceParam = (face==='back') ? '&face=back' : '';
            var currentKey = nm+':'+face+':'+desiredVersion;
            var prevFace = imgEl.getAttribute('data-face');
            var faceChanged = prevFace && prevFace !== face;
            if(imgEl.getAttribute('data-current')!== currentKey){
              var src='https://api.scryfall.com/cards/named?fuzzy='+fuzzy+'&format=image&version='+desiredVersion+faceParam;
              if(faceChanged){ imgEl.style.opacity = 0; }
              prefetch(src);
              imgEl.src = src;
              imgEl.setAttribute('data-current', currentKey);
              imgEl.setAttribute('data-face', face);
              imgEl.addEventListener('load', function onLoad(){ imgEl.removeEventListener('load', onLoad); requestAnimationFrame(function(){ imgEl.style.opacity = 1; }); });
            }
            if(!imgEl.__errBound){
              imgEl.__errBound = true;
              imgEl.addEventListener('error', function(){
                var cur = imgEl.getAttribute('src')||'';
                if(cur.indexOf('version=large')>-1){ imgEl.src = cur.replace('version=large','version=normal'); }
                else if(cur.indexOf('version=normal')>-1){ imgEl.src = cur.replace('version=normal','version=small'); }
              });
            }
          }
          renderHoverFace(chosenFace);
          window.__dfcNotifyHover = hasFlip ? function(cardRef, face){ if(cardRef === lastCard){ renderHoverFace(face); } } : null;
          if(evt){ window.__lastPointerEvent = evt; }
          if(isMobileMode()){
            panel.classList.add('mobile');
            panel.style.pointerEvents = 'auto';
            panel.style.maxHeight = '80vh';
          } else {
            panel.classList.remove('mobile');
            panel.style.pointerEvents = 'none';
            panel.style.maxHeight = '';
            panel.style.bottom = 'auto';
          }
          panel.style.display='block'; panel.setAttribute('aria-hidden','false'); move(evt);
        }
        function hide(){
          panel.style.display='none';
          panel.setAttribute('aria-hidden','true');
          cancelSchedule();
          panel.classList.remove('mobile');
          panel.style.pointerEvents = 'none';
          panel.style.transform = 'none';
          panel.style.bottom = 'auto';
          panel.style.maxHeight = '';
          window.__dfcNotifyHover = null;
        }
        document.addEventListener('mousemove', move);
        function getCardFromEl(el){
          if(!el) return null;
          if(el.closest){
            var altBtn = el.closest('.alts button[data-card-name]');
            if(altBtn){ return altBtn; }
          }
          // If inside flip button
          var btn = el.closest && el.closest('.dfc-toggle');
          if(btn) return btn.closest('.card-sample, .commander-cell, .commander-thumb, .commander-card, .card-tile, .candidate-tile, .card-preview, .stack-card');
          // Recognized container classes (add .stack-card for finished/random deck thumbnails)
          var container = el.closest && el.closest('.card-sample, .commander-cell, .commander-thumb, .commander-card, .card-tile, .candidate-tile, .card-preview, .stack-card');
          if(container) return container;
          // Image-based detection (any card image carrying data-card-name)
          if(el.matches && (el.matches('img.card-thumb') || el.matches('img[data-card-name]') || el.classList.contains('commander-img'))){
            var up = el.closest && el.closest('.stack-card');
            return up || el; // fall back to the image itself
          }
          // List view spans (deck summary list mode, finished deck list, etc.)
            if(el.hasAttribute && el.hasAttribute('data-card-name')) return el;
          return null;
        }
        document.addEventListener('pointermove', function(e){ window.__lastPointerEvent = e; });
        document.addEventListener('pointerover', function(e){
          if(isMobileMode()) return;
          var card = getCardFromEl(e.target);
              if(!card) return;
          // If hovering flip button, refresh immediately (no activation delay)
          if(e.target.closest && e.target.closest('.dfc-toggle')){
            show(card, e);
            return;
          }
          if(lastCard === card && panel.style.display==='block') { return; }
          schedule(card, e);
        });
        document.addEventListener('pointerout', function(e){
          if(isMobileMode()) return;
          var relCard = getCardFromEl(e.relatedTarget);
          if(relCard && lastCard && relCard === lastCard) return; // moving within same card (img <-> button)
          if(!panel.contains(e.relatedTarget)){
            cancelSchedule();
            if(!relCard) hide();
          }
        });
        document.addEventListener('click', function(e){
          if(!isMobileMode()) return;
          if(panel.contains(e.target)) return;
          if(e.target.closest && (e.target.closest('.dfc-toggle') || e.target.closest('.hcp-close'))) return;
          if(e.target.closest && e.target.closest('button, input, select, textarea, a')) return;
          var card = getCardFromEl(e.target);
          if(card){
            cancelSchedule();
            var rect = card.getBoundingClientRect();
            var syntheticEvt = { clientX: rect.left + rect.width/2, clientY: rect.top + rect.height/2 };
            show(card, syntheticEvt);
          } else if(panel.style.display==='block'){
            hide();
          }
        });
        // Expose show function for external refresh (flip updates)
        window.__hoverShowCard = function(card){
          var ev = window.__lastPointerEvent || { clientX: (card.getBoundingClientRect().left+12), clientY: (card.getBoundingClientRect().top+12) };
          show(card, ev);
        };
        window.hoverShowByName = function(name){
          try {
            var el = document.querySelector('[data-card-name="'+CSS.escape(name)+'"]');
            if(el){ window.__hoverShowCard(el.closest('.card-sample, .commander-cell, .commander-thumb, .commander-card, .card-tile, .candidate-tile, .card-preview, .stack-card') || el); }
          } catch(_) {}
        };
        // Keyboard accessibility & focus traversal (P2 UI Hover keyboard accessibility)
  document.addEventListener('focusin', function(e){ var card=e.target.closest && e.target.closest('.card-sample, .commander-cell, .commander-thumb'); if(card){ show(card, {clientX:card.getBoundingClientRect().left+10, clientY:card.getBoundingClientRect().top+10}); }});
  document.addEventListener('focusout', function(e){ var next=e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest('.card-sample, .commander-cell, .commander-thumb'); if(!next) hide(); });
        document.addEventListener('keydown', function(e){ if(e.key==='Escape') hide(); });
        // Compact mode event listener
        document.addEventListener('mtg:hoverCompactToggle', function(){ panel.classList.toggle('compact-img', !!window.__hoverCompactMode); });
      }
      document.addEventListener('htmx:afterSwap', setup);
      document.addEventListener('DOMContentLoaded', setup);
      setup();
    })();
  </script>
    <script>
    // Global compact mode toggle function (UI Hover compact mode toggle)
    (function(){
      window.toggleHoverCompactMode = function(state){
        if(typeof state==='boolean') window.__hoverCompactMode = state; else window.__hoverCompactMode = !window.__hoverCompactMode;
        document.dispatchEvent(new CustomEvent('mtg:hoverCompactToggle'));
      };
    })();
    </script>
  </body>
  </html>
