// site-archive-sections.jsx — Archive edition extended sections
// Loaded after site-archive.jsx. Exports: ArchiveVideoCarousel, ArchiveQACarousel,
// ArchiveWinnersPanel, ArchivePhotosSection, ArchiveJurySection, ArchiveStudentsSection

// ── Inline YouTube thumb (no dependency on outer YouTubeThumb) ───────────────
function ArchYTThumb({ id, title }) {
  const [open, setOpen] = React.useState(false);
  if (!id) return <div style={{ aspectRatio: "16/9", background: "var(--ink-2)" }} />;
  const thumb = "https://img.youtube.com/vi/" + id + "/maxresdefault.jpg";
  if (open) return (
    <div style={{ position: "relative", aspectRatio: "16/9", background: "#000" }}>
      <iframe src={"https://www.youtube.com/embed/" + id + "?autoplay=1"} allow="autoplay; encrypted-media" allowFullScreen
        style={{ position: "absolute", inset: 0, width: "100%", height: "100%", border: "none" }} title={title || ""} />
    </div>
  );
  return (
    <div style={{ position: "relative", aspectRatio: "16/9", overflow: "hidden", cursor: "pointer", background: "#111" }}
      onClick={() => setOpen(true)}>
      <img src={thumb} alt={title || ""} style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", display: "block" }}
        onError={e => { e.currentTarget.onerror = null; e.currentTarget.src = "https://img.youtube.com/vi/" + id + "/hqdefault.jpg"; }} />
      <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center" }}>
        <div style={{ background: "var(--accent)", color: "#fff", display: "flex", alignItems: "center", gap: 10, padding: "12px 20px",
          fontFamily: "var(--mono)", fontSize: 11.5, letterSpacing: ".12em", textTransform: "uppercase" }}>
          <svg viewBox="0 0 24 24" fill="currentColor" style={{ width: 14, height: 14 }}><polygon points="5,3 19,12 5,21"/></svg> Play
        </div>
      </div>
    </div>
  );
}

// ── Arrow button ─────────────────────────────────────────────────────────────
function ArchArrow({ dir, onClick, disabled, accent, size }) {
  const [hov, setHov] = React.useState(false);
  const sz = size || 44;
  return (
    <button onClick={onClick} disabled={disabled}
      onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      style={{ width: sz, height: sz, flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center",
        background: disabled ? "rgba(255,255,255,0.04)" : hov ? "rgba(255,255,255,0.18)" : (accent || "var(--accent)"),
        border: "1px solid #fff", cursor: disabled ? "default" : "pointer", transition: "background .15s", padding: 0 }}>
      {dir === "prev"
        ? <svg viewBox="0 0 24 24" fill="none" stroke={disabled ? "rgba(255,255,255,0.25)" : "#fff"} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width: sz * 0.36, height: sz * 0.36 }}><path d="M15 18l-6-6 6-6"/></svg>
        : <svg viewBox="0 0 24 24" fill="none" stroke={disabled ? "rgba(255,255,255,0.25)" : "#fff"} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width: sz * 0.36, height: sz * 0.36 }}><path d="M9 18l6-6-6-6"/></svg>}
    </button>
  );
}

