// Showcase — Firebase/Firestore-powered, live data
// Uses global firebase.* (compat SDK v10, loaded before this file)
// No import statements — all globals.

const { useState: useShowState, useEffect: useShowEffect, useRef: useShowRef } = React;

// ─── Skeleton placeholder ─────────────────────────────────────────
function SkeletonBlock({ style = {} }) {
  return (
    <div style={{
      background: 'linear-gradient(90deg, rgba(255,255,255,0.04) 0%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 100%)',
      backgroundSize: '200% 100%',
      animation: 'shimmer 1.8s ease-in-out infinite',
      borderRadius: '8px',
      ...style,
    }} />
  );
}

// ─── Original deck — click / tap to cycle cards ───────────────────
function OriginalFanDeck({ originals }) {
  const [topIdx, setTopIdx] = useShowState(0);
  const [interacted, setInteracted] = useShowState(false);

  if (!originals || originals.length === 0) {
    return (
      <div style={{
        aspectRatio: '1/1', borderRadius: '10px',
        border: '1px dashed rgba(255,255,255,0.12)',
        background: 'linear-gradient(135deg, #1a1a1e, #111115)',
        position: 'relative', display: 'grid', placeItems: 'center', overflow: 'hidden',
      }}>
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: '10px',
          color: 'var(--text-tertiary)', letterSpacing: '0.15em',
          textTransform: 'uppercase', position: 'absolute', top: '12px', left: '12px',
        }}>● PŮVODNÍ · 1:1</span>
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: '12px',
          color: 'rgba(245,245,247,0.2)', letterSpacing: '0.1em', textTransform: 'uppercase',
        }}>bez obrázku</span>
      </div>
    );
  }

  const count = originals.length;

  const advance = () => {
    if (count <= 1) return;
    setTopIdx(i => (i + 1) % count);
    setInteracted(true);
  };

  // rank 0 = active top card; rank 1,2,… = behind it
  const getConfig = (i) => {
    const rank = (i - topIdx + count) % count;
    if (rank === 0) return { rot: 0, x: 0, z: count, dim: 1 };
    const side = rank % 2 === 1 ? -1 : 1;
    const depth = Math.ceil(rank / 2);
    return { rot: side * depth * 6, x: side * depth * 26, z: count - rank, dim: Math.max(0.4, 1 - rank * 0.22) };
  };

  return (
    <div style={{ position: 'relative', width: '100%' }}>
      <style>{`@keyframes deckHint{0%,100%{opacity:.7;transform:translateX(-50%) translateY(0)}50%{opacity:1;transform:translateX(-50%) translateY(-3px)}}`}</style>

      {/* Clickable card stack */}
      <div onClick={advance} style={{
        position: 'relative', aspectRatio: '1/1', width: '100%',
        cursor: count > 1 ? 'pointer' : 'default',
      }}>
        {originals.map((orig, i) => {
          const cfg = getConfig(i);
          const isTop = i === topIdx;
          const bgColor = orig.bg || '#111114';
          const bgStyle = orig.bgGradient
            ? `linear-gradient(${orig.bgAngle || 135}deg, ${bgColor}, ${orig.bgGradient})`
            : bgColor;
          const labelColor = orig.labelColor || 'rgba(245,245,247,0.75)';

          return (
            <div key={orig.id} style={{
              position: 'absolute', top: 0, left: 0,
              width: '100%', height: '100%',
              transform: `translateX(${cfg.x}px) rotate(${cfg.rot}deg)`,
              zIndex: cfg.z,
              transition: 'transform 500ms cubic-bezier(0.65,0,0.35,1), filter 500ms',
              transformOrigin: 'center 85%',
              borderRadius: '10px', overflow: 'hidden',
              border: isTop ? '1px solid rgba(255,255,255,0.18)' : '1px solid rgba(255,255,255,0.07)',
              background: bgStyle,
              filter: `brightness(${cfg.dim})`,
            }}>
              {orig.url && (
                <img src={orig.url} alt={orig.fileName || 'originál'}
                  style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }} />
              )}
              {isTop && (
                <span style={{
                  position: 'absolute', top: '12px', left: '12px',
                  fontFamily: 'var(--font-mono)', fontSize: '10px', color: labelColor,
                  letterSpacing: '0.15em', textTransform: 'uppercase',
                  textShadow: '0 1px 4px rgba(0,0,0,0.6)', pointerEvents: 'none',
                }}>● PŮVODNÍ · 1:1</span>
              )}
            </div>
          );
        })}

        {/* "klikni · další" hint — disappears after first interaction */}
        {count > 1 && !interacted && (
          <div style={{
            position: 'absolute', bottom: '14px', left: '50%',
            zIndex: count + 2, pointerEvents: 'none',
            display: 'flex', alignItems: 'center', gap: '5px',
            background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(6px)',
            borderRadius: '20px', padding: '5px 12px',
            animation: 'deckHint 1.9s ease-in-out infinite',
          }}>
            <svg width="10" height="10" viewBox="0 0 24 24" fill="none"
              stroke="rgba(245,245,247,0.6)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
              <polyline points="9 18 15 12 9 6"/>
            </svg>
            <span style={{
              fontFamily: 'var(--font-mono)', fontSize: '9px', letterSpacing: '0.12em',
              textTransform: 'uppercase', color: 'rgba(245,245,247,0.6)', whiteSpace: 'nowrap',
            }}>klikni · další</span>
          </div>
        )}
      </div>

      {/* Pill-dot indicators — clickable to jump to specific card */}
      {count > 1 && (
        <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '6px', marginTop: '14px' }}>
          {originals.map((_, i) => (
            <div key={i} onClick={() => { setTopIdx(i); setInteracted(true); }} style={{
              width: i === topIdx ? '20px' : '6px', height: '6px', borderRadius: '3px',
              background: i === topIdx ? 'var(--accent)' : 'rgba(255,255,255,0.18)',
              transition: 'width 320ms cubic-bezier(0.65,0,0.35,1), background 320ms',
              cursor: 'pointer',
            }} />
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Speaker icon SVGs ────────────────────────────────────────────
function IconMuted() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
      <line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/>
    </svg>
  );
}
function IconUnmuted() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
      <path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
      <path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
    </svg>
  );
}

