// HydroPulse — shared UI: logo, nav, footer, icons, utilities

const { useState, useEffect, useRef } = React;

// ------ Hash router ------
window.useHashRoute = function useHashRoute() {
  const [route, setRoute] = useState(() => window.location.hash.slice(1) || "/");
  useEffect(() => {
    const onHash = () => setRoute(window.location.hash.slice(1) || "/");
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);
  useEffect(() => {
    window.scrollTo({ top: 0, behavior: "instant" });
  }, [route]);
  return route;
};

window.navTo = function navTo(path) {
  window.location.hash = path;
};

// ------ Reveal observer ------
window.useReveal = function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll(".reveal");
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.add("in");
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
    );
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  });
};

// ------ Icons ------
window.Icon = function Icon({ name, size = 14, stroke = 1.5 }) {
  const s = size;
  const common = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (name) {
    case "arrow-right": return <svg {...common}><path d="M5 12h14M13 5l7 7-7 7"/></svg>;
    case "arrow-up-right": return <svg {...common}><path d="M7 17L17 7M8 7h9v9"/></svg>;
    case "arrow-down": return <svg {...common}><path d="M12 5v14M5 13l7 7 7-7"/></svg>;
    case "plus": return <svg {...common}><path d="M12 5v14M5 12h14"/></svg>;
    case "minus": return <svg {...common}><path d="M5 12h14"/></svg>;
    case "close": return <svg {...common}><path d="M6 6l12 12M18 6l-12 12"/></svg>;
    case "download": return <svg {...common}><path d="M12 3v14M5 11l7 7 7-7M5 21h14"/></svg>;
    case "play": return <svg {...common}><path d="M8 5l12 7-12 7z" fill="currentColor"/></svg>;
    case "menu": return <svg {...common}><path d="M4 7h16M4 12h16M4 17h16"/></svg>;
    case "search": return <svg {...common}><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.5-4.5"/></svg>;
    case "wave": return <svg {...common} viewBox="0 0 24 24"><path d="M2 12c2 0 2 -3 4 -3s2 3 4 3 2-3 4-3 2 3 4 3 2-3 4-3"/></svg>;
    case "dot": return <svg {...common} viewBox="0 0 24 24"><circle cx="12" cy="12" r="3" fill="currentColor" stroke="none"/></svg>;
    default: return null;
  }
};

// ------ Logo variant (root `tweaks.logoVariant`, synced in applyTweaks + data-logo-variant) ------
function readLogoVariantFromDom() {
  if (typeof document === "undefined") return "inline";
  const a = document.documentElement.getAttribute("data-logo-variant");
  if (a === "brand") return "brand";
  if (a === "inline") return "inline";
  if (typeof window !== "undefined" && window.HP_LOGO_USE_INLINE) return "inline";
  return "inline";
}

window.useLogoVariant = function useLogoVariant() {
  const [v, setV] = useState(readLogoVariantFromDom);
  useEffect(() => {
    const onTweaks = (e) => {
      if (e && e.detail && (e.detail.logoVariant === "brand" || e.detail.logoVariant === "inline")) {
        setV(e.detail.logoVariant);
        return;
      }
      setV(readLogoVariantFromDom());
    };
    window.addEventListener("hp-tweaks", onTweaks);
    return () => window.removeEventListener("hp-tweaks", onTweaks);
  }, []);
  return v;
};

// ------ Brand logos: assets/brand/HydroPulse_logo_*.svg (see EXPORT-INSTRUCTIONS.md) ------
// *Inverse* = light/white art for dark UI (full menu) — no paper chip. If missing, we fall back to the light SVG + chip.
// ?v bumped when the logo artwork is replaced on disk (URLs are otherwise cached).
const HP_LOGO = {
  noEu: "assets/brand/HydroPulse_logo_no_eu_text.svg?v=2",
  withEu: "assets/brand/HydroPulse_logo_eu_text.svg?v=2",
  // Same as light lockups, with light/white art on transparent — *required* for full menu to drop the white chip
  // no-eu dark export may be missing; EU dark has white art on transparent (safe on dark UI)
  noEuInverse: "assets/brand/HydroPulse_logo_dark_mode_eu_text.svg?v=2",
  withEuInverse: "assets/brand/HydroPulse_logo_dark_mode_eu_text.svg?v=2",
};

