// site-programme.jsx — strand landing, per-strand film grid, and film detail.

const STRAND_META = {
  Documentary:  { color: "var(--purple)", hex: "#784c95", textOn: "#fff",     colorName: "Purple", italic: "of reality.",    desc: "Films rooted in reality — observational, investigative, personal. Work that bears witness to the world as it is, whether intimate or expansive." },
  Animation:    { color: "var(--yellow)", hex: "#ffc823", textOn: "#16151a",  colorName: "Yellow", italic: "of imagination.",desc: "Films where drawing, modelling and digital craft meet storytelling. From hand-drawn gesture to stop-motion to CGI — animation as a form in its own right." },
  Experimental: { color: "var(--cyan)",   hex: "#65c8d0", textOn: "#16151a",  colorName: "Cyan",   italic: "of form.",       desc: "Films that question what cinema can be. Structural, essayistic, formally adventurous — work that doesn't fit neatly anywhere else, and doesn't pretend to." },
  Fiction:      { color: "var(--pink)",   hex: "#ff5c70", textOn: "#fff",     colorName: "Pink",   italic: "of story.",      desc: "Scripted, acted, directed — stories imagined and put on screen. From intimate drama to dark comedy to genre film. The oldest tradition in cinema, still finding new ground." },
};

// Derive programmer names from the students list for a given strand
function getProgrammers(people, strand) {
  const names = (people || [])
    .filter(p => p.group === "student" && p.strand === strand && p.name)
    .map(p => p.name);
  if (!names.length) return null;
  if (names.length === 1) return names[0];
  return names.slice(0, -1).join(", ") + " & " + names[names.length - 1];
}

// Runtime formatting helpers
function fmtRuntime(mins, secs) {
  const m = parseInt(mins) || 0;
  const s = parseInt(secs) || 0;
  if (!m && !s) return "";
  return s ? m + "'" + String(s).padStart(2,"0") + "\"" : m + "'";
}
function fmtRuntimeLong(mins, secs) {
  const m = parseInt(mins) || 0;
  const s = parseInt(secs) || 0;
  if (!m && !s) return "";
  return s ? m + " MIN " + s + " SEC" : m + " MIN";
}

function getAwardLabel(film) {
  const prize = film.prize || "";
  const strand = film.strand || "";
  if (prize === "Jury") return "Paper Bird — " + strand + " Winner";
  if (prize === "Audience") return "Paper Bird — Audience Award";
  if (prize === "Special Mention") return "Special Mention — " + strand;
  return "";
}

// Merge hardcoded defaults with CMS overrides
function mergeStrandMeta(overrides) {
  const out = {};
  Object.keys(STRAND_META).forEach(s => { out[s] = { ...STRAND_META[s], ...(overrides?.[s] || {}) }; });
  return out;
}