// ── Slide transition hook ────────────────────────────────────────────────────
function useArchSlider(total) {
  const [cur, setCur] = React.useState(0);
  const [phase, setPhase] = React.useState("idle");
  const [slots, setSlots] = React.useState([{ offset: 0 }]);
  const [trackX, setTrackX] = React.useState(0);
  const [targetX, setTargetX] = React.useState(0);
  const pending = React.useRef(null);
  const touchX  = React.useRef(null);

  const go = (dir) => {
    if (phase !== "idle") return;
    const next = cur + (dir === "next" ? 1 : -1);
    if (next < 0 || next >= total) return;
    pending.current = next;
    if (dir === "next") { setSlots([{ offset: 0 }, { offset: 1 }]); setTrackX(0);    setTargetX(-100); }
    else                { setSlots([{ offset: -1 }, { offset: 0 }]); setTrackX(-100); setTargetX(0); }
    setPhase("setup");
  };

  React.useEffect(() => {
    if (phase === "setup") requestAnimationFrame(() => requestAnimationFrame(() => setPhase("animating")));
  }, [phase]);

  const onEnd = () => {
    if (phase !== "animating") return;
    setCur(pending.current);
    setPhase("idle");
    setSlots([{ offset: 0 }]);
  };

  const onTouchStart = (e) => { touchX.current = e.touches[0].clientX; };
  const onTouchEnd   = (e) => {
    if (touchX.current === null) return;
    const delta = e.changedTouches[0].clientX - touchX.current;
    touchX.current = null;
    if (Math.abs(delta) < 40) return;
    go(delta < 0 ? "next" : "prev");
  };

  const currentX  = phase === "animating" ? targetX : (phase === "setup" ? trackX : 0);
  const transition = phase === "animating" ? "transform 0.7s cubic-bezier(0.16,1,0.3,1)" : "none";
  const reset = () => { setCur(0); setPhase("idle"); setSlots([{ offset: 0 }]); };

  return { cur, setCur, slots, currentX, transition, go, onEnd, onTouchStart, onTouchEnd, reset };
}