// ─── Single output portrait card ──────────────────────────────────
function OutputCard({ out, activeAudioId, onActivateAudio, hideMeta = false }) {
  const isVideo = out.fileType === 'video';
  const [hov, setHov] = useShowState(false);
  const fit = hov ? 'contain' : 'cover';
  const videoRef = useShowRef(null);
  const isAudioOn = activeAudioId === out.id;

  // Sync muted state imperatively (React doesn't reliably toggle muted via prop)
  useShowEffect(() => {
    if (videoRef.current) {
      videoRef.current.muted = !isAudioOn;
    }
  }, [isAudioOn]);

  const toggleAudio = (e) => {
    e.stopPropagation();
    onActivateAudio(isAudioOn ? null : out.id);
  };

  return (
    <div
      style={{
        aspectRatio: '9/16',
        borderRadius: '10px',
        border: `1px solid ${hov ? 'rgba(247,19,63,0.55)' : 'rgba(255,255,255,0.08)'}`,
        position: 'relative',
        overflow: 'hidden',
        background: '#0a0a0b',
        flexShrink: 0,
        transition: 'border-color 300ms',
      }}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
    >
      {isVideo ? (
        <video
          ref={videoRef}
          src={out.url}
          autoPlay muted loop playsInline
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: fit, transition: 'object-fit 0ms' }}
        />
      ) : out.url ? (
        <img
          src={out.url}
          alt={out.label || out.fileName || ''}
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: fit, transition: 'object-fit 0ms' }}
        />
      ) : (
        <div style={{
          position: 'absolute', inset: 0,
          background: 'radial-gradient(ellipse at 50% 60%, rgba(247,19,63,0.18), transparent 70%)',
        }} />
      )}

      {/* Gradient overlay — hidden on hover so full image is visible */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 45%)',
        pointerEvents: 'none',
        opacity: hov ? 0 : 1,
        transition: 'opacity 250ms',
      }} />

      {/* Label overlay — desktop only, with subtle transparent background */}
      {!hideMeta && out.label && (
        <span style={{
          position: 'absolute', top: '10px', left: '10px',
          display: 'inline-flex', alignItems: 'center', gap: '5px',
          padding: '3px 8px', borderRadius: '4px',
          background: 'rgba(255,255,255,0.28)',
          backdropFilter: 'blur(8px)',
          fontFamily: 'var(--font-mono)', fontSize: '9px', fontWeight: 500,
          color: out.labelColor || '#F7133F',
          letterSpacing: '0.15em', textTransform: 'uppercase',
        }}>
          <span style={{ width: '5px', height: '5px', borderRadius: '999px', background: out.labelColor || '#F7133F', flexShrink: 0 }} />
          {out.label}
        </span>
      )}

      {/* Speaker toggle — only for videos marked hasAudio */}
      {isVideo && out.hasAudio && (
        <button
          onClick={toggleAudio}
          title={isAudioOn ? 'Ztlumit' : 'Zapnout zvuk'}
          style={{
            position: 'absolute', bottom: '10px', right: '10px',
            width: '28px', height: '28px',
            borderRadius: '50%',
            background: isAudioOn ? 'rgba(247,19,63,0.85)' : 'rgba(0,0,0,0.55)',
            border: isAudioOn ? '1px solid rgba(247,19,63,0.9)' : '1px solid rgba(255,255,255,0.18)',
            color: '#fff',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer',
            transition: 'background 220ms, border-color 220ms, transform 150ms',
            transform: isAudioOn ? 'scale(1.1)' : 'scale(1)',
            zIndex: 3,
          }}
        >
          {isAudioOn ? <IconUnmuted /> : <IconMuted />}
        </button>
      )}

    </div>
  );
}

