/* global React, PageIntro, useViewport, EnquiryBlock */
const { useState: useStatePD, useEffect: useEffectPD, useRef: useRefPD } = React;

/* ------------------------------------------------------------------ */
/* ProjectDetail                                                       */
/*                                                                    */
/* Template derives from Adelaide Airport live page + SEO brief:      */
/*   · Full-bleed hero image                                          */
/*   · Title strip with Project Information + Collections in use      */
/*   · H2 spine: The Brief · Design Response · Collections in Use ·   */
/*     Outcome                                                        */
/*   · 500-900-word narrative paragraphs                              */
/*   · One named quote                                                */
/*   · Specific metrics (seats, sqm, charging points, passengers)     */
/*   · Image slider (manual, arrows + dots)                           */
/*   · Contextual feature blocks (Wheelchair Zones / Connectivity)    */
/*   · Designer credit + photographer                                 */
/*   · Related projects + EnquiryBlock                                */
/*                                                                    */
/* The component takes a `project` prop so the same template drives   */
/* every case study. Defaults render the Adelaide Airport flagship.   */
/* ------------------------------------------------------------------ */

/* --- Info row (label + value) -------------------------------------- */
function InfoCell({ label, value }) {
  return (
    <div style={{
      padding: "16px 0",
      borderTop: "0.5px solid rgba(26,25,23,0.18)",
    }}>
      <div style={{
        fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase",
        color: "#6c6862", fontWeight: 500, marginBottom: 6,
      }}>
        {label}
      </div>
      <div style={{ fontSize: 14, color: "#1a1917", lineHeight: 1.4 }}>
        {value}
      </div>
    </div>
  );
}

/* --- Image slider -------------------------------------------------- */
function Slider({ images, vw }) {
  const [idx, setIdx] = useStatePD(0);
  const isMobile = vw < 760;
  const n = images.length;
  const go = (d) => setIdx(((idx + d) % n + n) % n);

  return (
    <div style={{ position: "relative" }}>
      <div style={{
        aspectRatio: isMobile ? "4/3" : "16/9",
        background: "#e4e2da", overflow: "hidden", position: "relative",
      }}>
        {images.map((img, i) => (
          <img
            key={i}
            src={img.src} srcSet={window.IMG && window.IMG.srcsetFor(img.src)} sizes={window.IMG && window.IMG.sizes()}
            alt={img.alt || ""}
            loading={i === 0 ? "eager" : "lazy"}
            style={{
              position: "absolute", inset: 0,
              width: "100%", height: "100%", objectFit: "cover", display: "block",
              opacity: i === idx ? 1 : 0,
              transition: "opacity 420ms cubic-bezier(0.2,0.6,0.2,1)",
            }}
          />
        ))}
      </div>
      {/* Arrows */}
      <button
        onClick={() => go(-1)}
        aria-label="Previous"
        style={{
          position: "absolute", top: "50%", left: isMobile ? 12 : 20,
          transform: "translateY(-50%)",
          width: 40, height: 40, borderRadius: "50%",
          background: "rgba(245,243,239,0.9)",
          backdropFilter: "blur(6px)", border: 0, cursor: "pointer",
          fontSize: 16, color: "#1a1917",
        }}
      >←</button>
      <button
        onClick={() => go(1)}
        aria-label="Next"
        style={{
          position: "absolute", top: "50%", right: isMobile ? 12 : 20,
          transform: "translateY(-50%)",
          width: 40, height: 40, borderRadius: "50%",
          background: "rgba(245,243,239,0.9)",
          backdropFilter: "blur(6px)", border: 0, cursor: "pointer",
          fontSize: 16, color: "#1a1917",
        }}
      >→</button>
      {/* Dots */}
      <div style={{
        position: "absolute", bottom: 16, left: 0, right: 0,
        display: "flex", justifyContent: "center", gap: 8,
      }}>
        {images.map((_, i) => (
          <button
            key={i}
            onClick={() => setIdx(i)}
            aria-label={`Slide ${i + 1}`}
            style={{
              width: 6, height: 6, borderRadius: "50%",
              background: i === idx ? "#f5f3ef" : "rgba(245,243,239,0.5)",
              border: 0, padding: 0, cursor: "pointer",
            }}
          />
        ))}
      </div>
    </div>
  );
}

