const { useState, useEffect, useRef } = React;

/* ---------- Tweak defaults ---------- */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": "subway"
}/*EDITMODE-END*/;

/* ---------- Reveal hook ---------- */
function useReveal(deps) {
  useEffect(() => {
    const els = document.querySelectorAll(".reveal:not(.in)");
    if (!els.length) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add("in");
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.10, rootMargin: "0px 0px -6% 0px" });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, deps);
}

/* ---------- Active section ---------- */
function useActiveSection(ids) {
  const [active, setActive] = useState("");
  useEffect(() => {
    const sections = ids.map((id) => document.getElementById(id)).filter(Boolean);
    const io = new IntersectionObserver((entries) => {
      const visible = entries
        .filter((e) => e.isIntersecting)
        .sort((a, b) => b.intersectionRatio - a.intersectionRatio);
      if (visible[0]) setActive(visible[0].target.id);
    }, { rootMargin: "-35% 0px -55% 0px", threshold: [0, 0.25, 0.5, 0.75, 1] });
    sections.forEach((s) => io.observe(s));
    return () => io.disconnect();
  }, [ids.join(",")]);
  return active;
}

/* ---------- Date formatting ---------- */
const TODAY = new Date(2026, 4, 24); // May 24, 2026
const fmtIso = (d) =>
  `${d.getFullYear()}.${String(d.getMonth() + 1).padStart(2, "0")}.${String(d.getDate()).padStart(2, "0")}`;

const CALENDLY = "https://calendly.com/elise-hasseltine/elise-hasseltine-public-translation-media";

/* ---------- Top Nav ---------- */
function scrollToId(id) {
  const el = document.getElementById(id);
  if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
  history.replaceState(null, "", location.pathname + location.search);
}

function TopNav({ active, onNavClick }) {
  const links = [
    { id: "practice", label: "Practice", num: "01" },
    { id: "focus",    label: "Focus",    num: "02" },
    { id: "contact",  label: "Contact",  num: "03" },
  ];
  const handleClick = (e, id) => {
    e.preventDefault();
    if (onNavClick) onNavClick(id);
    scrollToId(id);
  };
  return (
    <React.Fragment>
      <div className="top-rule"></div>
      <nav className="top">
        <div className="row">
          <div className="left">
            <a href="#top" style={{ letterSpacing: "0.22em" }} onClick={(e) => handleClick(e, "top")}>Elise Hasseltine</a>
          </div>
          <div className="center">
            COMMUNICATIONS PRACTICE / EST. 2026
          </div>
          <div className="right">
            {links.map((l) => (
              <a key={l.id} href={"#" + l.id} className={active === l.id ? "active" : ""} onClick={(e) => handleClick(e, l.id)}>
                <span className="num">{l.num}</span>{l.label}
              </a>
            ))}
            <a className="nav-cta" href={CALENDLY} target="_blank" rel="noreferrer noopener">Book a Call</a>
          </div>
        </div>
      </nav>
    </React.Fragment>
  );
}

/* ---------- Hero ---------- */
function Hero() {
  return (
    <section className="hero" id="top">
      <div className="hero-stage">
        <div>
          <h1 className="hello reveal">hello<span className="dot">.</span></h1>
          <p className="hero-tag reveal d1">
            A communications practice for <b>mission driven institutions</b> whose research, policy, and legal work deserves to reach <span className="hilite">further than the press release.</span>
          </p>
        </div>

        <div className="hero-aside reveal d2">
          <div className="stamp">Now Booking 2026</div>
          <div className="signature">
            <span className="label">Proprietor</span>
            <div className="name">Elise Hasseltine</div>
            <div className="where">Editorial strategy, narrative development,<br/>and platform native content.</div>
          </div>
        </div>
      </div>

      <div className="hero-foot">
        <span className="l">A three part program <span className="arrow"></span> Practice / Focus / Contact</span>
        <span className="r">Scroll to begin</span>
      </div>
    </section>
  );
}