// ── Programme landing: hero + stacked editorial strand rows ──────────────────
function ProgrammeLanding({ navigate }) {
  const d = useSite();
  const films = usePublishedFilms();
  const allStrandMeta = mergeStrandMeta(d.festival?.strandMeta);
  const order = ["Documentary", "Animation", "Experimental", "Fiction"];
  const countOf = (s) => films.filter((f) => f.strand === s).length;

  const pg = d.festival?.pages?.programme || {};

  return (
    <Page k="programme-landing">
      {/* ── Hero ── */}
      <section className="dark section tight page-hero">
        <div className="wrap prog-hero-grid" style={{ display: "grid", gridTemplateColumns: "1.5fr 1fr", gap: 48, alignItems: "end" }}>
          <div>
            <span className="eyebrow">{pg.heroEyebrow || ("Official Selection · " + d.festival.year)}</span>
            {d.festival.votingOpen && (
              <div style={{ display: "inline-flex", alignItems: "center", gap: 8, marginTop: 14, marginLeft: 30, padding: "7px 14px", borderRadius: 30, background: "rgba(34,197,94,0.12)", border: "1px solid rgba(34,197,94,0.35)" }}>
                <span className="voting-dot" style={{ width: 9, height: 9, borderRadius: "50%", background: "#22c55e", display: "inline-block" }} />
                <span style={{ fontFamily: "var(--mono)", fontSize: 11.5, letterSpacing: ".14em", textTransform: "uppercase", color: "#22c55e", fontWeight: 600 }}>Voting Live</span>
              </div>
            )}
            <h1 className="display" style={{ fontSize: "clamp(48px,9vw,128px)", margin: "16px 0 0", lineHeight: 0.92 }}>
              The<br /><span style={{ color: "var(--coral-bright)", fontStyle: "italic" }}>Programme.</span>
            </h1>
          </div>
          <div style={{ borderTop: "1px solid var(--line-d)", paddingTop: 18 }}>
            <p style={{ color: "rgba(240,238,233,0.78)", lineHeight: 1.65, fontSize: 15.5, marginBottom: 18 }}>
              {films.length} short films across four strands. {pg.heroLead || "Each strand programmes and presents independently, culminating in the Paper Bird Awards on the closing night."}
            </p>
            <div style={{ borderTop: "1px solid var(--line-d)", paddingTop: 12 }}>
              {order.map((s, i) =>
              <div key={s} style={{ display: "flex", justifyContent: "space-between", padding: "10px 0", borderBottom: i < 3 ? "1px solid var(--line-d)" : "none" }}>
                  <span className="label" style={{ color: STRAND_META[s].color }}>0{i + 1} · {s}</span>
                  <span className="label muted">{countOf(s)} films</span>
                </div>
              )}
            </div>
          </div>
        </div>
      </section>

      {/* ── Strand rows, stacked vertically ── */}
      {order.map((strand, i) =>
      <StrandRow key={strand} strand={strand} count={countOf(strand)} idx={i} navigate={navigate} meta={allStrandMeta[strand]} />
      )}
    </Page>);

}

// One strand presented as a full-width editorial section with alternating bg.
function StrandRow({ strand, count, idx, navigate, meta: metaProp }) {
  const d = useSite();
  const meta = metaProp || STRAND_META[strand];
  // 0 = paper, 1 = dark, 2 = paper-2, 3 = dark — gives strong light/dark rhythm
  const isDark = idx % 2 === 1;
  const bg = isDark ? "var(--ink)" : idx === 0 ? "var(--paper)" : "var(--paper-2)";
  const fg = isDark ? "var(--paper)" : "var(--ink)";
  const muted = isDark ? "var(--muted-d)" : "var(--muted)";
  const line = isDark ? "var(--line-d)" : "var(--line)";

  return (
    <section style={{ background: bg, color: fg, padding: "clamp(64px,8vw,120px) 0" }}>
      <div className="wrap">
        {/* Top kicker bar */}
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", borderBottom: "1px solid " + line, paddingBottom: 14, marginBottom: "clamp(28px,4vw,48px)", gap: 20 }}>
          <span className="eyebrow" style={{ color: meta.hex }}>▸ 0{idx + 1} · Strand</span>
          <span className="label" style={{ color: muted }}>{count} films · Edition VI</span>
        </div>

        {/* Full-width strand name — sized so even "EXPERIMENTAL" (longest) never overflows */}
        <h2 className="display" style={{ lineHeight: 0.88, letterSpacing: "-0.02em", margin: "0 0 clamp(20px,3vw,40px)", overflowWrap: "break-word", wordBreak: "break-word", fontSize: "clamp(28px,8vw,120px)" }}>
          {strand}<br />
          <span style={{ color: meta.hex, fontStyle: "italic" }}>{meta.italic}</span>
        </h2>

        {/* Two-column lower row: description (left) + meta table + CTA (right) */}
        <div className="strand-lower" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "clamp(28px,4vw,64px)", borderTop: "1px solid " + line, paddingTop: "clamp(24px,3vw,40px)" }}>
          <p style={{ fontSize: "clamp(15px,1.5vw,18px)", lineHeight: 1.7, color: muted, margin: 0 }}>
            {meta.desc}
          </p>
          <div>
            {[["Films", count], ["Programmers", getProgrammers(d.people, strand)], ["Award", "Paper Bird \u00b7 " + meta.colorName]].filter(([_k, v]) => v).map(([k, v], j) =>
            <div key={k} style={{ display: "flex", justifyContent: "space-between", padding: "10px 0", borderBottom: j < 2 ? "1px solid " + line : "none", gap: 16 }}>
                <span className="label muted" style={{ flex: "none" }}>{k}</span>
                <span className="label" style={{ color: fg, textAlign: "right", maxWidth: "70%" }}>{v}</span>
              </div>
            )}
            <button
              className="btn"
              style={{ marginTop: 24, background: meta.hex, color: meta.textOn, borderColor: meta.hex }}
              onClick={() => navigate("/programme/" + strand.toLowerCase())}>
              
              Browse {strand} <SIcon name="arrowR" />
            </button>
          </div>
        </div>
      </div>
    </section>);

}

