window.AdCard = function AdCard({ ad, selected, onToggle, onExpand, minimal }) {
  const p = PLATFORMS[ad.platform] || PLATFORMS.foreplay;
  const hasTranscript = ad.transcript && ad.transcript.length > 0;
  const hasVideo = !!ad.creativeUrl;
  // AdSpy / BrandSearch transcripts are generated server-side via Whisper /
  // ElevenLabs after the ad is fetched. Until the transcript hydration call
  // returns we badge the card so users know something is in flight \u2014
  // otherwise the missing transcript looks like a permanent gap.
  const transcriptStatus = ad.transcriptStatus || null;
  const transcriptPending = hasVideo
    && !hasTranscript
    && (ad.platform === 'adspy' || ad.platform === 'brandsearch')
    && transcriptStatus !== 'fallback'
    && transcriptStatus !== 'unavailable'
    && transcriptStatus !== 'empty';
  const showVideoThumb = hasVideo && !ad.thumbnailUrl;
  const score = ad.score;
  const scoreText = (typeof score === 'number')
    ? (score > 10 ? Math.round(score) : score.toFixed(1))
    : null;
  const metricChips = RESILIA_ADAPT.metricChips(ad);
  const isCurrentRun = !!(ad.currentSearchRun || ad.currentAutoresearchRun);

  const openExpand = (e) => { e.stopPropagation(); onExpand(ad); };

  return (
    <div className={`ad ${selected ? 'sel' : ''} ${minimal ? 'minimal' : ''} ${isCurrentRun ? 'current-run' : ''}`} onClick={onToggle}>
      <div className="ad-thumb" style={{ background: ad.thumb }}>
        {showVideoThumb && (
          <video
            className="ad-thumb-video"
            src={ad.creativeUrl}
            muted
            playsInline
            preload="metadata"
            onLoadedMetadata={(e) => {
              try { e.currentTarget.currentTime = 0.15; } catch { /* preview seek can fail on some streams */ }
            }}
            onError={(e) => { e.currentTarget.style.display = 'none'; }}
          />
        )}
        <div className={`ad-cbx ${selected ? 'on' : ''}`} onClick={(e) => { e.stopPropagation(); onToggle(); }}>
          {selected && <Icon name="check" size={10} />}
        </div>
        <span className="ad-plat" style={{ background: p.bg, color: p.color }}>
          <span className="ad-plat-dot" style={{ background: p.color }} />
          {p.label}
        </span>
        {isCurrentRun && <span className="ad-current-run">{ad.currentRunLabel || 'Current'}</span>}
        <button
          className="ad-play"
          onClick={openExpand}
          title={hasVideo ? 'Play in drawer' : 'Open details'}
          style={{ border: 'none', cursor: 'pointer' }}
        >
          <Icon name="play" size={14} />
        </button>
        {ad.duration && <span className="ad-duration">{ad.duration}</span>}
        <button
          className="ad-expand"
          onClick={openExpand}
          title="Open details + transcript"
        >
          <Icon name="expand" size={12} />
        </button>
      </div>
      {!minimal && (
        <div className="ad-body">
          <div className="ad-brand-row">
            <span className="ad-brand">{ad.brand}</span>
            {hasTranscript && (
              <span className="ad-has-transcript" title="Transcript available">
                <Icon name="caption" size={11} />
              </span>
            )}
            {!hasTranscript && transcriptPending && (
              <span
                className="ad-transcript-pending"
                title="Generating transcript from video (Whisper). Typically 5–20s per ad depending on length."
                style={{
                  display: 'inline-flex',
                  alignItems: 'center',
                  gap: 4,
                  fontSize: 11,
                  color: 'var(--fg-3)',
                }}
              >
                <Icon name="loader" size={11} />
                Transcribing… (~5–20s)
              </span>
            )}
          </div>
          {ad.directionLabel && (
            <div className="ad-direction" title={`Direction: ${ad.directionLabel}`}>
              <Icon name="target" size={10} />
              <span>{ad.directionLabel}</span>
            </div>
          )}
          {ad.searchReturnLabel && (
            <div className="ad-search-return" title={`Returned by: ${ad.searchReturnLabel}`}>
              <Icon name="search" size={10} />
              <span>{ad.searchReturnLabel}</span>
            </div>
          )}
          <div className="ad-copy">{ad.copy}</div>
          <div className="ad-meta">
            {scoreText !== null && (
              <span
                className={`ad-score${ad.fallbackSource ? ' ad-score-fallback' : ''}`}
                title={ad.fallbackSource
                  ? `No ${ad.fallbackSource} ads matched the query — this is a top-of-platform fallback. Score reflects general quality, not query match.`
                  : 'Evaluator score (0–10)'}
                style={ad.fallbackSource ? { color: '#a01c1c', borderColor: '#f5c2c2' } : undefined}
              >
                <Icon name="trend" size={10} />
                {scoreText}
                {ad.fallbackSource ? ' · fallback' : ''}
              </span>
            )}
            {metricChips.map(({ label, value, title, className }) => (
              <span key={label} className={`ad-metric-chip ${className}`.trim()} title={title}>
                <span className="ad-metric-label">{label}</span>
                {value}
              </span>
            ))}
            {ad.daysRunning ? <span>{ad.daysRunning}d running</span> : null}
          </div>
        </div>
      )}
      {minimal && (
        <div className="ad-body ad-body-min">
          <div className="ad-brand">{ad.brand}</div>
          {scoreText !== null && (
            <span className="ad-score"><Icon name="trend" size={10} />{scoreText}</span>
          )}
        </div>
      )}
    </div>
  );
};