// ── VIDEO CAROUSEL — exact same pattern as AwardsVideoCarousel ──────────────
function ArchiveVideoCarousel({ videos, accent, kicker }) {
  const [active, setActive]     = React.useState(0);
  const [shown, setShown]       = React.useState(0);
  const [videoKey, setVideoKey] = React.useState(0);
  const [exitStyle, setExitStyle] = React.useState(null);
  const [enterAnim, setEnterAnim] = React.useState(null);
  const [textVisible, setTextVisible] = React.useState(true);
  const inFlight = React.useRef(false);
  const touchX   = React.useRef(null);

  const go = (dir) => {
    if (inFlight.current) return;
    inFlight.current = true;
    const next = (active + (dir === 'next' ? 1 : -1) + videos.length) % videos.length;
    setTextVisible(false);
    setExitStyle(dir === 'next' ? 'left' : 'right');
    setTimeout(() => {
      setActive(next); setShown(next); setVideoKey(k => k + 1);
      setExitStyle(null); setEnterAnim(dir === 'next' ? 'from-right' : 'from-left');
      setTimeout(() => setTextVisible(true), 80);
      setTimeout(() => { setEnterAnim(null); inFlight.current = false; }, 550);
    }, 390);
  };

  const onTouchStart = (e) => { touchX.current = e.touches[0].clientX; };
  const onTouchEnd   = (e) => {
    if (touchX.current === null) return;
    const delta = e.changedTouches[0].clientX - touchX.current;
    touchX.current = null;
    if (Math.abs(delta) < 40 || videos.length < 2) return;
    go(delta < 0 ? 'next' : 'prev');
  };

  if (!videos || !videos.length) return null;
  const vid = videos[shown];
  const hasMultiple = videos.length > 1;
  const ac = accent || "var(--accent)";

  const videoWrapStyle = exitStyle
    ? { transform: exitStyle === 'right' ? "translateX(55%)" : "translateX(-55%)", opacity: 0, transition: "transform 0.39s ease, opacity 0.33s ease" }
    : { transform: "translateX(0)", opacity: 1 };

  const enterAnimStyle = enterAnim
    ? { animation: enterAnim === 'from-left' ? "arch-from-left 0.47s ease forwards" : "arch-from-right 0.47s ease forwards" }
    : {};

  const arrowBtn = { width:48, height:48, borderRadius:0, background: ac, border:"none", outline:"none", display:"flex", alignItems:"center", justifyContent:"center", cursor:"pointer", position:"absolute", top:"50%", transform:"translateY(-50%)", zIndex:4, transition:"background 0.15s" };

  return (
    <section className="section dark tight">
      <style>{`
        @keyframes arch-from-left  { from { transform: translateX(-55%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
        @keyframes arch-from-right { from { transform: translateX(55%);  opacity: 0; } to { transform: translateX(0); opacity: 1; } }
      `}</style>
      <div className="wrap">
        {kicker && <Kicker n="▸">{kicker}</Kicker>}
        <div style={{ display:"grid", gridTemplateColumns:"1.2fr 1fr", gap:"clamp(57px,5vw,81px)", alignItems:"center", marginTop: kicker ? 16 : 0 }} className="arch-trailer-grid awards-video-grid">

          {/* Video + arrows */}
          <div className="awards-vid-wrap" style={{ position:"relative" }} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
            <div style={{ overflow:"hidden", background:"var(--ink)", width:"100%", aspectRatio:"16/9" }}>
              <div key={videoKey} style={{ width:"100%", height:"100%", ...(enterAnim ? enterAnimStyle : videoWrapStyle) }}>
                <ArchYTThumb id={vid.id} title={vid.title || vid.label} />
              </div>
            </div>
            {hasMultiple && (
              <button onClick={() => go('prev')}
                onMouseEnter={e => { e.currentTarget.style.background="#fff"; e.currentTarget.querySelector("svg").style.stroke="#16151a"; }}
                onMouseLeave={e => { e.currentTarget.style.background=ac; e.currentTarget.querySelector("svg").style.stroke="#fff"; }}
                className="av-arrow av-arrow-prev" style={{ ...arrowBtn, left:-43 }}>
                <svg viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width:18, height:18 }}><path d="M15 18l-6-6 6-6"/></svg>
              </button>
            )}
            {hasMultiple && (
              <button onClick={() => go('next')}
                onMouseEnter={e => { e.currentTarget.style.background="#fff"; e.currentTarget.querySelector("svg").style.stroke="#16151a"; }}
                onMouseLeave={e => { e.currentTarget.style.background=ac; e.currentTarget.querySelector("svg").style.stroke="#fff"; }}
                className="av-arrow av-arrow-next" style={{ ...arrowBtn, right:-43 }}>
                <svg viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width:18, height:18 }}><path d="M9 18l6-6-6-6"/></svg>
              </button>
            )}
          </div>

          {/* Text */}
          <div style={{ opacity: textVisible ? 1 : 0, transform: textVisible ? "translateY(0)" : "translateY(5px)", transition:"opacity 0.3s ease, transform 0.3s ease", minWidth: 0, overflow: "hidden" }}>
            {vid.label && (
              <div style={{ fontSize:11, letterSpacing:"0.14em", textTransform:"uppercase", fontFamily:"var(--mono)", color: ac, marginBottom:14 }}>
                {vid.label}
              </div>
            )}
            <p className="display" style={{ fontSize:"clamp(26px,4vw,56px)", color:"var(--paper)", lineHeight:0.95, margin:"0 0 clamp(14px,2vw,24px)", letterSpacing:"-0.01em" }}>
              {vid.title || vid.label || "Watch."}
            </p>
            {vid.desc && (
              <p style={{ fontSize:"clamp(13px,1.2vw,15px)", lineHeight:1.75, color:"rgba(240,238,233,0.62)", margin:"0 0 clamp(16px,2vw,28px)" }}>{vid.desc}</p>
            )}
            {vid.hosts && (
              <p style={{ fontSize:12, color:"rgba(240,238,233,0.4)", fontFamily:"var(--mono)", margin:"0 0 clamp(16px,2vw,28px)" }}>
                Hosted by {vid.hosts} &nbsp;&middot;&nbsp; {vid.date}
              </p>
            )}
            {hasMultiple && (
              <div style={{ fontSize:10, fontFamily:"var(--mono)", color:"rgba(240,238,233,0.28)", letterSpacing:"0.12em" }}>
                {String(active + 1).padStart(2,"0")} / {String(videos.length).padStart(2,"0")}
              </div>
            )}
          </div>
        </div>
      </div>
    </section>
  );
}