/* --- Feature block (image + text, alternating side) ---------------- */
function FeatureBlock({ image, alt, eyebrow, title, body, reverse, vw }) {
  const isMobile = vw < 900;
  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: isMobile ? "1fr" : (reverse ? "minmax(0,1fr) minmax(0,1fr)" : "minmax(0,1fr) minmax(0,1fr)"),
      gap: 0,
      direction: isMobile ? "ltr" : (reverse ? "rtl" : "ltr"),
    }}>
      <div style={{
        direction: "ltr",
        aspectRatio: isMobile ? "4/3" : "auto",
        minHeight: isMobile ? "auto" : 520,
        background: "#e4e2da", overflow: "hidden", position: "relative",
      }}>
        <img src={image} srcSet={window.IMG && window.IMG.srcsetFor(image)} sizes={window.IMG && window.IMG.sizes()} alt={alt || ""} loading="lazy"
          style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
      </div>
      <div style={{
        direction: "ltr",
        background: "#f5f3ef",
        padding: isMobile ? "40px 24px" : "72px max(32px, 4vw)",
        display: "flex", alignItems: "center",
      }}>
        <div style={{ maxWidth: 460 }}>
          <div style={{
            fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase",
            color: "#6c6862", fontWeight: 500, marginBottom: 14,
          }}>
            {eyebrow}
          </div>
          <h3 style={{
            fontFamily: "var(--font-display, Poppins, sans-serif)",
            fontWeight: 500,
            fontSize: isMobile ? "clamp(24px, 6vw, 32px)" : "clamp(26px, 2.4vw, 36px)",
            lineHeight: 1.12, letterSpacing: "-0.02em",
            color: "#1a1917", margin: "0 0 18px 0", maxWidth: "18ch",
            textWrap: "balance",
          }}>
            {title}
          </h3>
          <p style={{ fontSize: isMobile ? 15 : 15.5, lineHeight: 1.65, color: "#3a3833", margin: 0, fontWeight: 400 }}>
            {body}
          </p>
        </div>
      </div>
    </div>
  );
}

/* --- Metric card --------------------------------------------------- */
function MetricCell({ value, label, note }) {
  return (
    <div style={{ padding: "28px 28px", borderRight: "0.5px solid rgba(26,25,23,0.15)" }}>
      <div style={{
        fontFamily: "var(--font-display, Poppins, sans-serif)",
        fontSize: "clamp(32px, 3.2vw, 48px)",
        fontWeight: 300, letterSpacing: "-0.03em",
        color: "#1a1917", marginBottom: 12, lineHeight: 1,
      }}>
        {value}
      </div>
      <div style={{
        fontSize: 11, letterSpacing: "0.14em", textTransform: "uppercase",
        color: "#1a1917", fontWeight: 500, marginBottom: 6,
      }}>
        {label}
      </div>
      {note && (
        <div style={{ fontSize: 12, color: "#6c6862", lineHeight: 1.4 }}>
          {note}
        </div>
      )}
    </div>
  );
}

/* --- Project tile masonry helpers --------------------------------- */

/* Project tiles size to each image's natural aspect ratio — so a portrait
   photo renders portrait, a landscape photo renders landscape, and the
   masonry rhythm comes from the photography itself. */

function ProjectTile({ src, alt, onRatio }) {
  return (
    <div style={{
      background: "#e4e2da",
      overflow: "hidden",
      display: "block",
    }}>
      <img src={src} srcSet={window.IMG && window.IMG.srcsetFor(src)} sizes={window.IMG && window.IMG.sizes()} alt={alt || ""} loading="lazy"
        onLoad={(e) => {
          const el = e.currentTarget;
          if (onRatio && el.naturalWidth && el.naturalHeight) onRatio(el.naturalWidth / el.naturalHeight);
        }}
        style={{ width: "100%", height: "auto", display: "block" }} />
    </div>
  );
}

/* --- ProjectMoodboard: JS masonry that places each tile into the shortest column,
   so the cascade packs tight with no trailing voids. Genuine panoramas
   (ratio >= 1.8) break out full-width as editorial punctuation.
   NOTE: named ProjectMoodboard (not Moodboard) to avoid colliding with the
   global Moodboard page component in Moodboard.jsx. ------- */