function LogoFallback({ theme = "default" }) {
  const isInverse = theme === "inverse";
  const color = isInverse ? "var(--paper)" : "var(--ink)";
  return (
    <a href="#/" className="hp-logo hp-logo--inline" style={{ display: "inline-flex", alignItems: "center", gap: 10, textDecoration: "none", color }}>
      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
        <path d="M2 14c2.5 0 2.5 -3 5 -3s2.5 3 5 3 2.5 -3 5 -3 2.5 3 5 3"/>
        <path d="M2 19c2.5 0 2.5 -3 5 -3s2.5 3 5 3 2.5 -3 5 -3 2.5 3 5 3" opacity="0.55"/>
        <circle cx="12" cy="6" r="1.6" fill={color} stroke="none"/>
      </svg>
      <span style={{ fontFamily: "var(--display)", fontSize: 20, letterSpacing: "-0.01em", fontWeight: 500 }}>HydroPulse</span>
    </a>
  );
}

/**
 * @param {object} props
 * @param {boolean} [props.brandOnDarkChrome] — Look 4 (and similar): on inverse/dark header or overlay, use the white brand lockup even when the design panel is set to “Preview” (inline). The strip is built for the actual dark-mode SVG.
 * @param {boolean} [props.noInkChip] — On dark bar / inverse: if the inverse SVG fails, use inline wordmark (light on dark) instead of the light lockup on a paper chip.
 */
window.Logo = function Logo({ theme = "default", variant = "noEu", noInkChip = false, brandOnDarkChrome = false }) {
  const panelVariant = window.useLogoVariant();
  const [forceInline, setForceInline] = useState(false);
  const isInverse = theme === "inverse";
  const [revertToLightOnDark, setRevertToLightOnDark] = useState(false);
  const alt = variant === "withEu" ? "HydroPulse, co-funded by the European Union" : "HydroPulse";

  const useBrandImages = (panelVariant === "brand" || (brandOnDarkChrome && isInverse)) && !forceInline;

  useEffect(() => {
    setForceInline(false);
  }, [panelVariant, isInverse, variant, noInkChip, brandOnDarkChrome]);

  useEffect(() => {
    setRevertToLightOnDark(false);
  }, [isInverse, variant, noInkChip]);

  if (!useBrandImages) return <LogoFallback theme={theme} />;

  let src;
  let onInkChip = false;
  if (isInverse && !revertToLightOnDark) {
    src = variant === "withEu" ? HP_LOGO.withEuInverse : HP_LOGO.noEuInverse;
  } else if (isInverse && revertToLightOnDark) {
    src = variant === "withEu" ? HP_LOGO.withEu : HP_LOGO.noEu;
    onInkChip = true;
  } else {
    src = variant === "withEu" ? HP_LOGO.withEu : HP_LOGO.noEu;
  }

  return (
    <a
      href="#/"
      className={"hp-logo" + (onInkChip ? " hp-logo--on-ink" : "") + (variant === "withEu" ? " hp-logo--with-eu" : "")}
      style={{ display: "inline-flex", alignItems: "center", textDecoration: "none", lineHeight: 0 }}
    >
      <img
        className="hp-logo__img"
        src={src}
        alt={alt}
        decoding="async"
        onError={() => {
          if (isInverse && !revertToLightOnDark) {
            if (noInkChip) {
              setForceInline(true);
            } else {
              setRevertToLightOnDark(true);
            }
            return;
          }
          setForceInline(true);
        }}
      />
    </a>
  );
};

// ------ data-look (L1–L4): syncs React with vanilla `applyTweaks` + design panel —------
function useDataLook() {
  const read = () => document.documentElement.getAttribute("data-look") || "1";
  const [look, setLook] = useState(read);
  useEffect(() => {
    const onTweaks = (e) => {
      const v = e && e.detail && e.detail.laf != null ? String(e.detail.laf) : read();
      setLook(v);
    };
    window.addEventListener("hp-tweaks", onTweaks);
    return () => window.removeEventListener("hp-tweaks", onTweaks);
  }, []);
  return look;
}

// ------ Nav ------
// First 6 show in the top bar; the rest live under "More". Dissemination kit is
// surfaced in the top bar (client ask, May 2026); Brand is folded into that kit
// (its featured block + /brand deep-dive), so it is intentionally not a nav item.
const NAV_ITEMS = [
  { path: "/about", label: "About" },
  { path: "/work-packages", label: "Work packages" },
  { path: "/sites", label: "Demonstration sites" },
  { path: "/consortium", label: "Consortium" },
  { path: "/news", label: "News" },
  { path: "/dissemination-kit", label: "Dissemination kit" },
  { path: "/resources", label: "Resources" },
  { path: "/advisory-board", label: "Advisory board" },
  { path: "/engage", label: "Get involved" },
];