// ── WINNERS PANEL ────────────────────────────────────────────────────────────
function ArchiveWinnersPanel({ awards, year, navigate, strandColors }) {
  const [active, setActive] = React.useState(0);
  const w = awards[active];
  const sc = (strandColors || {})[w?.strand] || {};

  return (
    <section className="section">
      <div className="wrap">
        <Kicker n="▸">Paper Bird Awards</Kicker>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1.05fr", gap: "clamp(20px,4vw,52px)", alignItems: "start", marginTop: 16 }} className="arch-awards-grid">
          {/* List */}
          <div>
            <h2 className="display" style={{ fontSize: "clamp(28px,4vw,52px)", marginBottom: 20 }}>The honour roll.</h2>
            {awards.map((a, i) => {
              const c = (strandColors || {})[a.strand] || {};
              return (
                <div key={a.strand} onClick={() => setActive(i)}
                  style={{ display: "grid", gridTemplateColumns: "120px 1fr", padding: "13px 10px", borderTop: "1px solid var(--line)",
                    borderBottom: i === awards.length - 1 ? "1px solid var(--line)" : "none",
                    gap: 12, alignItems: "center", cursor: "pointer",
                    background: i === active ? "var(--paper-2)" : "transparent", transition: "background .12s" }}>
                  <span style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: ".1em", textTransform: "uppercase", color: c.hexLight || c.hex || "var(--ink)" }}>{a.strand === "Audience" ? "Audience" : a.strand + " Winner"}</span>
                  <span className="display" style={{ fontSize: "clamp(14px,1.6vw,20px)", fontStyle: "italic" }}>{a.film}</span>
                  
                </div>
              );
            })}
          </div>

          {/* Detail */}
          {w && (() => {
            const archFilms = (window.ARCHIVE_FILMS || {})[year] || [];
            const matched = archFilms.find(f =>
              f.title && f.title.toLowerCase().trim() === w.film.toLowerCase().trim() &&
              f.strand && f.strand.toLowerCase() === w.strand.toLowerCase()
            );
            const still = matched?.stills?.[0] || matched?.poster || null;
            const filmUrl = "/archive/" + year + "/" + w.strand.toLowerCase() + (matched?.id ? "/" + matched.id : "");
            return (
              <div style={{ border: "1px solid var(--line)", padding: "clamp(14px,2.5vw,28px)", position: "sticky", top: 110 }}>
                <div style={{ marginBottom: 16, overflow: "hidden", aspectRatio: "16/9", background: "var(--ink-2)" }}>
                  {still
                    ? <img src={still} alt={w.film} style={{ width:"100%", height:"100%", objectFit:"cover", display:"block" }} />
                    : <Frame seed={w.film + String(year)} ratio="16/9" corner={null} />}
                </div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: ".14em", textTransform: "uppercase", color: sc.hex || "var(--accent)", marginBottom: 8 }}>
                  Paper Bird &nbsp;&middot;&nbsp; {w.strand}
                </div>
                <h3 className="display" style={{ fontSize: "clamp(20px,2.5vw,34px)", fontStyle: "italic", margin: "0 0 6px", lineHeight: 1.05 }}>{w.film}</h3>
                <p style={{ fontSize: 13, color: "var(--muted)", margin: "0 0 18px" }}>dir. {w.director}</p>
                <button onClick={() => navigate(filmUrl)}
                  style={{ display: "inline-flex", alignItems: "center", gap: 8, background: "var(--accent)", color: "#fff", border: "none", padding: "11px 22px",
                    fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".1em", textTransform: "uppercase", cursor: "pointer", transition: "filter .15s" }}
                  onMouseEnter={e => { e.currentTarget.style.background = "#fff"; e.currentTarget.style.color = "var(--ink)"; }}
                  onMouseLeave={e => { e.currentTarget.style.background = "var(--accent)"; e.currentTarget.style.color = "#fff"; }}>
                  Go to film page
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width: 12, height: 12 }}><path d="M5 12h14M12 5l7 7-7 7"/></svg>
                </button>
              </div>
            );
          })()}
        </div>
      </div>
    </section>
  );
}