// ─── Outputs horizontal scroll row ───────────────────────────────
const CARD_GAP = 14;
const VISIBLE_CARDS_DESKTOP = 4;
const VISIBLE_CARDS_MOBILE  = 2;
// Skeleton fallback dimensions (before ResizeObserver fires)
const SKEL_CARD_W = 200;
const SKEL_CARD_H = Math.round(SKEL_CARD_W * 16 / 9);

// Threshold in px: below this the row switches to compact (2-up) layout
const COMPACT_BREAKPOINT = 760;

function OutputsRow({ outputs, activeAudioId, onActivateAudio, onUpdate, onArrowChange }) {
  const scrollRef  = useShowRef(null);
  const wrapperRef = useShowRef(null);
  const cardWRef   = useShowRef(200);
  const initDoneRef = useShowRef(false); // prevent re-init on subsequent containerW updates

  const [containerW, setContainerW] = useShowState(0);
  const [canLeft,    setCanLeft]    = useShowState(true);
  const [canRight,   setCanRight]   = useShowState(true);

  const isCompact = containerW > 0 && containerW < COMPACT_BREAKPOINT;
  const visibleCards = isCompact ? VISIBLE_CARDS_MOBILE : VISIBLE_CARDS_DESKTOP;

  const cardW = containerW > 0
    ? Math.max(100, Math.floor((containerW - (visibleCards - 1) * CARD_GAP) / visibleCards))
    : 200;
  const cardH = Math.round(cardW * 16 / 9);
  cardWRef.current = cardW;

  // Infinite loop: triple the outputs so we always have cards on both sides
  const canLoop = outputs && outputs.length > 1;
  const loopedOutputs = canLoop ? [...outputs, ...outputs, ...outputs] : (outputs || []);
  const origCount = outputs ? outputs.length : 0;

  // Measure container
  useShowEffect(() => {
    const el = wrapperRef.current;
    if (!el) return;
    const measure = () => setContainerW(el.clientWidth);
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  // Initialize scroll to middle set when container first measured
  useShowEffect(() => {
    if (!canLoop || containerW === 0 || initDoneRef.current) return;
    const el = scrollRef.current;
    if (!el) return;
    const STEP = cardWRef.current + CARD_GAP;
    el.scrollLeft = origCount * STEP;
    initDoneRef.current = true;
  }, [containerW, canLoop]);

  // Silent boundary snap — keeps us in the middle set
  const snapBoundary = () => {
    if (!canLoop) return;
    const el = scrollRef.current;
    if (!el) return;
    const STEP = cardWRef.current + CARD_GAP;
    const singleW = origCount * STEP;
    if (el.scrollLeft >= singleW * 2) {
      el.scrollLeft -= singleW;
    } else if (el.scrollLeft < STEP * 0.5 && el.scrollLeft >= 0) {
      el.scrollLeft += singleW;
    }
  };

  const fireUpdate = () => {
    const el = scrollRef.current;
    if (!el || !onUpdate) return;
    const STEP = cardWRef.current + CARD_GAP;
    const singleW = origCount * STEP;
    // Modulo-wrap so SVG always gets scrollLeft within [0, singleW)
    // even while smooth-scroll animation is playing through clone zone
    let adjustedLeft = el.scrollLeft - (canLoop ? singleW : 0);
    if (canLoop && singleW > 0) {
      adjustedLeft = ((adjustedLeft % singleW) + singleW) % singleW;
    }
    onUpdate(adjustedLeft, el.clientWidth, cardWRef.current, CARD_GAP);
  };

  const updateArrows = () => {
    const el = scrollRef.current;
    if (!el) return;
    snapBoundary();
    // In loop mode arrows are always available
    setCanLeft(canLoop || el.scrollLeft > 4);
    setCanRight(canLoop || el.scrollLeft + el.clientWidth < el.scrollWidth - 4);
    fireUpdate();
  };

  useShowEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    updateArrows();
    el.addEventListener('scroll', updateArrows, { passive: true });
    return () => el.removeEventListener('scroll', updateArrows);
  }, [outputs, containerW, isCompact]);

  useShowEffect(() => { fireUpdate(); }, [containerW]);

  // Bubble arrow state up
  useShowEffect(() => {
    if (!onArrowChange) return;
    onArrowChange({ isCompact, canLeft, canRight, scroll });
  }, [isCompact, canLeft, canRight]);

  // Auto-advance every 8s (infinite — no end check needed)
  useShowEffect(() => {
    if (!canLoop) return;
    const el = scrollRef.current;
    if (!el) return;
    const timer = setInterval(() => {
      el.scrollBy({ left: cardWRef.current + CARD_GAP, behavior: 'smooth' });
    }, 8000);
    return () => clearInterval(timer);
  }, [canLoop]);

  const scroll = (dir) => {
    const el = scrollRef.current;
    if (el) el.scrollBy({ left: dir * (cardWRef.current + CARD_GAP), behavior: 'smooth' });
  };

  if (!outputs || outputs.length === 0) {
    return (
      <div style={{
        height: '200px', border: '1px dashed rgba(255,255,255,0.08)', borderRadius: '10px',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: '#606068', fontFamily: 'var(--font-mono)',
        fontSize: '11px', letterSpacing: '0.1em', textTransform: 'uppercase',
      }}>žádné výstupy</div>
    );
  }

  // SVG-only arrow icon
  const ArrowIcon = ({ dir }) => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <line x1={dir === 1 ? 4 : 20} y1="12" x2={dir === 1 ? 20 : 4} y2="12"/>
      <polyline points={dir === 1 ? '14,6 20,12 14,18' : '10,6 4,12 10,18'}/>
    </svg>
  );

  // Desktop: absolute arrows overlapping the scroll row
  const desktopArrowBtn = (dir, visible, onClick) => (
    <button onClick={onClick} style={{
      position: 'absolute', top: '50%',
      [dir === -1 ? 'left' : 'right']: '10px',
      transform: 'translateY(-50%)',
      width: '40px', height: '40px', borderRadius: '50%',
      background: 'rgba(10,10,11,0.78)',
      backdropFilter: 'blur(10px)',
      border: '1px solid rgba(255,255,255,0.13)',
      color: '#F5F5F7', cursor: 'pointer',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      opacity: visible ? 1 : 0, pointerEvents: visible ? 'auto' : 'none',
      transition: 'opacity 250ms, transform 200ms',
      zIndex: 4,
      boxShadow: '0 2px 12px rgba(0,0,0,0.5)',
    }}
    onMouseEnter={e => e.currentTarget.style.transform = 'translateY(-50%) scale(1.08)'}
    onMouseLeave={e => e.currentTarget.style.transform = 'translateY(-50%) scale(1)'}
    >
      <ArrowIcon dir={dir} />
    </button>
  );

  // Right-edge fade mask when more cards exist beyond viewport
  const showRightFade = canRight;

  return (
    <div ref={wrapperRef}>
      {/* Scroll row */}
      <div style={{ position: 'relative', overflow: 'hidden' }}>
        {!isCompact && desktopArrowBtn(-1, canLeft, () => scroll(-1))}
        <div ref={scrollRef} style={{
          display: 'flex', gap: `${CARD_GAP}px`,
          overflowX: 'auto', scrollSnapType: 'x mandatory',
          paddingBottom: '4px', scrollbarWidth: 'none', msOverflowStyle: 'none',
        }}>
          {loopedOutputs.map((out, loopIdx) => (
            <div key={`${out.id}_${loopIdx}`} style={{ width: `${cardW}px`, flexShrink: 0, scrollSnapAlign: 'start', display: 'flex', flexDirection: 'column' }}>
              {/* Desktop: description above card */}
              {!isCompact && out.description && (
                <div style={{ marginBottom: '6px' }}>
                  <span style={{
                    fontFamily: 'var(--font-mono)', fontSize: '11px',
                    color: out.descriptionColor || '#A0A0A8',
                    letterSpacing: '0.06em', lineHeight: 1.3,
                    whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                    display: 'block',
                  }}>{out.description}</span>
                </div>
              )}
              {/* Compact: fixed-height label slot — always rendered for alignment */}
              {isCompact && (
                <div style={{ height: '24px', display: 'flex', alignItems: 'center', marginBottom: '6px' }}>
                  {out.label && (
                    <span style={{
                      display: 'inline-flex', alignItems: 'center', gap: '5px',
                      padding: '3px 8px', borderRadius: '4px',
                      background: 'rgba(255,255,255,0.15)',
                      backdropFilter: 'blur(8px)',
                      fontFamily: 'var(--font-mono)', fontSize: '9px',
                      color: out.labelColor || '#F7133F',
                      letterSpacing: '0.14em', textTransform: 'uppercase',
                      whiteSpace: 'nowrap',
                    }}>
                      <span style={{ width: '5px', height: '5px', borderRadius: '999px', background: out.labelColor || '#F7133F', flexShrink: 0 }} />
                      {out.label}
                    </span>
                  )}
                </div>
              )}
              <div style={{ height: `${cardH}px` }}>
                <OutputCard out={out} activeAudioId={activeAudioId} onActivateAudio={onActivateAudio} hideMeta={isCompact} />
              </div>
              {/* Compact: fixed-height description slot — always rendered for alignment */}
              {isCompact && (
                <div style={{ height: '22px', display: 'flex', alignItems: 'center', marginTop: '6px' }}>
                  {out.description && (
                    <span style={{
                      fontFamily: 'var(--font-mono)', fontSize: '10px',
                      color: out.descriptionColor || '#606068',
                      letterSpacing: '0.06em', lineHeight: 1.3,
                      whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                    }}>{out.description}</span>
                  )}
                </div>
              )}
            </div>
          ))}
        </div>
        {!isCompact && desktopArrowBtn(1, canRight, () => scroll(1))}
        {/* Right edge fade — hints that more cards exist */}
        <div style={{
          position: 'absolute', top: 0, right: 0, bottom: '4px',
          width: '80px', pointerEvents: 'none',
          background: 'linear-gradient(to left, var(--bg-base) 0%, transparent 100%)',
          opacity: showRightFade ? 1 : 0,
          transition: 'opacity 300ms',
          zIndex: 3,
        }} />
        {/* Left edge fade — desktop only */}
        {!isCompact && (
          <div style={{
            position: 'absolute', top: 0, left: 0, bottom: '4px',
            width: '80px', pointerEvents: 'none',
            background: 'linear-gradient(to right, var(--bg-base) 0%, transparent 100%)',
            opacity: canLeft ? 1 : 0,
            transition: 'opacity 300ms',
            zIndex: 3,
          }} />
        )}
      </div>
    </div>
  );
}