// ── Strand page ───────────────────────────────────────────────────────────────
function StrandPage({ strand, navigate }) {
  const d = useSite();
  const key = strand ? strand.charAt(0).toUpperCase() + strand.slice(1).toLowerCase() : "";
  const meta = { ...STRAND_META[key], ...d.festival?.strandMeta?.[key] };
  const films = usePublishedFilms().filter((f) => f.strand === key);
  const [q, setQ] = React.useState("");
  const shown = q ? films.filter((f) => (f.title + f.directors + f.country).toLowerCase().includes(q.toLowerCase())) : films;

  if (!meta) return <ProgrammeLanding navigate={navigate} />;

  return (
    <Page k={"strand-" + key}>
      <section style={{ background: meta.hex, color: meta.textOn, padding: "clamp(48px,8vw,96px) 0 clamp(28px,4vw,52px)" }}>
        <div className="wrap">
          <button onClick={() => navigate("/programme")} style={{ background: "none", border: "none", cursor: "pointer", color: "inherit", opacity: .7, fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".14em", textTransform: "uppercase", display: "flex", alignItems: "center", gap: 6, marginBottom: 24, padding: 0 }}>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" style={{ width: 14, height: 14 }}><path d="M19 12H5M11 18l-6-6 6-6" /></svg>
            All strands
          </button>
          <h1 className="display" style={{ fontSize: "clamp(56px,12vw,160px)", textTransform: "uppercase", lineHeight: .88, letterSpacing: "-.02em" }}>{key}</h1>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginTop: 24, flexWrap: "wrap", gap: 16 }}>
            <p style={{ fontSize: 16, lineHeight: 1.6, maxWidth: 520, opacity: .88, margin: 0 }}>{meta.desc}<br /><span style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".1em", textTransform: "uppercase", opacity: .7, marginTop: 8, display: "block" }}>Curated by {getProgrammers(d.people, key)}</span></p>
            <input className="input" placeholder={"Search " + key + "…"} value={q} onChange={(e) => setQ(e.target.value)} style={{ maxWidth: 220, color: "#000", background: "rgba(255,255,255,0.2)", border: "1px solid rgba(255,255,255,0.3)", color: meta.textOn, fontSize: 13 }} />
          </div>
        </div>
      </section>

      <section className="section">
        <div className="wrap">
          {shown.length === 0 ?
          <p className="muted" style={{ textAlign: "center", padding: "60px 0" }}>No films match your search.</p> :

          <div className="grid cols-4 prog-grid">
              {shown.map((film, i) =>
            <FilmCard key={film.id} film={film} meta={meta} idx={i} navigate={navigate} />
            )}
            </div>
          }
        </div>
      </section>
    </Page>);

}