window.Nav = function Nav({ route }) {
  const dataLook = useDataLook();
  const logoKey = window.useLogoVariant();
  const isL4 = dataLook === "4";
  const [scrolled, setScrolled] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 20);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  useEffect(() => { setMenuOpen(false); }, [route]);

  const headerClass =
    "hp-site-header" +
    (isL4 ? " hp-header--l4" : !isL4 && scrolled ? " hp-site-header--scrolled" : "");
  const linkInk = isL4 ? "var(--l4-header-fg)" : "var(--ink)";
  const linkMute = isL4 ? "var(--l4-header-muted)" : "var(--muted)";

  return (
    <>
      <header className={headerClass}>
        <div className="wrap hp-header__inner" style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "18px var(--gutter)" }}>
          <Logo
            key={logoKey}
            theme={isL4 ? "inverse" : "default"}
            variant={isL4 ? "withEu" : "noEu"}
            noInkChip={isL4}
            brandOnDarkChrome={isL4}
          />
          <nav style={{ display: "flex", alignItems: "center", gap: 28 }} className="hp-nav-desktop">
            {NAV_ITEMS.slice(0, 6).map((item) => {
              const active = route === item.path;
              return (
                <a key={item.path} href={"#" + item.path}
                  className={isL4 ? "hp-nav-link hp-nav-link--l4" + (active ? " hp-nav-link--active" : "") : ""}
                  style={isL4 ? {
                    textDecoration: "none",
                    position: "relative",
                    transition: "color var(--dur) var(--ease)",
                    color: active ? linkInk : linkMute,
                  } : {
                    fontSize: 13.5, textDecoration: "none",
                    color: active ? "var(--ink)" : "var(--muted)",
                    fontWeight: active ? 500 : 400,
                    position: "relative",
                    transition: "color var(--dur) var(--ease)",
                  }}
                  onMouseEnter={(e) => { e.currentTarget.style.color = linkInk; }}
                  onMouseLeave={(e) => { e.currentTarget.style.color = active ? linkInk : linkMute; }}
                >
                  {item.label}
                  {active && (
                    <span
                      className={isL4 ? "hp-nav-link__bar" : undefined}
                      style={isL4 ? { position: "absolute", left: 0, right: 0, bottom: -22, height: 2, background: "var(--logo-sky)" } : { position: "absolute", left: 0, right: 0, bottom: -22, height: 1, background: "var(--ink)" }}
                    />
                  )}
                </a>
              );
            })}
            <button type="button" onClick={() => setMenuOpen(true)}
              className={isL4 ? "hp-nav-more--l4" : ""}
              style={isL4 ? {
                background: "transparent",
                border: "1px solid var(--l4-header-rule)",
                padding: "7px 12px",
                cursor: "pointer",
                display: "inline-flex",
                alignItems: "center",
                gap: 8,
                color: "var(--l4-header-fg)",
                fontFamily: "var(--mono)",
                fontSize: 10.5,
                letterSpacing: "0.1em",
                textTransform: "uppercase",
              } : {
                background: "transparent", border: "1px solid var(--rule-strong)", padding: "7px 12px", fontFamily: "var(--sans)", fontSize: 12, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 8, color: "var(--ink)",
              }}>
              <Icon name="menu" size={12}/> More
            </button>
          </nav>
          <button type="button" onClick={() => setMenuOpen(true)} aria-label="Open menu" className={"hp-nav-mobile" + (isL4 ? " hp-nav-more--l4" : "")}
            style={isL4 ? {
              background: "transparent", border: "1px solid var(--l4-header-rule)", padding: "9px 12px", cursor: "pointer", display: "none", alignItems: "center", gap: 8, color: "var(--l4-header-fg)",
            } : {
              background: "transparent", border: "1px solid var(--rule-strong)", padding: "9px 12px", cursor: "pointer", display: "none", alignItems: "center", gap: 8, color: "var(--ink)",
            }}>
            <Icon name="menu" size={14}/>
          </button>
        </div>
      </header>

      {menuOpen && <FullMenu onClose={() => setMenuOpen(false)} route={route} dataLook={dataLook} />}

      <style>{`
        @media (max-width: 900px) {
          .hp-nav-desktop { display: none !important; }
          .hp-nav-mobile { display: inline-flex !important; }
        }
      `}</style>
    </>
  );
};