/* ---------- Section banner ---------- */
function SectionBanner({ id, num, numStyle, title, titleAccent, subKey, subVal, open, onToggle }) {
  const collapsible = typeof onToggle === "function";
  const cls = "section-banner" + (collapsible ? " collapsible" : "") + (open ? " open" : "");
  const inner = (
    <React.Fragment>
      <div className={"num " + (numStyle || "")}>{num}</div>
      <div className="ttl">{title}{titleAccent && (
        titleAccent.startsWith(" ")
          ? <React.Fragment>{"\u00a0"}<em>{titleAccent.trimStart()}</em></React.Fragment>
          : <em>{titleAccent}</em>
      )}</div>
      <div className="sub">
        <span className="label">{subKey}</span>
        <span className="val">{subVal}</span>
      </div>
      {collapsible && <span className="banner-caret" aria-hidden="true">{"\u25be"}</span>}
    </React.Fragment>
  );
  if (collapsible) {
    return (
      <button type="button" className={cls} id={id} onClick={onToggle} aria-expanded={!!open}>
        {inner}
      </button>
    );
  }
  return <div className={cls} id={id}>{inner}</div>;
}

/* ---------- Practice ---------- */
function Practice({ open, onToggle }) {
  return (
    <section className="body">
      <SectionBanner
        id="practice"
        num="01"
        numStyle="red"
        title="The Practice"
        titleAccent="."
        subKey="Filed Under"
        subVal="Editorial / Translation"
        open={open}
        onToggle={onToggle}
      />
      {open && <div className="section-content">
        <div className="col">
          <span className="kicker reveal">A Statement</span>
          <div className="pull reveal d1">
            I help mission driven nonprofits translate sophisticated, fact based work into the formats where the public <span className="red">forms opinions.</span>
          </div>
        </div>
        <div className="col">
          <p className="reveal">
            <span className="lead">I work at the intersection of expert institutions and the public platforms most people now live on.</span>
          </p>
          <p className="reveal d1">
            A lot of the most important work being done by nonpartisan, fact based organizations is failing to reach the public. Not because the work is weak, but because the communications model it was built on (top down, gatekeeper validated, press release driven) no longer maps to how trust and attention are formed online. Only 38 percent of Americans now claim to follow news regularly, down from 51 percent a decade ago. Only 17 percent pay for it. The audience the press release was built to reach is no longer there.
          </p>
          <p className="reveal d2">
            I help organizations close that gap. In practice, that looks like <b>editorial strategy, narrative development, and platform native content</b> that takes rigorous research and policy work and renders it in the formats the public reads, watches, and shares, without losing the rigor.
          </p>
        </div>
      </div>}
    </section>
  );
}

/* ---------- Focus ---------- */
const FOCUS_ITEMS = [
  {
    color: "red",
    num: "01",
    kicker: "Field 01",
    title: "Information\nInfrastructure",
    body: "The systems by which credible knowledge reaches the people who need it. Search, encyclopedias, libraries, public broadcasters, fact checking, archival projects, and the standards that hold them together. Weekly use of generative AI nearly doubled in the last year, from 18 to 34 percent. The layer through which people find out what is true is being rebuilt in real time.",
    tag: "FLD / 01",
  },
  {
    color: "yellow",
    num: "02",
    kicker: "Field 02",
    title: "AI\nGovernance",
    body: "Independent research, policy, and civil society work on how powerful systems are built, deployed, and held to account. Translating that work for the audiences and platforms shaping the public's understanding of it.",
    tag: "FLD / 02",
  },
  {
    color: "blue",
    num: "03",
    kicker: "Field 03",
    title: "Civic\nInstitutions",
    body: "Courts, legal aid networks, voting and elections work, nonpartisan policy shops, and the small set of organizations still trying to make government legible to the public it serves.",
    tag: "FLD / 03",
  },
  {
    color: "black",
    num: "04",
    kicker: "Field 04",
    title: "The Public\nSphere",
    body: "The long term health of the spaces where citizens form opinions together. Local journalism, civic media, deliberative platforms, and the slow, unglamorous work of rebuilding shared reference points.",
    tag: "FLD / 04",
  },
];