// ── Film card (shared between strand grid + "more from strand") ───────────────
function FilmCard({ film, meta, idx, navigate }) {
  const d = useSite();
  const m = meta || STRAND_META[film.strand] || {};
  const showVote = d.festival.votingOpen && String(film.year) === String(d.festival.year) && film.voteLink;
  return (
    <div className="film-card" onClick={() => navigate("/film/" + film.id)}>
      <div style={{ aspectRatio: "2/3", overflow: "hidden", position: "relative", background: (m.hex || "#555") + "22" }}>
        {film.poster ?
        <img src={film.poster} alt={film.title} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} /> :
        <Frame seed={film.title} ratio="2/3" corner={String((idx || 0) + 1).padStart(2, "0")} />
        }
        {getAwardLabel(film) && (
          <>
            <div style={{ position: "absolute", inset: "35% 0 0 0", background: "linear-gradient(to top, rgba(22,21,26,0.72) 0%, transparent 100%)", pointerEvents: "none" }} />
            <img
              src={film.prize === "Special Mention" ? "uploads/laurel special mention.svg" : "uploads/laurel winner.svg"}
              alt={getAwardLabel(film)}
              style={{ position: "absolute", bottom: "5%", right: "4%", width: "45%", filter: "brightness(0) invert(1)", opacity: 0.92, pointerEvents: "none" }}
            />
          </>
        )}
      </div>
      <div className="meta">
        <div className="toprow">
          <span className="tag" style={{ borderColor: m.hex, color: m.hex }}>{film.premiere || film.strand}</span>
          <span className="label muted">{fmtRuntime(film.runtime, film.runtimeSecs)}</span>
        </div>
        <div className="title">{film.title}</div>
        <div className="sub" style={{ color: m.hex }}>{film.directors} · {film.country}</div>
        {showVote && (
          <button
            onClick={(e) => { e.stopPropagation(); window.open(film.voteLink, "_blank"); }}
            style={{ marginTop: 12, width: "100%", background: m.hex, color: m.textOn, border: "none", padding: "9px 12px", fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".1em", textTransform: "uppercase", fontWeight: 700, cursor: "pointer", transition: "filter .15s" }}
            onMouseEnter={(e) => e.currentTarget.style.filter = "brightness(1.12)"}
            onMouseLeave={(e) => e.currentTarget.style.filter = "none"}>
            Vote For This Film
          </button>
        )}
      </div>
    </div>);

}