// ── PHOTOS SECTION ───────────────────────────────────────────────────────────
function ArchivePhotosSection({ photos, strandColors }) {
  // Accept either a flat array or a {category:[...]} object — flatten to one list
  const imgs = Array.isArray(photos)
    ? photos.filter(p => p && (p.url || typeof p === "string"))
    : Object.values(photos || {}).flat().filter(p => p && (p.url || typeof p === "string"));
  const [start, setStart] = React.useState(0);
  const PER = 3;
  React.useEffect(() => { preloadImages(imgs.map(im => im.url || im)); }, [imgs.length]);
  if (!imgs.length) return null;
  const total = imgs.length;
  const visible = imgs.slice(start, start + PER);

  return (
    <section className="section" style={{ background: "var(--paper-2)" }}>
      <div className="wrap">
        <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 20, flexWrap: "wrap", gap: 12 }}>
          <div>
            <Kicker n="▸">In Pictures</Kicker>
            <h2 className="display" style={{ fontSize: "clamp(28px,4vw,52px)", margin: 0 }}>From the screenings.</h2>
          </div>
        </div>
        <div className="arch-photos-grid" style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 2 }}>
          {visible.map((img, i) => (
            <div key={i} style={{ aspectRatio: "4/3", overflow: "hidden" }}>
              <img src={img.url || img} alt={img.caption || ""} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
            </div>
          ))}
          {visible.length < PER && Array.from({ length: PER - visible.length }).map((_, i) => (
            <div key={"e"+i} style={{ aspectRatio: "4/3", background: "var(--line)" }} />
          ))}
        </div>
        {total > PER && (
          <div style={{ display: "flex", justifyContent: "center", gap: 8, marginTop: 12 }}>
            <button onClick={() => setStart(s => Math.max(0, s - PER))} disabled={start === 0}
              style={{ padding: "7px 16px", background: "none", border: "1px solid var(--ink)", cursor: start === 0 ? "default" : "pointer", opacity: start === 0 ? 0.3 : 1 }}>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width: 13, height: 13 }}><path d="M15 18l-6-6 6-6"/></svg>
            </button>
            <button onClick={() => setStart(s => Math.min(total - PER, s + PER))} disabled={start + PER >= total}
              style={{ padding: "7px 16px", background: "none", border: "1px solid var(--ink)", cursor: start + PER >= total ? "default" : "pointer", opacity: start + PER >= total ? 0.3 : 1 }}>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ width: 13, height: 13 }}><path d="M9 18l6-6-6-6"/></svg>
            </button>
          </div>
        )}
      </div>
    </section>
  );
}