function FocusGeom({ kind }) {
  // Vignelli-ish geometric flourish, low opacity background
  if (kind === "red") return (
    <svg className="geom" viewBox="0 0 200 200" fill="none">
      <circle cx="100" cy="100" r="92" stroke="currentColor" strokeWidth="6"/>
      <circle cx="100" cy="100" r="50" fill="currentColor"/>
    </svg>
  );
  if (kind === "yellow") return (
    <svg className="geom" viewBox="0 0 200 200" fill="none">
      <rect x="8" y="8" width="184" height="184" stroke="currentColor" strokeWidth="6"/>
      <path d="M8 8 L192 192 M192 8 L8 192" stroke="currentColor" strokeWidth="6"/>
    </svg>
  );
  if (kind === "blue") return (
    <svg className="geom" viewBox="0 0 200 200" fill="none">
      <polygon points="100,12 188,188 12,188" stroke="currentColor" strokeWidth="6"/>
      <circle cx="100" cy="130" r="30" fill="currentColor"/>
    </svg>
  );
  return (
    <svg className="geom" viewBox="0 0 200 200" fill="none">
      <circle cx="60" cy="100" r="48" fill="currentColor"/>
      <circle cx="140" cy="100" r="48" stroke="currentColor" strokeWidth="6"/>
    </svg>
  );
}

function Focus({ open, onToggle }) {
  const [focusOpen, setFocusOpen] = useState(0);
  return (
    <section className="body">
      <SectionBanner
        id="focus"
        num="02"
        numStyle="yellow"
        title="Current Focus"
        titleAccent=""
        subKey="Engaged With"
        subVal="A Small Number"
        open={open}
        onToggle={onToggle}
      />
      {open && <div className="focus-grid">
        {FOCUS_ITEMS.map((it, i) => {
          const isOpen = focusOpen === i;
          return (
            <div
              key={it.num}
              className={"focus-cell " + it.color + (isOpen ? " open" : "")}
              onClick={() => setFocusOpen(isOpen ? -1 : i)}
              role="button"
              tabIndex={0}
              onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setFocusOpen(isOpen ? -1 : i); } }}
            >
              <span className="ftag">{it.tag} {isOpen ? "/ OPEN" : "/ TAP"}</span>
              <FocusGeom kind={it.color} />
              <div>
                <div className="fnum">{it.num}</div>
              </div>
              <div>
                <div className="fkicker">{it.kicker}</div>
                <div className="ftitle">{it.title.split("\n").map((s, j) => <div key={j}>{s}</div>)}</div>
                <div className="fbody">{it.body}</div>
              </div>
            </div>
          );
        })}
      </div>}
    </section>
  );
}