function FullMenu({ onClose, route, dataLook = "1" }) {
  const logoKey = window.useLogoVariant();
  const l4 = dataLook === "4";
  useEffect(() => {
    const onKey = (e) => e.key === "Escape" && onClose();
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [onClose]);

  const line = l4 ? "var(--l4-overlay-line)" : "color-mix(in srgb, var(--paper) 16%, transparent)";
  const sub = l4 ? "var(--l4-header-muted)" : "color-mix(in srgb, var(--paper) 55%, transparent)";

  if (l4) {
    return (
      <div className="hp-fullmenu hp-fullmenu--l4" role="dialog" aria-modal="true" aria-label="Main menu">
        <div className="wrap hp-fullmenu__closebar" style={{ padding: "22px var(--gutter)", display: "flex", justifyContent: "space-between", alignItems: "center", borderBottom: "1px solid var(--l4-overlay-line)" }}>
          <Logo key={logoKey} theme="inverse" variant="withEu" noInkChip brandOnDarkChrome />
          <button type="button" className="hp-fullmenu__close" onClick={onClose} style={{ background: "transparent", border: "1px solid var(--l4-overlay-line)", color: "var(--l4-header-fg)", padding: "10px 14px", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 10, fontFamily: "var(--sans)", fontSize: 12 }}>
            Close <Icon name="close" size={12} />
          </button>
        </div>
        <div className="wrap" style={{ padding: "clamp(28px, 5vw, 64px) var(--gutter) 80px", maxWidth: 720 }}>
          <div className="eyebrow hp-fullmenu__note" style={{ marginBottom: 20 }}>Menu</div>
          <ul style={{ listStyle: "none", padding: 0, margin: 0 }}>
            {[...NAV_ITEMS, { path: "/contact", label: "Contact" }].map((item, i) => (
              <li key={item.path} className="hp-fullmenu__row" style={{ borderTop: i === 0 ? "1px solid " + "var(--l4-overlay-line)" : "none", borderBottom: "1px solid var(--l4-overlay-line)" }}>
                <a className="hp-fullmenu__link" href={"#" + item.path}
                  onClick={onClose}
                  style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "18px 2px", textDecoration: "none", fontFamily: "var(--display)", fontWeight: 400, opacity: route === item.path ? 1 : 0.75 }}>
                  <span><span style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.12em", marginRight: 20, color: "var(--l4-header-muted)" }}>{String(i + 1).padStart(2, "0")}</span>{item.label}</span>
                  <Icon name="arrow-up-right" size={16} stroke={1.2} />
                </a>
              </li>
            ))}
          </ul>
          <p className="hp-fullmenu__lede" style={{ fontFamily: "var(--display)", fontSize: 20, lineHeight: 1.5, fontWeight: 300, maxWidth: "52ch", margin: "48px 0 0" }}>
            A five-year European research programme redesigning hydropower so that clean energy and living rivers are no longer a trade-off, but a joint design problem.
          </p>
        </div>
      </div>
    );
  }

  return (
    <div className="hp-fullmenu" role="dialog" aria-modal="true" aria-label="Main menu">
      <div className="wrap" style={{ padding: "22px var(--gutter)", display: "flex", justifyContent: "space-between", alignItems: "center", borderBottom: "1px solid " + line }}>
        <Logo key={logoKey} theme="inverse" variant="withEu" />
        <button type="button" onClick={onClose} style={{ background: "transparent", border: "1px solid " + "color-mix(in srgb, var(--paper) 24%, transparent)", color: "var(--paper)", padding: "10px 14px", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 10, fontFamily: "var(--sans)", fontSize: 12 }}>
          Close <Icon name="close" size={12} />
        </button>
      </div>
      <div className="wrap hp-fullmenu__grid" style={{ padding: "clamp(32px, 7vw, 96px) var(--gutter)", display: "grid", gridTemplateColumns: "1fr 1fr", gap: "clamp(32px, 6vw, 80px)" }}>
        <div>
          <div className="eyebrow" style={{ color: sub, marginBottom: 28 }}>Navigate</div>
          <ul style={{ listStyle: "none", padding: 0, margin: 0 }}>
            {[...NAV_ITEMS, { path: "/contact", label: "Contact" }].map((item, i) => (
              <li key={item.path} style={{ borderTop: i === 0 ? "1px solid " + line : "none", borderBottom: "1px solid " + line }}>
                <a href={"#" + item.path}
                  onClick={onClose}
                  style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "22px 4px", color: "var(--paper)", textDecoration: "none", fontFamily: "var(--display)", fontSize: "clamp(28px, 3.4vw, 44px)", letterSpacing: "-0.02em", fontWeight: 300, opacity: route === item.path ? 1 : 0.8 }}>
                  <span><span style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.12em", marginRight: 22, opacity: 0.5 }}>{String(i + 1).padStart(2, "0")}</span>{item.label}</span>
                  <Icon name="arrow-up-right" size={18} stroke={1.2} />
                </a>
              </li>
            ))}
          </ul>
        </div>
        <div style={{ alignSelf: "end" }}>
          <div className="eyebrow" style={{ color: sub, marginBottom: 20 }}>HydroPulse — a note</div>
          <p style={{ fontFamily: "var(--display)", fontSize: 22, lineHeight: 1.45, fontWeight: 300, color: "var(--paper)", maxWidth: 460, margin: 0, opacity: 0.88 }}>
            A five-year European research programme redesigning hydropower so that clean energy and living rivers are no longer a trade-off, but a joint design problem.
          </p>
          <div style={{ marginTop: 40, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24, fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.06em", color: "color-mix(in srgb, var(--paper) 70%, transparent)" }}>
            <div><div style={{ opacity: 0.55, marginBottom: 4 }}>DURATION</div>2026 — 2030</div>
            <div><div style={{ opacity: 0.55, marginBottom: 4 }}>FUNDING</div>Horizon Europe</div>
            <div><div style={{ opacity: 0.55, marginBottom: 4 }}>PARTNERS</div>{HP_DATA.project.partnerCount} across {HP_DATA.project.countries} countries</div>
            <div><div style={{ opacity: 0.55, marginBottom: 4 }}>DEMO SITES</div>{HP_DATA.project.demoSites} across Europe</div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ------ Footer ------
const FOOTER_COLS = [
  { h: "Project", links: [["About", "/about"], ["Work packages", "/work-packages"], ["Demonstration sites", "/sites"], ["Consortium", "/consortium"], ["Advisory board", "/advisory-board"]] },
  { h: "Public outputs", links: [["News", "/news"], ["Resources", "/resources"], ["Dissemination kit", "/dissemination-kit"], ["Get involved", "/engage"]] },
  { h: "Contact", links: [["info@hydropulse.eu", "/contact"], ["Press & media", "/contact"], ["Partner login", "/contact"], ["Newsletter", "/contact"]] },
];

function Footer() {
  const dataLook = useDataLook();
  const logoKey = window.useLogoVariant();
  const l4 = dataLook === "4";
  const onInk = "color-mix(in srgb, var(--paper) 88%, transparent)";
  const onInk2 = "color-mix(in srgb, var(--paper) 56%, transparent)";
  const onInk3 = "color-mix(in srgb, var(--paper) 50%, transparent)";
  const line = "color-mix(in srgb, var(--paper) 16%, transparent)";

  if (l4) {
    return (
      <footer className="hp-footer--light" style={{ marginTop: 120 }}>
        <div className="wrap" style={{ padding: "clamp(56px, 7vw, 100px) var(--gutter) 48px" }}>
          <div className="hp-footer__head hp-l4-foot-grid">
            <div>
              <Logo key={logoKey} theme="default" variant="withEu" />
              <p className="body" style={{ color: "var(--l4-footer-muted)", maxWidth: 360, marginTop: 20, fontSize: 14, lineHeight: 1.55 }}>
                A European research consortium advancing ecosystem-based hydropower. Coordinated by NTUA under Horizon Europe, 2026 — 2030.
              </p>
            </div>
            {FOOTER_COLS.map((col) => (
              <div key={col.h}>
                <div className="eyebrow hp-footer__eyebrow" style={{ marginBottom: 16 }}>{col.h}</div>
                <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 10 }}>
                  {col.links.map(([l, p]) => (
                    <li key={l}><a href={"#" + p} style={{ color: "var(--l4-footer-ink)", textDecoration: "none", fontSize: 13.5, fontWeight: 400 }}>{l}</a></li>
                  ))}
                </ul>
              </div>
            ))}
          </div>
          <div
            className="hp-footer__funding"
            style={{ marginTop: 40, display: "inline-flex", alignItems: "center", gap: 14, padding: "10px 14px", border: "1px solid var(--l4-footer-rule)" }}
          >
            <svg width="24" height="18" viewBox="0 0 24 18"><rect width="24" height="18" fill="#003399" />{[...Array(12)].map((_, i) => {
              const a = (i / 12) * Math.PI * 2 - Math.PI / 2;
              const x = 12 + Math.cos(a) * 5.5;
              const y = 9 + Math.sin(a) * 5.5;
              return <circle key={i} cx={x} cy={y} r="0.8" fill="#FFCC00" />;
            })}</svg>
            <div>
              <div style={{ fontFamily: "var(--mono)", fontSize: 9.5, letterSpacing: "0.12em", color: "var(--l4-footer-muted)", textTransform: "uppercase" }}>Funded by</div>
              <div style={{ fontSize: 12, marginTop: 2, color: "var(--l4-footer-ink)" }}>European Union · Horizon Europe</div>
            </div>
          </div>
          <div className="hp-footer__rule" style={{ marginTop: 48, paddingTop: 24, borderTop: "1px solid var(--l4-footer-rule)", display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 16 }}>
            <div className="caption" style={{ color: "var(--l4-footer-muted)" }}>
              © 2026 HydroPulse Consortium · Grant Agreement No. 101198xxx · Site content reflects authors' views only
            </div>
            <div style={{ display: "flex", gap: 20, fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--l4-footer-muted)" }}>
              <a href="#/legal" style={{ color: "inherit", textDecoration: "none" }}>Legal</a>
              <a href="#/legal" style={{ color: "inherit", textDecoration: "none" }}>Privacy</a>
              <a href="#/legal" style={{ color: "inherit", textDecoration: "none" }}>Accessibility</a>
            </div>
          </div>
        </div>
      </footer>
    );
  }

  return (
    <footer style={{ background: "var(--ink)", color: "var(--paper)", marginTop: 120 }}>
      <div className="wrap" style={{ padding: "clamp(60px, 8vw, 120px) var(--gutter) 40px" }}>
        <div className="hp-footer__grid" style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr 1fr 1fr", gap: "clamp(24px, 4vw, 60px)", alignItems: "start" }}>
          <div>
            <Logo key={logoKey} theme="inverse" variant="withEu" />
            <p className="body" style={{ color: onInk, maxWidth: 360, marginTop: 24, fontSize: 14 }}>
              A European research consortium advancing ecosystem-based hydropower. Coordinated by NTUA under Horizon Europe, 2026 — 2030.
            </p>
            <div style={{ marginTop: 32, display: "inline-flex", alignItems: "center", gap: 14, padding: "10px 14px", border: "1px solid " + "color-mix(in srgb, var(--paper) 22%, transparent)" }}>
              <svg width="24" height="18" viewBox="0 0 24 18"><rect width="24" height="18" fill="#003399" />{[...Array(12)].map((_, i) => {
                const a = (i / 12) * Math.PI * 2 - Math.PI / 2;
                const x = 12 + Math.cos(a) * 5.5;
                const y = 9 + Math.sin(a) * 5.5;
                return <circle key={i} cx={x} cy={y} r="0.8" fill="#FFCC00" />;
              })}</svg>
              <div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 9.5, letterSpacing: "0.12em", color: onInk2, textTransform: "uppercase" }}>Funded by</div>
                <div style={{ fontSize: 12, marginTop: 2 }}>European Union · Horizon Europe</div>
              </div>
            </div>
          </div>
          {FOOTER_COLS.map((col) => (
            <div key={col.h}>
              <div className="eyebrow" style={{ color: onInk2, marginBottom: 18 }}>{col.h}</div>
              <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 11 }}>
                {col.links.map(([l, p]) => (
                  <li key={l}><a href={"#" + p} style={{ color: onInk, textDecoration: "none", fontSize: 14 }}>{l}</a></li>
                ))}
              </ul>
            </div>
          ))}
        </div>
        <div style={{ marginTop: 80, paddingTop: 28, borderTop: "1px solid " + line, display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 18 }}>
          <div className="caption" style={{ color: onInk3 }}>
            © 2026 HydroPulse Consortium · Grant Agreement No. 101198xxx · Site content reflects authors' views only
          </div>
          <div style={{ display: "flex", gap: 22, fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.08em", textTransform: "uppercase", color: onInk3 }}>
            <a href="#/legal" style={{ color: "inherit", textDecoration: "none" }}>Legal notice</a>
            <a href="#/legal" style={{ color: "inherit", textDecoration: "none" }}>Privacy</a>
            <a href="#/legal" style={{ color: "inherit", textDecoration: "none" }}>Accessibility</a>
          </div>
        </div>
      </div>
    </footer>
  );
}