// ─── Single client showcase row ───────────────────────────────────
function ShowcaseRow({ client, idx, total, activeAudioId, onActivateAudio }) {
  const outputs = client.outputs || [];
  const originals = client.originals || [];
  const count = outputs.length;

  // Arrow state bubbled up from OutputsRow
  const [arrowInfo, setArrowInfo] = useShowState({ isCompact: false, canLeft: false, canRight: false, scroll: null });

  // Refs for imperative SVG connector updates (no re-render needed on scroll)
  const pathsRef = useShowRef([]);
  const svgRef = useShowRef(null);
  const SVG_H = 110;
  const uid = client.id || idx; // unique per row for gradient/marker IDs

  const RowArrowIcon = ({ dir }) => (
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <line x1={dir === 1 ? 4 : 20} y1="12" x2={dir === 1 ? 20 : 4} y2="12"/>
      <polyline points={dir === 1 ? '14,6 20,12 14,18' : '10,6 4,12 10,18'}/>
    </svg>
  );

  const handleScrollUpdate = (scrollLeft, containerW, cardW, gap) => {
    const cW = cardW || 200;
    const g  = gap  || CARD_GAP;
    const originX = containerW / 2;
    // How many px of scroll travel the grow/retract animation spans
    const fadeZone = cW * 0.5;

    pathsRef.current.forEach((pathEl, i) => {
      if (!pathEl) return;
      const cardCenter = i * (cW + g) + cW / 2 - scrollLeft;
      const cardLeft  = cardCenter - cW / 2;
      const cardRight = cardCenter + cW / 2;

      // alpha: 0 = line fully retracted at origin, 1 = line fully extended to card
      const leftAlpha  = Math.min(1, Math.max(0, (cardLeft  + fadeZone) / fadeZone));
      const rightAlpha = Math.min(1, Math.max(0, (containerW - cardRight + fadeZone) / fadeZone));
      const alpha = Math.min(leftAlpha, rightAlpha);

      // Ease-out so the line shoots out quickly then settles
      const t = 1 - Math.pow(1 - alpha, 2);

      const dx  = cardCenter - originX;
      const p1x = originX;
      const p1y = SVG_H - t * SVG_H * 0.65;
      const p2x = originX + t * dx;
      const p2y = SVG_H - t * SVG_H * 0.35;
      const p3x = originX + t * dx;
      const p3y = SVG_H * (1 - t);

      pathEl.style.opacity = alpha > 0.01 ? '1' : '0';
      if (alpha > 0.01) {
        pathEl.setAttribute('d',
          `M ${originX} ${SVG_H} C ${p1x} ${p1y} ${p2x} ${p2y} ${p3x} ${p3y}`
        );
      }
    });
  };

  return (
    <div style={{ padding: '48px 0 0', display: 'flex', flexDirection: 'column', gap: '0' }}>
      {/* Client meta + compact arrows */}
      <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '12px' }}>
        <h3 style={{
          fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: '22px',
          letterSpacing: '-0.015em', color: 'var(--text-primary)', margin: 0,
        }}>{client.name}</h3>
        {arrowInfo.isCompact && (
          <div style={{ display: 'flex', gap: '8px', flexShrink: 0 }}>
            {[-1, 1].map(dir => (
              <button key={dir} onClick={() => arrowInfo.scroll?.(dir)} style={{
                width: '34px', height: '34px', borderRadius: '50%',
                background: 'rgba(10,10,11,0.78)',
                backdropFilter: 'blur(10px)',
                border: '1px solid rgba(255,255,255,0.13)',
                color: '#F5F5F7', cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                opacity: dir === -1 ? (arrowInfo.canLeft ? 1 : 0.25) : (arrowInfo.canRight ? 1 : 0.25),
                transition: 'opacity 250ms', flexShrink: 0,
              }}>
                <RowArrowIcon dir={dir} />
              </button>
            ))}
          </div>
        )}
      </div>

      {/* Outputs row */}
      <OutputsRow
        outputs={outputs}
        activeAudioId={activeAudioId}
        onActivateAudio={onActivateAudio}
        onUpdate={handleScrollUpdate}
        onArrowChange={setArrowInfo}
      />

      {/* Animated connector SVG — paths updated imperatively on scroll */}
      <svg ref={svgRef} width="100%" height={SVG_H}
        style={{ display: 'block', overflow: 'visible', marginTop: '2px' }}>
        <defs>
          <linearGradient id={`cg_${uid}`} x1="0" y1="1" x2="0" y2="0">
            <stop offset="0%"   stopColor="rgba(247,19,63,0.08)"/>
            <stop offset="50%"  stopColor="rgba(247,19,63,0.55)"/>
            <stop offset="100%" stopColor="rgba(247,19,63,1)"/>
          </linearGradient>
        </defs>
        {outputs.map((_, i) => (
          <path
            key={i}
            ref={el => { pathsRef.current[i] = el; }}
            fill="none"
            stroke={`url(#cg_${uid})`}
            strokeWidth="2"
            strokeDasharray="4 9"
            style={{ opacity: 0, transition: 'opacity 300ms' }}
          >
            <animate attributeName="stroke-dashoffset"
              from="52" to="0"
              dur={`${1.6 + i * 0.15}s`}
              repeatCount="indefinite"/>
          </path>
        ))}
      </svg>

      {/* Originals — deck centered */}
      <div style={{ maxWidth: '263px', margin: '0 auto', width: '100%' }}>
        <OriginalFanDeck originals={originals} />
        {client.description && (
          <p style={{
            fontFamily: 'var(--font-body)', fontSize: '13px',
            color: 'var(--text-secondary)', marginTop: '14px',
            lineHeight: 1.55, textAlign: 'center',
          }}>{client.description}</p>
        )}
      </div>
    </div>
  );
}