// ── JURY SECTION — video-carousel style ─────────────────────────────────────
function ArchiveJurySection({ jury, strands, year, accent, strandColors }) {
  const available = (strands || []).filter(s => jury && jury[s] && jury[s].length > 0);
  const [strandKey, setStrandKey] = React.useState(available[0] || null);
  const [active, setActive]       = React.useState(0);
  // Preload all jury photos on mount
  React.useEffect(() => {
    const allPhotos = Object.values(jury || {}).flat().map(m => m.photo).filter(Boolean);
    preloadImages(allPhotos);
  }, []);
  const [shown, setShown]         = React.useState(0);
  const [videoKey, setVideoKey]   = React.useState(0);
  const [exitStyle, setExitStyle] = React.useState(null);
  const [enterAnim, setEnterAnim] = React.useState(null);
  const [textVisible, setTextVisible] = React.useState(true);
  const inFlight = React.useRef(false);
  const ac = accent || "var(--accent)";

  const members = (jury && strandKey && jury[strandKey]) ? jury[strandKey] : [];

  React.useEffect(() => { setActive(0); setShown(0); setVideoKey(k => k+1); setTextVisible(true); }, [strandKey]);

  const go = (dir) => {
    if (inFlight.current || members.length < 2) return;
    inFlight.current = true;
    const next = (active + (dir === 'next' ? 1 : -1) + members.length) % members.length;
    setTextVisible(false);
    setExitStyle(dir === 'next' ? 'left' : 'right');
    setTimeout(() => {
      setActive(next); setShown(next); setVideoKey(k => k+1);
      setExitStyle(null); setEnterAnim(dir === 'next' ? 'from-right' : 'from-left');
      setTimeout(() => setTextVisible(true), 80);
      setTimeout(() => { setEnterAnim(null); inFlight.current = false; }, 550);
    }, 390);
  };

  const member = members[shown] || null;
  const hasMultiple = members.length > 1;

  const photoWrapStyle = exitStyle
    ? { transform: exitStyle === 'right' ? "translateX(55%)" : "translateX(-55%)", opacity: 0, transition: "transform 0.39s ease, opacity 0.33s ease" }
    : { transform: "translateX(0)", opacity: 1 };
  const enterAnimStyle = enterAnim
    ? { animation: enterAnim === 'from-left' ? "arch-jury-fl 0.47s ease forwards" : "arch-jury-fr 0.47s ease forwards" }
    : {};

  const arrowBtn = { width:48, height:48, borderRadius:0, background: ac, border:"none", outline:"none",
    display:"flex", alignItems:"center", justifyContent:"center", cursor:"pointer",
    position:"absolute", top:"50%", transform:"translateY(-50%)", zIndex:4, transition:"background 0.15s" };

  const [bioExpanded, setBioExpanded] = React.useState(false);
  const [isMobile, setIsMobile] = React.useState(typeof window !== 'undefined' && window.innerWidth <= 920);
  React.useEffect(() => {
    const h = () => setIsMobile(window.innerWidth <= 920);
    window.addEventListener('resize', h);
    return () => window.removeEventListener('resize', h);
  }, []);
  React.useEffect(() => { setBioExpanded(false); }, [shown, strandKey]);
  const touchX = React.useRef(null);
  const onTouchStart = (e) => { touchX.current = e.touches[0].clientX; };
  const onTouchEnd   = (e) => {
    if (touchX.current === null) return;
    const delta = e.changedTouches[0].clientX - touchX.current;
    touchX.current = null;
    if (Math.abs(delta) < 40 || !hasMultiple) return;
    go(delta < 0 ? 'next' : 'prev');
  };

  if (!available.length) return null;

  return (
    <section className="section dark">
      <style>{`
        @keyframes arch-jury-fl { from { transform: translateX(-55%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
        @keyframes arch-jury-fr { from { transform: translateX(55%);  opacity: 0; } to { transform: translateX(0); opacity: 1; } }
      `}</style>
      <div className="wrap">
        <Kicker n="▸">Jury {year}</Kicker>
        <h2 className="display" style={{ fontSize:"clamp(28px,4vw,52px)", color:"var(--paper)", margin:"0 0 16px" }}>The {strandKey} jury.</h2>
        <div className="arch-jury-strand-btns" style={{ display:"flex", gap:8, flexWrap:"wrap", marginBottom:"clamp(20px,3vw,32px)" }}>
          {available.map(s => {
            const c = (strandColors || {})[s] || {};
            const active = strandKey === s;
            return (
              <button key={s} onClick={() => setStrandKey(s)}
                className="arch-jury-strand-btn"
                style={{ padding:"10px 18px", fontFamily:"var(--mono)", fontSize:11, letterSpacing:".1em", textTransform:"uppercase",
                  background: active ? (c.hex || ac) : "transparent",
                  color: active ? (c.textOn || "#fff") : "rgba(240,238,233,0.7)",
                  border:"1.5px solid "+(c.hex || ac), cursor:"pointer", textAlign:"center",
                  transition:"background .15s, color .15s" }}>
                {s}
              </button>
            );
          })}
        </div>

        {member && (
          <div style={{ display:"grid", gridTemplateColumns:"clamp(180px,24vw,260px) 1fr", gap:"clamp(40px,5vw,72px)", alignItems:"center" }} className="awards-video-grid">
            {/* Photo — carousel slide */}
            <div className="awards-vid-wrap" style={{ position:"relative" }} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
              <div style={{ overflow:"hidden", background:"var(--ink-2)" }}>
                <div key={videoKey} style={{ aspectRatio:"3/4", ...(enterAnim ? enterAnimStyle : photoWrapStyle) }}>
                  {member.photo
                    ? <img src={member.photo} alt={member.name} style={{ width:"100%", height:"100%", objectFit:"cover", display:"block" }} />
                    : <div style={{ width:"100%", height:"100%", background:"var(--ink-2)" }} />}
                </div>
              </div>
              {hasMultiple && (
                <button onClick={() => go('prev')}
                  className="av-arrow av-arrow-prev"
                  onMouseEnter={e=>{e.currentTarget.style.background="#fff";e.currentTarget.querySelector("svg").style.stroke="#16151a";}}
                  onMouseLeave={e=>{e.currentTarget.style.background=ac;e.currentTarget.querySelector("svg").style.stroke="#fff";}}
                  style={{...arrowBtn,left:-43}}>
                  <svg viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{width:18,height:18}}><path d="M15 18l-6-6 6-6"/></svg>
                </button>
              )}
              {hasMultiple && (
                <button onClick={() => go('next')}
                  className="av-arrow av-arrow-next"
                  onMouseEnter={e=>{e.currentTarget.style.background="#fff";e.currentTarget.querySelector("svg").style.stroke="#16151a";}}
                  onMouseLeave={e=>{e.currentTarget.style.background=ac;e.currentTarget.querySelector("svg").style.stroke="#fff";}}
                  style={{...arrowBtn,right:-43}}>
                  <svg viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{width:18,height:18}}><path d="M9 18l6-6-6-6"/></svg>
                </button>
              )}
            </div>

            {/* Text */}
            <div style={{ opacity:textVisible?1:0, transform:textVisible?"translateY(0)":"translateY(5px)", transition:"opacity 0.3s ease, transform 0.3s ease" }}>
              <div style={{ fontSize:11, letterSpacing:"0.14em", textTransform:"uppercase", fontFamily:"var(--mono)", color: (strandColors && strandColors[strandKey] && strandColors[strandKey].hex) || ac, marginBottom:14 }}>
                {strandKey} Jury
              </div>
              <p className="display" style={{ fontSize:"clamp(26px,4vw,56px)", color:"var(--paper)", lineHeight:0.95, margin:"0 0 clamp(14px,2vw,24px)", letterSpacing:"-0.01em" }}>
                {member.name}
              </p>
              {member.bio && (() => {
                const LIMIT = 240;
                const isLong = isMobile && member.bio.length > LIMIT;
                const displayBio = !isLong || bioExpanded ? member.bio : member.bio.slice(0, LIMIT).replace(/\s+\S*$/, '') + '…';
                return (
                  <div>
                    {displayBio.split("\n\n").map((para, i) => (
                      <p key={i} style={{ fontSize:"clamp(13px,1.2vw,15px)", lineHeight:1.75, color:"rgba(240,238,233,0.72)", margin: i===0 ? 0 : "12px 0 0" }}>{para}</p>
                    ))}
                    {isLong && (
                      <button onClick={() => setBioExpanded(e => !e)}
                        style={{ marginTop:10, background:"none", border:"none", cursor:"pointer", fontFamily:"var(--mono)", fontSize:10.5, letterSpacing:".1em", textTransform:"uppercase", color:"var(--accent)", padding:0, display:"flex", alignItems:"center", gap:6 }}>
                        {bioExpanded ? "Read less ↑" : "Read more →"}
                      </button>
                    )}
                  </div>
                );
              })()}
              {hasMultiple && (
                <div style={{ marginTop:24, fontSize:10, fontFamily:"var(--mono)", color:"rgba(240,238,233,0.28)", letterSpacing:"0.12em" }}>
                  {String(active+1).padStart(2,"0")} / {String(members.length).padStart(2,"0")}
                </div>
              )}
            </div>
          </div>
        )}
      </div>
    </section>
  );
}