window.Footer = Footer;

// ------ Image with placeholder ------
window.PImage = function PImage({ src, alt = "Placeholder image", caption, ratio = "16 / 9", label = "PLACEHOLDER IMAGE", duotone = true, style = {}, children }) {
  const [loaded, setLoaded] = useState(false);
  return (
    <figure style={{ margin: 0, position: "relative", ...style }}>
      <div className={duotone ? "img-duotone" : ""} style={{ aspectRatio: ratio, background: "var(--ink)", position: "relative" }}>
        <img src={src} alt={alt} onLoad={() => setLoaded(true)}
          style={{ opacity: loaded ? (duotone ? 0.86 : 1) : 0, transition: "opacity 700ms var(--ease)" }}/>
        {label && <div className="img-placeholder-label">{label}</div>}
        {children}
      </div>
      {caption && <figcaption className="caption" style={{ marginTop: 10, color: "var(--muted)" }}>{caption}</figcaption>}
    </figure>
  );
};

// ------ Brand motifs & patterns ------
// Ported verbatim from the brand pack (hydropulse-brand-new/lib/hp-brand.jsx) so the
// native /brand page renders the same five reusable motifs + background patterns the
// designer system defines. Colours are passed as explicit hexes (the site does not
// define the brand pack's --hp-* custom properties), and the hero motif is fixed to the
// rotor direction — no motif switcher is needed here.