function ProjectMoodboard({ items, cols, gap }) {
  const [ratios, setRatios] = useStatePD({});
  const setRatio = (key, r) => setRatios((prev) => (prev[key] === r ? prev : { ...prev, [key]: r }));

  // Relative height weight for balancing (column width is shared/equal).
  const weightOf = (it, key) => {
    if (it.kind === "callout") return 0.95;
    const r = ratios[key] || 1.3;
    return 1 / r;
  };
  const keyOf = (it, i) => (it.kind === "image" ? it.src : `cb-${i}`) || `it-${i}`;
  const isWide = (it, key) => it.kind === "image" && (ratios[key] || 0) >= 1.8;

  // Split the ordered flow into segments: full-width panoramas stand alone;
  // everything else accumulates into masonry groups (order preserved).
  const segments = [];
  let group = null;
  items.forEach((it, i) => {
    const key = keyOf(it, i);
    if (isWide(it, key)) {
      if (group) { segments.push(group); group = null; }
      segments.push({ type: "wide", it, key });
    } else {
      if (!group) { group = { type: "masonry", entries: [] }; }
      group.entries.push({ it, key });
    }
  });
  if (group) segments.push(group);

  const renderItem = (it, key) => it.kind === "callout"
    ? <ProjectCallout eyebrow={it.eyebrow} title={it.title} body={it.body} />
    : <ProjectTile src={it.src} srcSet={window.IMG && window.IMG.srcsetFor(it.src)} sizes={window.IMG && window.IMG.sizes()} alt={it.alt} onRatio={(r) => setRatio(key, r)} />;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap }}>
      {segments.map((seg, si) => {
        if (seg.type === "wide") {
          return <div key={`w-${si}`}>{renderItem(seg.it, seg.key)}</div>;
        }
        // Greedy shortest-column packing for this masonry group.
        const colItems = Array.from({ length: cols }, () => []);
        const colH = Array(cols).fill(0);
        seg.entries.forEach(({ it, key }) => {
          let c = 0;
          for (let k = 1; k < cols; k++) if (colH[k] < colH[c] - 1e-6) c = k;
          colItems[c].push({ it, key });
          colH[c] += weightOf(it, key) + 0.04;
        });
        return (
          <div key={`m-${si}`} style={{ display: "flex", gap, alignItems: "flex-start" }}>
            {colItems.map((col, ci) => (
              <div key={ci} style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap }}>
                {col.map(({ it, key }) => (
                  <div key={key}>{renderItem(it, key)}</div>
                ))}
              </div>
            ))}
          </div>
        );
      })}
    </div>
  );
}

function ProjectCallout({ eyebrow, title, body }) {
  return (
    <div style={{
      background: "#ebe6de",
      padding: "36px 32px 40px",
      marginBottom: 2,
      breakInside: "avoid",
      WebkitColumnBreakInside: "avoid",
      pageBreakInside: "avoid",
      display: "block",
      border: "0.5px solid rgba(26,25,23,0.12)",
    }}>
      {eyebrow && (
        <div style={{
          fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase",
          color: "#6c6862", fontWeight: 500, marginBottom: 14,
        }}>
          {eyebrow}
        </div>
      )}
      <h3 style={{
        fontFamily: "var(--font-display, Poppins, sans-serif)",
        fontWeight: 500,
        fontSize: "clamp(20px, 1.6vw, 26px)",
        lineHeight: 1.18, letterSpacing: "-0.015em",
        color: "#1a1917", margin: "0 0 14px 0", maxWidth: "20ch",
        textWrap: "balance",
      }}>
        {title}
      </h3>
      <p style={{ fontSize: 14, lineHeight: 1.6, color: "#3a3833", margin: 0, fontWeight: 400 }}>
        {body}
      </p>
    </div>
  );
}

/* --- Hero carousel (only used when >1 hero image is declared) ------ */
function HeroCarousel({ images, alt, isMobile }) {
  const [idx, setIdx] = useStatePD(0);
  const [paused, setPaused] = useStatePD(false);
  useEffectPD(() => {
    if (paused || images.length < 2) return;
    const t = setInterval(() => setIdx((i) => (i + 1) % images.length), 7000);
    return () => clearInterval(t);
  }, [paused, images.length]);
  return (
    <div
      onMouseEnter={() => setPaused(true)}
      onMouseLeave={() => setPaused(false)}
      style={{
        aspectRatio: isMobile ? "4/5" : "16/8",
        maxHeight: isMobile ? "auto" : "90vh",
        overflow: "hidden", position: "relative", background: "#e4e2da",
      }}>
      {images.map((src, i) => (
        <img key={i} src={src} srcSet={window.IMG && window.IMG.srcsetFor(src)} sizes={window.IMG && window.IMG.sizes()} alt={alt || ""} loading={i === 0 ? "eager" : "lazy"}
          style={{
            position: "absolute", inset: 0, width: "100%", height: "100%",
            objectFit: "cover", display: "block",
            opacity: i === idx ? 1 : 0,
            transition: "opacity 1100ms ease",
          }}
        />
      ))}
      <div style={{
        position: "absolute", bottom: 28, left: 0, right: 0,
        display: "flex", justifyContent: "center", gap: 10,
      }}>
        {images.map((_, i) => (
          <button key={i} onClick={() => setIdx(i)} aria-label={`Slide ${i + 1}`}
            style={{
              width: i === idx ? 24 : 6, height: 2,
              background: i === idx ? "rgba(245,243,239,0.95)" : "rgba(245,243,239,0.45)",
              border: 0, padding: 0, cursor: "pointer",
              transition: "width 320ms ease, background 320ms ease",
            }}
          />
        ))}
      </div>
    </div>
  );
}