// ── STUDENTS SECTION — alphabetical, rectangular photo ───────────────────────
function ArchiveStudentsSection({ students, accent, strandColors }) {
  const COLS = 4;
  const sorted = [...(students||[])].sort((a,b) => a.name.localeCompare(b.name));
  const total = sorted.length;
  const pages = Math.ceil(total / COLS);
  const [page, setPage] = React.useState(0);
  const visible = sorted.slice(page * COLS, (page + 1) * COLS);

  if (!total) return null;

  return (
    <section className="section" style={{ background:"var(--paper-2)" }}>
      <div className="wrap">
        <div style={{ display:"flex", alignItems:"flex-end", justifyContent:"space-between", marginBottom:24, flexWrap:"wrap", gap:12 }}>
          <div>
            <Kicker n="▸">The Team</Kicker>
            <h2 className="display" style={{ fontSize:"clamp(28px,4vw,52px)", margin:0 }}>The programmers.</h2>
          </div>
          {pages > 1 && (
            <div style={{ display:"flex", gap:8, alignItems:"center" }}>
              <span style={{ fontFamily:"var(--mono)", fontSize:11, color:"var(--muted)" }}>
                {String(page+1).padStart(2,"0")} / {String(pages).padStart(2,"0")}
              </span>
              <button onClick={() => setPage(p => Math.max(0,p-1))} disabled={page===0}
                style={{ width:32, height:32, background:"none", border:"1px solid var(--line)", cursor:page===0?"default":"pointer", display:"flex", alignItems:"center", justifyContent:"center", opacity:page===0?0.3:1 }}>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{width:13,height:13}}><path d="M15 18l-6-6 6-6"/></svg>
              </button>
              <button onClick={() => setPage(p => Math.min(pages-1,p+1))} disabled={page===pages-1}
                style={{ width:32, height:32, background:"none", border:"1px solid var(--line)", cursor:page===pages-1?"default":"pointer", display:"flex", alignItems:"center", justifyContent:"center", opacity:page===pages-1?0.3:1 }}>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{width:13,height:13}}><path d="M9 18l6-6-6-6"/></svg>
              </button>
            </div>
          )}
        </div>

        <div style={{ display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:"clamp(12px,2vw,24px)" }} className="arch-students-grid">
          {visible.map(s => {
            const sc = (strandColors||{})[s.strand] || {};
            return (
              <div key={s.id||s.name}>
                <div style={{ width:"100%", aspectRatio:"3/4", overflow:"hidden", marginBottom:12, background:"var(--ink-2)", borderRight:"3px solid "+(sc.hex||accent||"var(--accent)"), borderBottom:"3px solid "+(sc.hex||accent||"var(--accent)") }}>
                  {s.photo
                    ? <img src={s.photo} alt={s.name} style={{ width:"100%", height:"100%", objectFit:"cover", objectPosition:"center 15%", display:"block" }} />
                    : <div style={{ width:"100%", height:"100%", background:sc.hex||"var(--ink-2)" }} />}
                </div>
                <div className="display" style={{ fontSize:"clamp(14px,1.4vw,17px)", marginBottom:3, lineHeight:1.1 }}>{s.name}</div>
                <div style={{ fontFamily:"var(--mono)", fontSize:9.5, letterSpacing:".12em", textTransform:"uppercase", color:sc.hex||accent||"var(--accent)", marginBottom:5 }}>{s.strand}</div>
                <div style={{ fontSize:11.5, color:"var(--muted)", lineHeight:1.55 }}>{s.role}</div>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}


Object.assign(window, {
  ArchYTThumb, ArchArrow, useArchSlider,
  ArchiveVideoCarousel, ArchiveWinnersPanel, ArchivePhotosSection, ArchiveJurySection, ArchiveStudentsSection,
});