/* ---------- Contact ---------- */
function Contact({ open, onToggle }) {
  const calendly  = "https://calendly.com/elise-hasseltine/elise-hasseltine-public-translation-media";
  const linkedin  = "linkedin.com/in/elisehasseltine";
  const instagram = "instagram.com/elisehasseltine";
  return (
    <section className="body">
      <button type="button" className={"section-banner collapsible" + (open ? " open" : "")} id="contact" onClick={onToggle} aria-expanded={!!open}>
        <div className="num blue">03</div>
        <div className="ttl">Correspond<em>.</em></div>
        <div className="sub">
          <span className="label">Status</span>
          <span className="val">Open to Inquiries</span>
        </div>
        <span className="banner-caret" aria-hidden="true">▾</span>
      </button>
      {open && <div className="contact-wrap">
        <div className="contact-lhs">
          <h2 className="reveal">hello<span className="dot">.</span></h2>
          <div className="lede reveal d1">
            If your organization is doing rigorous, public interest work and the translation layer is where things are <b>breaking down</b>, I would like to hear from you. Initial conversations are unstructured and free.
          </div>
          <a className="copy-btn" href={calendly} target="_blank" rel="noreferrer noopener">
            Schedule a Call
          </a>
        </div>

        <div className="ctk reveal d2">
          <div className="ctk-head">
            <div className="ttl">Admit <span className="red">One</span></div>
            <div className="no">No. 001 / 064</div>
            <div className="right">Communications<br/>Day Pass</div>
          </div>
          <div className="ctk-strip">
            <div><span className="k">Issued</span>{fmtIso(TODAY)}</div>
            <div><span className="k">Valid</span>Through 2026</div>
            <div><span className="k">Class</span>All Inquiries</div>
          </div>
          <div className="ctk-rows">
            <a href={calendly} target="_blank" rel="noreferrer noopener">
              <span className="k">Schedule</span>
              <span className="v">calendly.com/elise-hasseltine</span>
              <span className="arr">Book →</span>
            </a>
            <a href={"https://www." + linkedin} target="_blank" rel="noreferrer noopener">
              <span className="k">LinkedIn</span>
              <span className="v">/in/elisehasseltine</span>
              <span className="arr">Open →</span>
            </a>
            <a href={"https://www." + instagram} target="_blank" rel="noreferrer noopener">
              <span className="k">Instagram</span>
              <span className="v">@elisehasseltine</span>
              <span className="arr">Open →</span>
            </a>
          </div>
          <div className="ctk-foot">
            <span>E.H. / 2026 / SD</span>
            <span className="barcode" aria-hidden="true">
              <i></i><i className="s"></i><i className="w"></i><i></i><i className="s"></i><i></i><i className="w"></i><i></i><i></i><i className="s"></i><i className="w"></i><i></i><i></i><i className="s"></i><i></i>
            </span>
          </div>
        </div>
      </div>}
    </section>
  );
}

/* ---------- Footer ---------- */
function Footer() {
  return (
    <footer>
      <div className="l">
        <b>Elise Hasseltine</b> &nbsp;/&nbsp; Communications Practice
      </div>
      <div className="c">
        Set in Helvetica &nbsp;/&nbsp; Hand built, 2026
      </div>
      <div className="r">
        © 2026 &nbsp;/&nbsp; All Rights Reserved
      </div>
    </footer>
  );
}

/* ---------- Tweaks ---------- */
function Tweaks({ tweaks, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Palette" />
      <TweakSelect
        label="Color System"
        value={tweaks.palette}
        options={[
          { value: "subway",    label: "Subway (Default)" },
          { value: "newsstand", label: "Newsstand Vintage" },
          { value: "press",     label: "Letterpress Muted" },
        ]}
        onChange={(v) => setTweak("palette", v)}
      />
    </TweaksPanel>
  );
}

/* ---------- App ---------- */
function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const active = useActiveSection(["practice", "focus", "contact"]);
  const [openSections, setOpenSections] = useState({});
  useReveal([openSections]);
  const toggle = (id) => setOpenSections((s) => ({ ...s, [id]: !s[id] }));
  const openSection = (id) => setOpenSections((s) => (s[id] ? s : { ...s, [id]: true }));

  useEffect(() => {
    document.documentElement.setAttribute("data-palette", tweaks.palette);
  }, [tweaks.palette]);

  useEffect(() => {
    if (location.hash) {
      const id = location.hash.slice(1);
      if (["practice", "focus", "contact"].includes(id)) openSection(id);
      requestAnimationFrame(() => scrollToId(id));
    }
  }, []);

  return (
    <React.Fragment>
      <TopNav active={active} onNavClick={openSection} />
      <Hero />
      <Practice open={!!openSections.practice} onToggle={() => toggle("practice")} />
      <Focus open={!!openSections.focus} onToggle={() => toggle("focus")} />
      <Contact open={!!openSections.contact} onToggle={() => toggle("contact")} />
      <Footer />
      <Tweaks tweaks={tweaks} setTweak={setTweak} />
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