// ─── Skeleton row ─────────────────────────────────────────────────
function ShowcaseSkeleton() {
  return (
    <div style={{ padding: '48px 0 0', display: 'flex', flexDirection: 'column', gap: '20px' }}>
      {/* Header */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
        <SkeletonBlock style={{ width: '160px', height: '26px' }} />
        <SkeletonBlock style={{ width: '90px', height: '14px' }} />
      </div>
      {/* Outputs row */}
      <div style={{ display: 'flex', gap: `${CARD_GAP}px` }}>
        {[1, 2, 3, 4].map(i => (
          <SkeletonBlock key={i} style={{ width: `${SKEL_CARD_W}px`, height: `${SKEL_CARD_H}px`, flexShrink: 0 }} />
        ))}
      </div>
      {/* Connector */}
      <SkeletonBlock style={{ width: '100%', height: '72px' }} />
      {/* Original */}
      <SkeletonBlock style={{ maxWidth: '210px', margin: '0 auto', width: '100%', aspectRatio: '1/1' }} />
    </div>
  );
}

// ─── Main Showcase section ────────────────────────────────────────
function Showcase() {
  const { isMobile } = useBreakpoint();
  const [clients, setClients] = useShowState([]);
  const [loading, setLoading] = useShowState(true);
  const [error, setError] = useShowState('');
  // Shared audio state — only one video plays audio at a time across all clients
  const [activeAudioId, setActiveAudioId] = useShowState(null);

  useShowEffect(() => {
    const db = firebase.firestore();

    let cancelled = false;
    const loadAll = async () => {
      try {
        const clientSnap = await db.collection('clients').orderBy('order', 'asc').get();
        if (cancelled) return;

        if (clientSnap.empty) {
          setClients([]);
          setLoading(false);
          return;
        }

        const loaded = await Promise.all(
          clientSnap.docs.map(async (doc) => {
            const data = { id: doc.id, ...doc.data() };

            const [origSnap, outSnap] = await Promise.all([
              db.collection('clients').doc(doc.id).collection('originals').orderBy('order', 'asc').get(),
              db.collection('clients').doc(doc.id).collection('outputs').orderBy('order', 'asc').get(),
            ]);

            data.originals = origSnap.docs.map(d => ({ id: d.id, ...d.data() }));
            data.outputs = outSnap.docs.map(d => ({ id: d.id, ...d.data() }));
            return data;
          })
        );

        if (!cancelled) {
          setClients(loaded);
          setLoading(false);
        }
      } catch (err) {
        console.error('Showcase: error loading from Firestore', err);
        if (!cancelled) {
          setError('Nepodařilo se načíst data. Zkuste to znovu.');
          setLoading(false);
        }
      }
    };

    loadAll();
    return () => { cancelled = true; };
  }, []);

  return (
    <section data-screen-label="03 Ukázky" style={{
      maxWidth: '1440px',
      margin: '0 auto',
      padding: isMobile ? '48px 20px 48px' : '65px 32px 65px',
    }}>
      <NumberedHeader
        num="03"
        title="Co z vašeho produktu uděláme."
        sub="Dole je původní materiál a nahoře to, co z něj vyrobíme."
      />

      {/* Shimmer keyframe — inject once */}
      <style>{`
        @keyframes shimmer {
          0%, 100% { background-position: -200% 0; }
          50%       { background-position:  200% 0; }
        }
      `}</style>

      {loading ? (
        // Show 2 skeleton rows while loading
        <div>
          <ShowcaseSkeleton />
          <ShowcaseSkeleton />
        </div>
      ) : error ? (
        <div style={{
          padding: '60px 24px',
          textAlign: 'center',
          fontFamily: 'var(--font-body)',
          color: '#A0A0A8',
          fontSize: '15px',
        }}>{error}</div>
      ) : clients.length === 0 ? (
        <div style={{
          padding: '80px 24px',
          textAlign: 'center',
          fontFamily: 'var(--font-body)',
          color: '#606068',
          fontSize: '15px',
          border: '1px dashed rgba(255,255,255,0.07)',
          borderRadius: '12px',
        }}>
          Žádní klienti. Přidejte je v admin panelu.
        </div>
      ) : (
        <div>
          {clients.map((client, i) => (
            <ShowcaseRow
              key={client.id}
              client={client}
              idx={i + 1}
              total={clients.length}
              activeAudioId={activeAudioId}
              onActivateAudio={setActiveAudioId}
            />
          ))}
        </div>
      )}
    </section>
  );
}

// Export to window so index.html can use it
window.Showcase = Showcase;
window.OriginalFanDeck = OriginalFanDeck;
window.OutputCard = OutputCard;
window.OutputsRow = OutputsRow;