// Real vector paths lifted from the logo artwork (HydroPulse-02-01.svg, 1200×1200 space).
const HP_DOLPHIN_D = "M457.77,586.52s-21.62,22.34-10.09,51.17c0,0-13.69,10.81-5.77,32.43,0,0,5.77-5.04,15.13-7.21,0,0,24.5,60.54,85.76,57.65,0,0,12.25,17.3,36.75,12.97,0,0-4.32-10.81-15.85-17.3,0,0,10.09-4.32,18.74-28.11,0,0-30.27,4.32-39.64,22.34,0,0-45.4-5.77-54.77-60.54,0,0,12.25,1.44,17.3,12.25,0,0,5.04-26.66-20.9-30.27,0,0,0-28.83-19.46-44.68v13.69s-7.21-7.93-7.21-14.41Z";
const HP_LEAF_D = "M679.01,590.13s39.64,62.7-9.37,100.89l-8.65,9.37,18.74-3.6s19.46-27.39,44.68-7.21c0,0-18.74,33.87-46.12,10.81l-33.15,15.85h9.37s23.78-12.25,33.15,12.25c0,0-21.62,15.13-34.59-7.93l-12.25-1.44s-32.21,19.99-64.75,8.91l-1.55-2.43s31.71,4.32,46.12-11.53c0,0-7.21-20.9,18.74-32.43,0,0,5.77,20.18-15.85,36.75,0,0,24.5-5.04,42.52-30.99,0,0-11.53-28.11-2.16-51.89l8.65-21.62s7.21-18.74,6.49-23.78Z";
// Swept three-blade hydro-turbine impeller — odd blade count reads as rotation, not a flower.
const HP_ROTOR_BLADE = "M -3.5 -15 C 3 -27, 14 -36, 26 -37 C 24 -27, 17 -19, 6.5 -15 C 3 -13.5, -0.5 -13.5, -3.5 -15 Z";