// ── Film detail ───────────────────────────────────────────────────────────────
function FilmDetail({ filmId, navigate }) {
  const d = useSite();
  const film = d.films.find((f) => f.id === filmId);
  const others = d.films.filter((f) => f.status === "published" && f.id !== filmId && f.strand === film?.strand).slice(0, 4);
  const meta = film ? STRAND_META[film.strand] || {} : {};

  // Build per-director bio list — prefer film.directorBios array, fall back to parsed names
  const dirBios = film?.directorBios && film.directorBios.length > 0 ?
  film.directorBios :
  film ? film.directors.split(/\s*[&,]\s*/).map((n) => ({ name: n.trim(), bio: "" })).filter((d) => d.name) : [];

  const credits = film?.credits || {};
  const creditFields = [
    ["Director",  film?.directors || "—"],
    ["Country",   film?.country   || "—"],
    ["Runtime",   film ? fmtRuntimeLong(film.runtime, film.runtimeSecs) || "—" : "—"],
    ["Strand",    film?.strand    || "—"],
    ["Film School", film?.school  || "—"],
    ...(film?.additionalCredits?.length
      ? film.additionalCredits.filter(c => c.name).map(c => [c.role, c.name])
      : [
          ["Producer",       credits.producer       || "—"],
          ["Cinematography", credits.cinematography  || "—"],
          ["Editor",         credits.editor         || "—"],
          ["Sound",          credits.sound          || "—"],
          ["Cast",           credits.cast           || "—"],
          ["Production",     credits.production     || film?.school || "—"],
        ]
    ),
  ].filter(([, v]) => v && v !== "—");


  if (!film) return (
    <Page k="film-missing">
      <section className="section"><div className="wrap" style={{ textAlign: "center" }}>
        <h1 className="display" style={{ fontSize: 48 }}>Film not found</h1>
        <button className="btn" onClick={() => navigate("/programme")} style={{ marginTop: 16 }}>Back to programme</button>
      </div></section>
    </Page>);


  const bannerImg = film.stillsImages?.[0]?.url || film.bannerUrl || film.poster;

  return (
    <Page k={"film-" + filmId}>

      {/* ── Hero: first still as banner + gradient overlay ── */}
      <section style={{ position: "relative", overflow: "hidden", color: meta.textOn || "#fff", minHeight: "clamp(480px, 68vh, 820px)", display: "flex", flexDirection: "column", justifyContent: "flex-end" }}>

        {/* Banner — first still, falls back to poster */}
        {bannerImg ?
        <img src={bannerImg} alt={film.title} style={{ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, width: "100%", height: "100%", objectFit: "cover", objectPosition: "center 25%", zIndex: 0 }} /> :
        <div style={{ position: "absolute", inset: 0, background: meta.hex, zIndex: 0 }} />
        }

        {/* Gradient: solid strand colour on left → transparent on right */}
        <div style={{ position: "absolute", inset: 0, zIndex: 1, background: `linear-gradient(to right, ${meta.hex} 0%, ${meta.hex}ee 28%, ${meta.hex}bb 46%, ${meta.hex}66 65%, ${meta.hex}22 82%, ${meta.hex}00 100%)` }} />

        {/* Bottom fade into strand colour so it bleeds into next section */}
        <div style={{ position: "absolute", bottom: 0, left: 0, right: 0, height: "35%", zIndex: 1, background: `linear-gradient(to bottom, ${meta.hex}00 0%, ${meta.hex}cc 70%, ${meta.hex} 100%)` }} />

        {/* Content layer */}
        <div className="wrap" style={{ position: "relative", zIndex: 2, paddingTop: 28, paddingBottom: "clamp(40px,6vw,80px)" }}>

          {/* Back link */}
          <button onClick={() => navigate("/programme/" + (film.strand || "").toLowerCase())}
          style={{ background: "none", border: "none", cursor: "pointer", color: "inherit", opacity: .8, fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".14em", textTransform: "uppercase", display: "inline-flex", alignItems: "center", gap: 6, padding: 0, marginBottom: "clamp(120px,20vh,280px)" }}>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" style={{ width: 13, height: 13 }}><path d="M19 12H5M11 18l-6-6 6-6" /></svg>
            {film.strand}
          </button>

          {/* Title block — left side, max 62% width so poster shows through on right */}
          <div style={{ maxWidth: "clamp(300px,62%,760px)" }}>
            <h1 className="display" style={{ fontSize: "clamp(38px,7vw,96px)", lineHeight: .88, letterSpacing: "-0.02em", marginBottom: "clamp(14px,2vw,24px)", wordBreak: "break-word" }}>
              {film.title}
            </h1>
            <div style={{ fontSize: "clamp(15px,1.8vw,20px)", opacity: .92, fontFamily: "var(--display)", fontWeight: 500, marginBottom: 8 }}>
              {film.directors}
            </div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".12em", textTransform: "uppercase", opacity: .7, lineHeight: 1.6 }}>
              {[film.country, fmtRuntimeLong(film.runtime, film.runtimeSecs) || null, film.school].filter(Boolean).join(" · ")}
            </div>
            {getAwardLabel(film) &&
            <div style={{ display: "inline-flex", alignItems: "center", gap: 6, marginTop: 14, background: "rgba(255,255,255,0.18)", padding: "6px 14px", backdropFilter: "blur(4px)" }}>
                <svg viewBox="0 0 24 24" fill="currentColor" style={{ width: 11, height: 11 }}><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" /></svg>
                <span style={{ fontFamily: "var(--mono)", fontSize: 9.5, letterSpacing: ".14em", textTransform: "uppercase" }}>{getAwardLabel(film)}</span>
              </div>
            }
          </div>
        </div>
      </section>

      {/* ── Synopsis + meta ── */}
      <section className="section">
        <div className="wrap film-head" style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: "clamp(32px,5vw,72px)", alignItems: "start" }}>
          <div>
            <Kicker n="§ 01" color={meta.hex}>Synopsis</Kicker>
            {film.synopsis ?
            <p style={{ fontSize: "clamp(16px,1.8vw,21px)", lineHeight: 1.7, color: "var(--ink)", marginTop: 12 }}>{film.synopsis}</p> :
            <p style={{ fontSize: 15, lineHeight: 1.7, color: "var(--muted)", marginTop: 12, fontStyle: "italic" }}>Synopsis to be added.</p>
            }
          </div>
          <div style={{ borderLeft: "1px solid var(--line)", paddingLeft: "clamp(20px,3vw,40px)" }}>
            {d.festival.votingOpen && String(film.year) === String(d.festival.year) && film.voteLink && (
            <button className="btn" style={{ width: "100%", marginBottom: 20, background: meta.hex, color: meta.textOn, borderColor: meta.hex }}
              onClick={() => window.open(film.voteLink, "_blank")}>
              Vote for this Film
            </button>
            )}
            <div>
              {[["Country", film.country], ["Runtime", fmtRuntimeLong(film.runtime, film.runtimeSecs)], ["Strand", film.strand],
              ...(film.school ? [["School", film.school]] : []),
              ...(film.premiere ? [["Screening", film.premiere]] : [])].
              map(([k, v]) =>
              <div key={k} style={{ display: "flex", justifyContent: "space-between", padding: "10px 0", borderBottom: "1px solid var(--line-2)", gap: 16 }}>
                  <span className="label muted" style={{ flex: "none" }}>{k}</span>
                  <span className="label" style={{ color: "var(--ink)", textAlign: "right" }}>{v}</span>
                </div>
              )}
            </div>
          </div>
        </div>
      </section>

      {/* ── Director(s) — bigger photo, full bio ── */}
      <section className="section" style={{ background: "var(--paper-2)" }}>
        <div className="wrap">
          <Kicker n="§ 02" color={meta.hex}>{dirBios.length > 1 ? "Directors" : "Director"}</Kicker>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(min(100%,360px), 1fr))", gap: "clamp(32px,5vw,64px)", marginTop: 32 }}>
            {dirBios.map((dir, i) =>
            <div key={dir.name} style={{ display: "grid", gridTemplateColumns: "clamp(96px,13vw,150px) 1fr", gap: "clamp(16px,2.5vw,28px)", alignItems: "start" }}>
                {/* Large circle portrait */}
                <div className={(dir.photo || film.directorPhoto) ? "img-zoomable" : ""} style={{ aspectRatio: "1/1", overflow: "hidden", borderRadius: "50%", border: "3px solid var(--line)", background: "var(--paper)", flex: "none" }}
                  onClick={() => (dir.photo || film.directorPhoto) && openLightbox([{ url: dir.photo || film.directorPhoto, caption: dir.name }])}>
                  <img
                  src={dir.photo || film.directorPhoto || "https://i.pravatar.cc/300?u=" + encodeURIComponent(dir.name + film.id)}
                  alt={dir.name}
                  style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                
                </div>
                {/* Name + bio */}
                <div>
                  <div className="display" style={{ fontSize: "clamp(18px,2.5vw,30px)", lineHeight: 1.1, marginBottom: 4 }}>{dir.name}</div>
                  <div className="label muted" style={{ marginBottom: 14 }}>Director · {film.country}{film.school ? " · " + film.school : ""}</div>
                  {dir.bio ?
                <p style={{ fontSize: 14.5, lineHeight: 1.8, color: "var(--muted)" }}>{dir.bio}</p> :
                <p style={{ fontSize: 14, lineHeight: 1.8, color: "var(--muted)", fontStyle: "italic" }}>Director biography to be added.</p>
                }
                </div>
              </div>
            )}
          </div>
        </div>
      </section>

      {/* ── Stills ── */}
      {(() => {
        const real = (film.stillsImages || []).filter(s => s && s.url).map(s => s.url);
        const urls = real.length
          ? real
          : (film.stills > 0
              ? Array.from({ length: Math.min(film.stills, 6) }, (_, i) => autoSrc(film.title + "s" + i, undefined, "16/9"))
              : []);
        if (!urls.length) return null;
        const items = urls.map(u => ({ url: u, caption: film.title }));
        return (
      <section className="section">
          <div className="wrap">
            <Kicker n="§ 03" color={meta.hex}>Stills</Kicker>
            <div className="grid cols-3" style={{ marginTop: 16 }}>
              {urls.map((u, i) =>
            <div key={i} className="img-zoomable" style={{ aspectRatio: "16/9", overflow: "hidden", background: (meta.hex || "#555") + "22" }}
              onClick={() => openLightbox(items, i)}>
              <img src={u} alt={film.title + " — still " + (i + 1)} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} loading="lazy" />
            </div>
            )}
            </div>
          </div>
        </section>
        );
      })()}

      {/* ── Full credits ── */}
      <section className="section dark tight">
        <div className="wrap">
          {film.specsEnabled && film.specs && film.specs.length > 0 && (
            <div style={{ marginBottom: 32 }}>
              <Kicker n="§ 05" color={meta.hex}>Specifications</Kicker>
              <div style={{ marginTop: 16 }}>
                {film.specs.map((s, i) => (
                  <div key={s.id || i} style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", padding: "10px 0", borderBottom: "1px solid var(--line-d)", gap: 20 }}>
                    <span style={{ fontWeight: 600, fontSize: 14, flex: "0 0 44%", color: "var(--ink-d)" }}>{s.label}:</span>
                    <span style={{ fontSize: 14, color: "var(--muted-d)", textAlign: "right" }}>{s.value}</span>
                  </div>
                ))}
              </div>
            </div>
          )}
          <Kicker n="§ 04" color={meta.hex}>Full credits</Kicker>
          <div className="film-credits-grid" style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: "0 clamp(24px,4vw,56px)", marginTop: 20 }}>
            {creditFields.map(([k, v]) =>
            <div key={k} style={{ display: "flex", justifyContent: "space-between", padding: "12px 0", borderBottom: "1px solid var(--line-d)", gap: 16 }}>
                <span className="label muted">{k}</span>
                <span style={{ fontSize: 13.5, color: v === "—" ? "rgba(240,238,233,0.28)" : "var(--paper)", textAlign: "right", maxWidth: 260 }}>{v}</span>
              </div>
            )}
          </div>
        </div>
      </section>

      {/* ── More from this strand ── */}
      {others.length > 0 &&
      <section className="section" style={{ background: "var(--paper-2)" }}>
          <div className="wrap">
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 32, flexWrap: "wrap", gap: 12 }}>
              <h2 className="display" style={{ fontSize: "clamp(28px,4vw,48px)" }}>More {film.strand}</h2>
              <span className="textlink" onClick={() => navigate("/programme/" + film.strand.toLowerCase())}>All {film.strand} <SIcon name="arrowR" /></span>
            </div>
            <div className="grid cols-4">
              {others.map((o, i) => <FilmCard key={o.id} film={o} idx={i} navigate={navigate} />)}
            </div>
          </div>
        </section>
      }
    </Page>);

}

function Programme({ navigate, strand }) {
  if (strand) return <StrandPage strand={strand} navigate={navigate} />;
  return <ProgrammeLanding navigate={navigate} />;
}

Object.assign(window, { Programme, FilmDetail, FilmCard, fmtRuntime, fmtRuntimeLong, getAwardLabel });