/* --- ProjectDetail page ------------------------------------------- */
function ProjectDetail({ project, slug, setScreen }) {
  const vw = useViewport();
  const isMobile = vw < 900;
  const isSmall = vw < 720;

  // Resolve project: explicit prop > slug lookup > Adelaide flagship default
  const resolved = project
    || (slug && window.getProject ? window.getProject(slug) : null)
    || (window.getProject ? window.getProject("project-adelaide") : null)
    || ADELAIDE_AIRPORT;

  const p = resolved;

  // Hero may be a single image or an array — only use carousel when 2+.
  const heroList = Array.isArray(p.hero) ? p.hero.filter(Boolean) : (p.hero ? [p.hero] : []);

  // De-duplicate imagery across the whole page. The former hero image now
  // leads the moodboard cascade as the first tile; gallery / featureA /
  // singleImage / featureB images each get added only if unseen. The result
  // is a flat list of unique tiles that flow into the masonry.
  // Identity key: collapses pixel-identical files that live at two paths
  // (e.g. a gallery photo copied into the feature/ folder). Falls back to the
  // raw path when the image is unique. See images-dupemap.js (window.IMG_CANON).
  const canonId = (src) => (typeof window !== "undefined" && window.IMG_CANON && window.IMG_CANON[src]) || src;
  const used = new Set();
  const tileImages = [];
  const pushImage = (src, alt) => {
    if (!src) return;
    const id = canonId(src);
    if (used.has(id)) return;
    used.add(id);
    tileImages.push({ kind: "image", src, alt });
  };
  heroList.forEach((src) => pushImage(src, p.heroAlt));
  (p.gallery || []).forEach((g) => g && pushImage(g.src, g.alt));
  if (p.featureA) pushImage(p.featureA.image, p.featureA.alt);
  if (p.singleImage) pushImage(p.singleImage.src, p.singleImage.alt);
  if (p.featureB) pushImage(p.featureB.image, p.featureB.alt);
  // Manifest-driven: append any images dropped into this project's folders
  // (images/projects/<slug>/{hero,gallery,feature}) that aren't already shown.
  // Dedup is by exact src, so curated tiles are untouched — only genuinely new
  // files appear, after running `node generate-manifests.js`.
  if (typeof window !== "undefined" && window.IMG && window.IMG.projSlides && p.slug) {
    const _pslug = String(p.slug).replace(/^project-/, "");
    ["hero", "gallery", "feature"].forEach((role) => {
      window.IMG.projSlides(_pslug, role).forEach((s) => pushImage(s.src, s.alt));
    });
  }

  // Callouts — only render if title isn't a placeholder.
  const calloutFromFeature = (f) => f && f.title && !/^\[/.test(String(f.title).trim())
    ? { kind: "callout", eyebrow: f.eyebrow, title: f.title, body: f.body, anchorSrc: f.image }
    : null;
  const callouts = [calloutFromFeature(p.featureA), calloutFromFeature(p.featureB)].filter(Boolean);

  // Insert each callout immediately after its anchor image when present, so
  // the wheelchair copy sits next to wheelchair photography and the power
  // copy sits next to the charging photography. Fall back to thirds if the
  // anchor image isn't in the flow.
  const flowItems = [...tileImages];
  callouts.forEach((c, idx) => {
    let insertAt = -1;
    if (c.anchorSrc) {
      const found = flowItems.findIndex((it) => it.kind === "image" && canonId(it.src) === canonId(c.anchorSrc));
      if (found >= 0) insertAt = found + 1;
    }
    if (insertAt < 0) {
      // No anchor — fall back to thirds positioning.
      insertAt = idx === 0
        ? Math.max(1, Math.floor(flowItems.length / 3))
        : Math.max(2, Math.floor(flowItems.length * 2 / 3));
    }
    flowItems.splice(Math.min(insertAt, flowItems.length), 0, c);
  });

  const tileCols = isSmall ? 1 : 2;
  const tileGap = isSmall ? 8 : 14;

  return (
    <>
      {/* Details first — title strip + project information (no opening hero) */}
      <section style={{
        marginTop: 60, // clear the fixed header
        padding: isMobile ? "40px 20px 32px" : "72px max(32px, 4vw) 48px",
        maxWidth: 1440, margin: "0 auto",
      }}>
        <div style={{
          display: "grid",
          gridTemplateColumns: isMobile ? "1fr" : "minmax(0, 5fr) minmax(0, 7fr)",
          gap: isMobile ? 32 : 64,
          alignItems: "start",
        }}>
          {/* Left — title */}
          <div>
            <div style={{
              fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase",
              color: "#6c6862", fontWeight: 500, marginBottom: 16,
            }}>
              {p.category} · {p.year}
            </div>
            <h1 style={{
              fontFamily: "var(--font-display, Poppins, sans-serif)",
              fontWeight: 500,
              fontSize: isMobile ? "clamp(32px, 8vw, 44px)" : "clamp(40px, 4.2vw, 64px)",
              lineHeight: 1.04, letterSpacing: "-0.03em",
              color: "#1a1917", margin: "0 0 14px 0", maxWidth: "16ch",
              textWrap: "balance",
            }}>
              {p.name}
            </h1>
            <div style={{ fontSize: isMobile ? 14 : 15, color: "#6c6862", lineHeight: 1.4 }}>
              {p.location}
            </div>
          </div>

          {/* Right — info grid */}
          <div style={{
            display: "grid",
            gridTemplateColumns: isMobile ? "1fr 1fr" : "repeat(4, 1fr)",
            columnGap: isMobile ? 20 : 32,
            rowGap: 0,
            alignSelf: "end",
          }}>
            <InfoCell label="Designer" value={p.architect} />
            <InfoCell label="Year" value={p.year} />
            <InfoCell label="Collection" value={(p.collections && p.collections.length > 0) ? p.collections.map((c) => c.name).join(", ") : p.sector} />
            <InfoCell label="Photographer" value={p.photographer} />
          </div>
        </div>
      </section>

      {/* Narrative — Description (always renders so the space is reserved for copy) */}
      <section style={{
        padding: isMobile ? "32px 20px" : "48px max(32px, 4vw)",
        maxWidth: 1440, margin: "0 auto",
      }}>
        <div style={{
          display: "grid",
          gridTemplateColumns: isMobile ? "1fr" : "minmax(0, 3fr) minmax(0, 6fr)",
          gap: isMobile ? 20 : 80,
          alignItems: "start",
        }}>
          <h2 style={{
            fontFamily: "var(--font-display, Poppins, sans-serif)",
            fontWeight: 500,
            fontSize: isMobile ? "clamp(22px, 5.5vw, 28px)" : "clamp(24px, 2.2vw, 34px)",
            lineHeight: 1.12, letterSpacing: "-0.015em",
            color: "#1a1917", margin: 0, maxWidth: "14ch",
          }}>
            Description
          </h2>
          <div style={{ fontSize: isMobile ? 15 : 16, lineHeight: 1.7, color: "#1a1917", maxWidth: "58ch" }}>
            {(p.brief && p.brief.length > 0 && p.brief.some((s) => s && !/^\[/.test(String(s).trim()))) ? (
              p.brief.map((para, i) => (
                <p key={i} style={{ margin: "0 0 18px 0", color: i === 0 ? "#1a1917" : "#3a3833" }}>
                  {para}
                </p>
              ))
            ) : (
              <p style={{ margin: 0, color: "#a9a49c", fontStyle: "italic", fontSize: isMobile ? 14 : 15 }}>
                Project description — to be supplied. Two to three short paragraphs covering the brief, the design response, and the outcome. Replace this placeholder once copy is ready.
              </p>
            )}
          </div>
        </div>
      </section>

      {/* Featured collections — slim strip between the description and the cascade */}
      {p.collections && p.collections.length > 0 && (
        <section style={{
          padding: isMobile ? "8px 20px 24px" : "0 max(32px, 4vw) 40px",
          maxWidth: 1440, margin: "0 auto",
        }}>
          <div style={{
            display: "grid",
            gridTemplateColumns: isMobile ? "1fr" : "minmax(0, 3fr) minmax(0, 6fr)",
            gap: isMobile ? 14 : 80,
            alignItems: "baseline",
            borderTop: "0.5px solid rgba(26,25,23,0.18)",
            paddingTop: isMobile ? 20 : 28,
          }}>
            <div style={{ fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "#6c6862", fontWeight: 500 }}>
              Featured collections
            </div>
            <div style={{ display: "flex", flexWrap: "wrap", gap: isMobile ? "10px 18px" : "12px 28px" }}>
              {p.collections.map((c) => (
                <a
                  key={c.name}
                  onClick={() => c.slug === "gateway" ? setScreen("gateway") : setScreen("collections")}
                  style={{
                    cursor: "pointer", display: "inline-flex", alignItems: "baseline", gap: 8,
                    fontFamily: "var(--font-display, Poppins, sans-serif)",
                    fontSize: isMobile ? 18 : 22, fontWeight: 500, letterSpacing: "-0.01em",
                    color: "#1a1917",
                  }}
                >
                  {c.name}
                  <span style={{ fontSize: 14, color: "#6c6862" }}>→</span>
                </a>
              ))}
            </div>
          </div>
        </section>
      )}

      {/* Masonry tiles — unique images and callout text boxes flowing together */}
      {flowItems.length > 0 && (
        <section style={{
          padding: isMobile ? "24px 20px" : "48px max(32px, 4vw)",
          maxWidth: 1600, margin: "0 auto",
        }}>
          <ProjectMoodboard items={flowItems} cols={tileCols} gap={tileGap} />
        </section>
      )}

      {/* Pull quote — named */}
      {p.quote && (
        <section style={{
          padding: isMobile ? "48px 20px" : "96px max(32px, 4vw)",
          maxWidth: 1440, margin: "0 auto",
        }}>
          <div style={{
            borderTop: "0.5px solid rgba(26,25,23,0.25)",
            borderBottom: "0.5px solid rgba(26,25,23,0.25)",
            padding: isMobile ? "40px 0" : "64px 0",
          }}>
            <blockquote style={{ margin: 0, maxWidth: 920 }}>
              <p style={{
                fontFamily: "var(--font-display, Poppins, sans-serif)",
                fontWeight: 500,
                fontSize: isMobile ? "clamp(22px, 5.5vw, 30px)" : "clamp(26px, 2.6vw, 40px)",
                lineHeight: 1.22, letterSpacing: "-0.02em",
                color: "#1a1917", margin: "0 0 32px 0",
                textWrap: "balance",
              }}>
                "{p.quote.text}"
              </p>
              <footer style={{
                display: "flex", alignItems: "baseline", gap: 14,
                fontSize: 12, letterSpacing: "0.14em", textTransform: "uppercase",
                color: "#6c6862", fontWeight: 500,
              }}>
                <span style={{ color: "#1a1917" }}>{p.quote.name}</span>
                <span>·</span>
                <span>{p.quote.role}</span>
              </footer>
            </blockquote>
          </div>
        </section>
      )}

      {/* Feature blocks + single image — now handled inline within the masonry above. */}

      {/* Metrics + Outcome sections removed — not consistently sourceable across projects. */}

      {/* Collections in use — removed; featured collections now listed up top under the details. */}

      {/* Related projects */}
      <section style={{
        padding: isMobile ? "0 20px 80px" : "0 max(32px, 4vw) 120px",
        maxWidth: 1440, margin: "0 auto",
      }}>
        <div style={{
          borderTop: "0.5px solid rgba(26,25,23,0.18)",
          paddingTop: isMobile ? 32 : 48,
        }}>
          <div style={{
            display: "flex", alignItems: "baseline", justifyContent: "space-between",
            marginBottom: isMobile ? 24 : 32, gap: 16, flexWrap: "wrap",
          }}>
            <div style={{ fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "#6c6862", fontWeight: 500 }}>
              Related projects
            </div>
            <a onClick={() => setScreen("projects")} style={{
              fontSize: 12, letterSpacing: "0.12em", textTransform: "uppercase",
              color: "#1a1917", borderBottom: "0.5px solid #1a1917",
              paddingBottom: 3, fontWeight: 500, cursor: "pointer",
            }}>All projects →</a>
          </div>
          <div style={{
            display: "grid",
            gridTemplateColumns: isSmall ? "1fr" : "repeat(3, 1fr)",
            gap: isMobile ? 32 : 24,
          }}>
            {(() => {
              const all = (typeof window !== "undefined" && window.PROJECTS) || [];
              const mine = new Set((p.collections || []).map((c) => c.slug));
              const scored = all
                .filter((x) => x && x.slug !== p.slug)
                .map((x) => ({ x, shared: (x.collections || []).filter((c) => mine.has(c.slug)).length }))
                .filter((e) => e.shared > 0)
                .sort((a, b) => b.shared - a.shared || (b.x.year || 0) - (a.x.year || 0));
              let picks = scored.map((e) => e.x);
              if (picks.length < 3) {
                const have = new Set(picks.map((x) => x.slug));
                picks = picks.concat(all.filter((x) => x && x.slug !== p.slug && !have.has(x.slug) && x.category === p.category));
              }
              picks = picks.slice(0, 3);
              if (picks.length === 0) return (p.related || DEFAULT_RELATED).map((r) => (
                <div key={r.name} style={{ cursor: "pointer" }}>
                  <div style={{ aspectRatio: "4/3", background: "#e4e2da", overflow: "hidden" }}>
                    <img src={r.image} srcSet={window.IMG && window.IMG.srcsetFor(r.image)} sizes={window.IMG && window.IMG.sizes()} alt={r.name} loading="lazy" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                  </div>
                  <div style={{ height: "0.5px", background: "rgba(26,25,23,0.18)", marginTop: 14 }} />
                  <div style={{ marginTop: 10, display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 12 }}>
                    <div style={{ fontSize: 16, fontWeight: 500, color: "#1a1917", letterSpacing: "-0.005em" }}>{r.name}</div>
                    <div style={{ fontSize: 11, color: "#6c6862", letterSpacing: "0.12em", textTransform: "uppercase" }}>{r.year}</div>
                  </div>
                  <div style={{ fontSize: 13, color: "#6c6862", marginTop: 4 }}>{r.location}</div>
                </div>
              ));
              return picks.map((r) => {
                const img = Array.isArray(r.hero) ? r.hero[0] : (r.indexImage || r.hero);
                const shared = (r.collections || []).filter((c) => mine.has(c.slug)).map((c) => c.name);
                return (
                  <div key={r.slug} onClick={() => setScreen(r.slug)} role="link" tabIndex={0}
                    onKeyDown={(e) => { if (e.key === "Enter") setScreen(r.slug); }}
                    style={{ cursor: "pointer" }}>
                    <div style={{ aspectRatio: "4/3", background: "#e4e2da", overflow: "hidden" }}>
                      <img src={img} srcSet={window.IMG && window.IMG.srcsetFor(img)} sizes={window.IMG && window.IMG.sizes()} alt={r.name} loading="lazy" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                    </div>
                    <div style={{ height: "0.5px", background: "rgba(26,25,23,0.18)", marginTop: 14 }} />
                    <div style={{ marginTop: 10, display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 12 }}>
                      <div style={{ fontSize: 16, fontWeight: 500, color: "#1a1917", letterSpacing: "-0.005em" }}>{r.name}</div>
                      <div style={{ fontSize: 11, color: "#6c6862", letterSpacing: "0.12em", textTransform: "uppercase" }}>{r.year}</div>
                    </div>
                    <div style={{ fontSize: 13, color: "#6c6862", marginTop: 4 }}>{r.location}</div>
                    {shared.length > 0 && (
                      <div style={{ fontSize: 11, color: "#6c6862", marginTop: 6, letterSpacing: "0.06em" }}>Shared: {shared.join(", ")}</div>
                    )}
                  </div>
                );
              });
            })()}
          </div>
        </div>
      </section>

      {(() => {
        const all = (typeof window !== "undefined" && window.PROJECTS) || [];
        const idx = all.findIndex((x) => x && x.slug === p.slug);
        const next = idx >= 0 ? all[(idx + 1) % all.length] : null;
        if (!next || next.slug === p.slug) return null;
        const nextImg = Array.isArray(next.hero) ? next.hero[0] : (next.hero || next.indexImage);
        return (
          <section
            onClick={() => setScreen(next.slug)}
            role="link" tabIndex={0}
            onKeyDown={(e) => { if (e.key === "Enter") setScreen(next.slug); }}
            style={{ cursor: "pointer", borderTop: "0.5px solid rgba(26,25,23,0.18)" }}>
            <div style={{
              maxWidth: 1440, margin: "0 auto",
              padding: isMobile ? "40px 20px" : "56px max(32px, 4vw)",
              display: "grid", gridTemplateColumns: isSmall ? "1fr" : "1fr auto",
              alignItems: "center", gap: isMobile ? 20 : 40,
            }}>
              <div>
                <div style={{ fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "#6c6862", fontWeight: 500, marginBottom: 12 }}>Next project</div>
                <div style={{ fontFamily: "var(--font-display, Poppins, sans-serif)", fontWeight: 500, fontSize: isMobile ? "clamp(26px, 7vw, 34px)" : "clamp(32px, 3.4vw, 52px)", lineHeight: 1.05, letterSpacing: "-0.02em", color: "#1a1917" }}>
                  {next.name} <span style={{ color: "#6c6862" }}>→</span>
                </div>
                <div style={{ fontSize: 13, color: "#6c6862", marginTop: 10, letterSpacing: "0.02em" }}>{next.location}{next.year ? ` · ${next.year}` : ""}</div>
                <div style={{ marginTop: 22 }}>
                  <span style={{
                    display: "inline-flex", alignItems: "center", gap: 10,
                    fontSize: 12, letterSpacing: "0.12em", textTransform: "uppercase", fontWeight: 500,
                    color: "#f5f3ef", background: "#1a1917", padding: "13px 26px",
                  }}>View next project →</span>
                </div>
              </div>
              {nextImg && !isSmall && (
                <div style={{ width: 220, aspectRatio: "16/10", overflow: "hidden", background: "#e4e2da" }}>
                  <img src={nextImg} srcSet={window.IMG && window.IMG.srcsetFor(nextImg)} sizes={window.IMG && window.IMG.sizes()} alt={next.name} loading="lazy" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                </div>
              )}
            </div>
          </section>
        );
      })()}

      <EnquiryBlock collection={p.collections && p.collections[0] ? p.collections[0].name : "this project"} />
    </>
  );
}

/* ------------------------------------------------------------------ */
/* Flagship content — Adelaide Airport                                 */
/* ------------------------------------------------------------------ */
const ADELAIDE_AIRPORT = {
  name: "Adelaide Airport",
  location: "South Australia, Australia",
  category: "Aviation",
  sector: "Aviation",
  year: "2025",
  architect: "Woods Bagot",
  photographer: "Florian Groehn",

  hero: "images/_legacy/projects/cairns-airport.webp",
  heroAlt: "Adelaide Airport gate lounge — Gateway seating by Derlot, 2025.",

  brief: [
    "Adelaide Airport briefed a gate lounge refresh that had to absorb rising passenger volumes inside an existing footprint. More seats per square metre. Integrated power at a meaningful proportion of positions. An inclusive specification — wheelchair zones integrated, not bolted on. And a visual language that read as considered architecture, not as transit furniture.",
    "\n",
  ],

  response: [
    "From day one the studio worked alongside Woods Bagot as co-designers rather than a product supplier. Our industrial team sketched alongside their interior leads, testing Gateway's modular logic against the gate cluster plan. Linear runs anchor the primary holding areas; rounded-end configurations soften the transition between gate and concourse; compact circular pods pick up the smaller satellite areas.",
    "Every seat position was specified against the passenger behaviour the airport wanted to enable. GPO and USB-C integration runs at every third module — roughly forty percent of all positions — chosen as the density that serves charging demand without cluttering the run with outlets. Wheelchair Zones are integrated at every gate cluster: clear access, open ends, seat heights tuned for easy transfer.",
    "The upholstery palette was tuned to Woods Bagot's material board across three earth-forward neutrals. A custom stitching detail was developed in collaboration between our workshop and their interior studio, visible in the seam lines running along the beam's top edge. Frames are powder-coated mild steel; every upholstered module is designed for on-site replacement without lifting the frame.",
    "Seats were manufactured in Brisbane and commissioned across a phased fit-out schedule that kept every gate live during install. The install team worked gate-by-gate through operational windows, with the design lead present for sign-off at each cluster handover.",
  ],

  quote: {
    text: "Derlot came to the table as co-designers. Not as a vendor trying to fit an existing product into our plan. Every decision that matters at Adelaide Airport happened in the room with their team.",
    name: "\n",
    role: "Woods Bagot · Interior Design Lead",
  },

  gallery: [
    { src: "assets/gateway/cairns-hero.webp",   alt: "Gate lounge with Gateway seating in three neutrals." },
    { src: "assets/gateway/cairns-02.webp",     alt: "Linear run of Gateway with integrated charging." },
    { src: "assets/gateway/dfw-linear.webp",    alt: "Wheelchair Zone integrated at a gate cluster." },
    { src: "images/_legacy/projects/cairns-airport-02.webp", alt: "Curved Gateway configuration in the main concourse." },
  ],

  featureA: {
    image: "assets/gateway/cairns-02.webp",
    alt: "Gateway Wheelchair Zone at an Adelaide Airport gate.",
    eyebrow: "Integrated, not added",
    title: "Wheelchair Zones specified at every cluster.",
    body: "Gateway demonstrates how inclusivity can be beautiful. Clear access zones with open ends, seat heights tuned for easy transfer, and sight lines that place every traveller — regardless of ability — at the centre of the lounge rather than the edge of it.",
  },

  singleImage: {
    src: "images/_legacy/projects/cairns-airport.webp",
    alt: "Wide view of an Adelaide Airport gate lounge.",
  },

  featureB: {
    image: "assets/gateway/dfw-linear.webp",
    alt: "Gateway with integrated power and USB-C at an armrest.",
    eyebrow: "Connectivity",
    title: "Power and USB-C integrated into the armrest.",
    body: "Modern travel is connected. Gateway integrates GPO and USB-C charging directly into the armrest shell, allowing passengers to recharge devices and themselves at roughly forty percent of positions across the terminal — the density the airport specified as the right balance of demand and visual calm.",
  },

  metrics: [
    { value: "2,400",  label: "Seats installed",    note: "Across 14 gate clusters." },
    { value: "1,860",  label: "Charging positions", note: "GPO + USB-C integrated into the armrest." },
    { value: "12,000", label: "Passengers / day",   note: "Mean terminal throughput, 2025." },
    { value: "100%",   label: "Made in Australia",  note: "Manufactured locally for the project." },
  ],

  outcome: [
    "The refreshed gate lounges opened in phases through 2025. The replaceable-module design is already earning its specification — individual seats in the first commissioned cluster have been refreshed without taking a gate out of service, proving out the long-cycle logic the airport invested in.",
    "Adelaide Airport was recognised at the 2025 Good Design Awards with the Gateway collection, acknowledged for the collaborative design process that made it possible.",
  ],

  collections: [
    { slug: "gateway", name: "Gateway", note: "Modular beam, 2022",    image: "images/_legacy/collections/gateway.webp" },
    { slug: "twig",    name: "Twig",    note: "Upholstered, 2020",      image: "images/_legacy/collections/twig.webp" },
  ],
};

const DEFAULT_RELATED = [
  { name: "Cairns Airport",            year: 2025, location: "Cairns, Australia",     image: "images/_legacy/projects/cairns-airport.webp" },
  { name: "DFW International Airport", year: 2024, location: "Dallas–Fort Worth, USA", image: "images/_legacy/projects/dfw-airport.webp" },
  { name: "Jubilee Place",             year: 2022, location: "Brisbane, Australia",    image: "images/_legacy/projects/jubilee-place.webp" },
];

window.ProjectDetail = ProjectDetail;
window.ADELAIDE_AIRPORT_PROJECT = ADELAIDE_AIRPORT;