window.HPRotor = function HPRotor({ size = 120, color = "#000547", accent = "#F1F6FB", stroke = 0, style = {} }) {
  return (
    <svg width={size} height={size} viewBox="-50 -50 100 100" style={{ display: "block", ...style }} aria-hidden>
      {stroke ? <circle r="47" fill="none" stroke={color} strokeWidth={stroke} opacity="0.26"/> : null}
      {[0, 120, 240].map((r, i) => <path key={i} transform={`rotate(${r})`} d={HP_ROTOR_BLADE} fill={color}/>)}
      <circle r="11" fill={color}/>
      <circle r="6" fill={accent}/>
      <circle r="2.4" fill={color}/>
    </svg>
  );
};

// Three community figures arching above the wheel — partner / participation badge.
window.HPCommunityTrio = function HPCommunityTrio({ size = 120, color = "#25B8F0", style = {} }) {
  return (
    <svg width={size} height={size * 0.5} viewBox="0 0 100 50" style={{ display: "block", ...style }} aria-hidden>
      <g fill={color}>
        <circle cx="20" cy="14" r="9"/>
        <path d="M 5 44 C 8 32, 14 28, 20 28 C 26 28, 32 32, 35 44 Z"/>
        <circle cx="50" cy="10" r="9"/>
        <path d="M 35 40 C 38 28, 44 24, 50 24 C 56 24, 62 28, 65 40 Z"/>
        <circle cx="80" cy="14" r="9"/>
        <path d="M 65 44 C 68 32, 74 28, 80 28 C 86 28, 92 32, 95 44 Z"/>
      </g>
    </svg>
  );
};

// Fish + leaf wreath — the ecology band, real dolphin + leaf paths from the mark.
window.HPFishLeaf = function HPFishLeaf({ size = 180, fishColor = "#25B8F0", leafColor = "#3DA17A", style = {} }) {
  return (
    <svg width={size} height={size * (192 / 312)} viewBox="425 562 312 192" style={{ display: "block", overflow: "visible", ...style }} aria-hidden>
      <path d={HP_DOLPHIN_D} fill={fishColor}/>
      <ellipse cx="458.65" cy="613.13" rx="3.05" ry="4.32" transform="rotate(-26.64 458.65 613.13)" fill="#fff"/>
      <path d={HP_LEAF_D} fill={leafColor}/>
    </svg>
  );
};

// Pulse waveform — a confident horizontal line that doubles as a section accent.
window.HPPulseLine = function HPPulseLine({ width = 200, height = 36, color = "#0078B6", stroke = 3, style = {} }) {
  return (
    <svg width={width} height={height} viewBox="0 0 240 28" style={{ display: "block", ...style }} aria-hidden>
      <path d="M 0 14 H 60 L 75 4 L 90 24 L 105 14 H 240"
            stroke={color} strokeWidth={stroke} fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
};

// EU 12-star constellation — the canonical 12 dots on a circle (also: partners orbiting the mission).
window.HPConstellation = function HPConstellation({ size = 120, color = "#000547", dotR = 28, count = 12, style = {} }) {
  const dots = Array.from({ length: count }, (_, i) => {
    const a = (i / count) * Math.PI * 2 - Math.PI / 2;
    return [Math.cos(a) * 40, Math.sin(a) * 40];
  });
  return (
    <svg width={size} height={size} viewBox="-50 -50 100 100" style={{ display: "block", ...style }} aria-hidden>
      <circle cx="0" cy="0" r="40" fill="none" stroke={color} strokeWidth="0.6" opacity="0.3"/>
      {dots.map(([x, y], i) => <circle key={i} cx={x} cy={y} r={dotR / 10} fill={color}/>)}
    </svg>
  );
};

// Background-pattern data: URIs (survive copy/paste). Hero motif = rotor.
window.HP_turbinePattern = function HP_turbinePattern(color = "#000547", opacity = 0.06, size = 160) {
  const body = `<g fill='${color}' opacity='${opacity}' transform='translate(${size / 2} ${size / 2}) scale(${size / 110})'>` +
    [0, 120, 240].map((r) => `<path transform='rotate(${r})' d='${HP_ROTOR_BLADE}'/>`).join("") +
    `<circle r='9'/></g>`;
  const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${size}' height='${size}' viewBox='0 0 ${size} ${size}'>${body}</svg>`;
  return `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}")`;
};

window.HP_dotPattern = function HP_dotPattern(color = "#000547", opacity = 0.22, spacing = 18, dotR = 1.2) {
  const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${spacing}' height='${spacing}'>` +
    `<circle cx='${spacing / 2}' cy='${spacing / 2}' r='${dotR}' fill='${color}' fill-opacity='${opacity}'/></svg>`;
  return `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}")`;
};

// Export to window for cross-file access
Object.assign(window, { NAV_ITEMS });
