// CTL 2025, main app

const { useState, useEffect, useMemo, useRef } = React;

const DATA = window.CTL_DATA;
const TAG_KEYS = ["RE", "CON", "CORP", "FIN", "SALES", "GOV"];

// ============ CHAPTER MOTIF ============
// A single typographic treatment for every cluster, the chapter title,
// rendered large in display italic. No symbology, no article-number charts.
// Keeps the right column simple and on-topic.

function ChapterMotif({ cluster }) {
  return (
    <div className="motif-typo">
      <div className="motif-typo-num">{cluster.num}</div>
      <div className="motif-typo-title">{cluster.title}</div>
      <div className="motif-typo-rule"></div>
      <div className="motif-typo-tags">
        {Array.from(new Set(cluster.cards.flatMap(c => c.tags))).map(t => (
          <span key={t} className="tag" data-tag={t}>{t}</span>
        ))}
      </div>
    </div>
  );
}

// ============ CARD ============

function Card({ card, baMode, activeTags }) {
  const [shown, setShown] = useState("after");
  const dimmed = activeTags.length > 0 && !card.tags.some(t => activeTags.includes(t));
  const articleNum = card.articlePrimary;
  const isSymbol = articleNum === "—";

  return (
    <article
      className={"card" + (dimmed ? " dimmed" : "")}
      id={"card-" + card.id}
      data-ba-mode={baMode}
      data-show={shown}
    >
      <header className="card-head">
        <div className="card-art">
          <span>Article</span>
          {isSymbol
            ? <span className="card-art-num is-symbol">§</span>
            : <window.ArticleRef number={articleNum} className="card-art-num">{articleNum}</window.ArticleRef>
          }
          {card.articles && card.articles.length > 1 && (
            <div className="card-art-extras">
              {card.articles
                .filter(a => a !== articleNum && /^\d+(?:\(\d+\))?$/.test(a))
                .map(a => {
                  const n = parseInt(a, 10);
                  return <window.ArticleRef key={a} number={n} className="card-art-chip">Art. {a}</window.ArticleRef>;
                })}
            </div>
          )}
          <span className="card-art-id">Card {card.id}</span>
        </div>
        <div className="card-head-main">
          <div className="card-id-row">
            <div className="card-tags">
              {card.tags.map(t => (
                <span key={t} className="tag" data-tag={t}>{t}</span>
              ))}
            </div>
          </div>
          <h3 className="card-title">{card.title}</h3>
          <p className="card-headline">{card.headline}</p>
        </div>
      </header>

      {baMode === "toggle" && (
        <div className="ba-toggle" role="tablist">
          <button
            className={shown === "before" ? "active" : ""}
            onClick={() => setShown("before")}
            role="tab"
            aria-selected={shown === "before"}
          >Before</button>
          <button
            className={shown === "after" ? "active" : ""}
            onClick={() => setShown("after")}
            role="tab"
            aria-selected={shown === "after"}
          >After</button>
        </div>
      )}

      <div className="ba">
        <div className="ba-col before">
          <div className="ba-label">
            <span>Before</span>
            <span className="date">≤ 31 May 2026</span>
          </div>
          <p className="ba-text">{window.linkifyArticles ? window.linkifyArticles(card.before, '1985') : card.before}</p>
        </div>
        <div className="ba-col after">
          <div className="ba-label">
            <span>After</span>
            <span className="date">≥ 1 June 2026</span>
          </div>
          <p className="ba-text">{window.linkifyArticles ? window.linkifyArticles(card.after, '2025') : card.after}</p>
        </div>
      </div>

      <section className="relevance">
        <div className="section-label">
          <span className="mark"></span>
          <span>Practice relevance</span>
        </div>
        <p>{window.linkifyArticles ? window.linkifyArticles(card.relevance) : card.relevance}</p>
      </section>

      <section className="drafting">
        <div className="section-label">
          <span className="mark"></span>
          <span>Drafting & process</span>
        </div>
        <ul>
          {card.drafting.map((d, i) => <li key={i}>{window.linkifyArticles ? window.linkifyArticles(d) : d}</li>)}
        </ul>
      </section>

      {card.disagreement && (
        <aside className="disagreement">
          <span className="disagreement-badge">Firms diverge</span>
          <p>{window.linkifyArticles ? window.linkifyArticles(card.disagreement) : card.disagreement}</p>
        </aside>
      )}

      {card.conceptId && window.ConceptTrigger && (
        <div className="card-concept">
          <window.ConceptTrigger
            conceptId={card.conceptId}
            label={card.conceptLabel || "Concept"}
          />
        </div>
      )}

      <footer className="sources">
        <span className="src-label">Sources</span>
        <span className="src-list">
          {card.sources.map(s => {
            const url = window.CTL_SOURCES && window.CTL_SOURCES[s];
            const entry = url && (typeof url === 'string' ? { url } : url);
            const href = entry && entry.url;
            return href
              ? <a key={s} href={href} target="_blank" rel="noopener noreferrer">{s}</a>
              : <span key={s}>{s}</span>;
          })}
        </span>
        <button
          type="button"
          className="card-feedback-trigger"
          onClick={() => window.openFeedback && window.openFeedback({ cardId: card.id, cardTitle: card.title })}
          title="Send feedback on this card"
        >
          Send feedback on this card
        </button>
      </footer>
    </article>
  );
}

// ============ CHAPTER ============

function Chapter({ cluster, baMode, activeTags }) {
  return (
    <section className="chapter" id={cluster.id} data-screen-label={cluster.num + " " + cluster.title}>
      <div className="chapter-watermark" aria-hidden="true">
        <span className="wm-title"><span className="wm-title-inner">{cluster.title}</span></span>
      </div>
      <div className="chapter-hero">
        <div className="chapter-eyebrow">
          <span className="chapter-eyebrow-num">Cluster {cluster.num}</span>
          <span className="chapter-eyebrow-rule"></span>
          <span className="chapter-eyebrow-count">{cluster.cards.length} cards</span>
        </div>
        <h2 className="chapter-title">{cluster.title}</h2>
        <p className="chapter-lede">{cluster.lede}</p>
        <div className="chapter-foot">
          <div className="chapter-tags">
            {Array.from(new Set(cluster.cards.flatMap(c => c.tags))).map(t => (
              <span key={t} className="tag" data-tag={t}>{t}</span>
            ))}
          </div>
          {cluster.conceptId && window.ConceptTrigger && (
            <window.ConceptTrigger
              conceptId={cluster.conceptId}
              label={cluster.conceptLabel}
            />
          )}
        </div>
      </div>
      <div className="chapter-cards">
        {cluster.cards.map(card => (
          <Card key={card.id} card={card} baMode={baMode} activeTags={activeTags} />
        ))}
      </div>
    </section>
  );
}

// ============ NAV ============

function ClusterNav({ clusters, activeId, viewMode, onViewModeChange }) {
  const navLabel =
    viewMode === "read"     ? null :
    viewMode === "sources"  ? "Sources, primary materials and firm commentaries cited" :
    viewMode === "about"    ? "About, the author and a feedback form" :
                              "Index, all reform cards on one screen";
  return (
    <nav className="cluster-nav" aria-label="Cluster navigation">
      <div className="cluster-nav-inner">
        {viewMode === "read" ? (
          <div className="cluster-nav-tabs">
            {clusters.map((c, i) => (
              <a
                key={c.id}
                href={"#" + c.id}
                className={"nav-item" + (activeId === c.id ? " active" : "")}
              >
                <span className="nav-num">{c.num}</span>
                <span className="nav-label">{c.title}</span>
                {i < clusters.length - 1 && <span className="nav-sep" aria-hidden="true">›</span>}
              </a>
            ))}
          </div>
        ) : (
          <div className="cluster-nav-index">
            <span className="nav-index-label">{navLabel}</span>
          </div>
        )}
        <div className="view-mode-toggle" role="tablist" aria-label="View mode">
          <button
            type="button"
            role="tab"
            aria-selected={viewMode === "index"}
            className={"view-mode-btn" + (viewMode === "index" ? " active" : "")}
            onClick={() => onViewModeChange("index")}
          >
            Index
          </button>
          <button
            type="button"
            role="tab"
            aria-selected={viewMode === "read"}
            className={"view-mode-btn" + (viewMode === "read" ? " active" : "")}
            onClick={() => onViewModeChange("read")}
          >
            Read
          </button>
          <button
            type="button"
            role="tab"
            aria-selected={viewMode === "sources"}
            className={"view-mode-btn" + (viewMode === "sources" ? " active" : "")}
            onClick={() => onViewModeChange("sources")}
          >
            Sources
          </button>
          <button
            type="button"
            role="tab"
            aria-selected={viewMode === "about"}
            className={"view-mode-btn" + (viewMode === "about" ? " active" : "")}
            onClick={() => onViewModeChange("about")}
          >
            About
          </button>
        </div>
      </div>
    </nav>
  );
}

// ============ SOURCES VIEW ============

function SourcesView() {
  const firms = useMemo(() => {
    const src = window.CTL_SOURCES || {};
    return Object.entries(src)
      .map(([key, entry]) => {
        const url   = typeof entry === 'string' ? entry : (entry && entry.url) || '';
        const title = (entry && entry.title) || '';
        return { key, url, title };
      })
      .sort((a, b) => a.key.localeCompare(b.key));
  }, []);

  return (
    <main className="sources-view">
      <section className="sources-section">
        <h2 className="sources-h">Methodology</h2>
        <p>
          This site is designed for legal professionals, and does not
          constitute legal advice.
        </p>
        <p>
          This site draws on an analysis of different versions of the law
          and analysis of commentaries of multiple leading UAE law firms
          combined to produce an editorial overview of the legal reforms.
        </p>
        <p>
          If firms have diverged in their analysis, this is highlighted at
          the bottom of each card.
        </p>
        <p>
          All of the sources and articles are listed below and you are
          highly encouraged to read them.
        </p>
      </section>

      <section className="sources-section">
        <h2 className="sources-h">How this project was built</h2>
        <p>
          This project utilises AI in both its coding and synthesis of
          information. All content has been reviewed and edited by a human
          lawyer (me) prior to publication, and you will see areas in which
          I have highlighted my disagreement with some of the analysis of
          other lawyers.
        </p>
        <p>
          You can view the source materials in the Sources tab. I have
          ensured that you are always able to see the new and old laws side
          by side, so you can read the law for yourself, and would encourage
          you to click the links and digest the commentary of the many law
          firms who have been publishing content on this topic. As new
          content appears, I will add an "Additional content" section to
          Sources to keep you provided with the most up to date information.
        </p>
        <p>
          Treat this project as a starting point for understanding the
          reform landscape. For any matter on which you intend to rely,
          engage one of the firms whose commentaries have helped shape this
          work, or other suitably qualified UAE counsel.
        </p>
      </section>

      <section className="sources-section">
        <h2 className="sources-h">Primary materials</h2>
        <p className="sources-lede">
          The gazetted text of both the 2025 Civil Transactions Law and the
          1985 Code it replaces, in the official English translations.
        </p>
        <ul className="sources-list sources-list-primary">
          <li>
            <a className="sources-item" href="uploads/CTL-2025-official.pdf" target="_blank" rel="noopener noreferrer">
              <span className="src-title">UAE Civil Transactions Law 2025</span>
              <span className="src-meta">Federal Decree-Law No. 25 of 2025 · official English PDF · in force 1 June 2026</span>
            </a>
          </li>
          <li>
            <a className="sources-item" href="uploads/Federal%20Decree%20by%20Law%20No.%20(25)%20of%202025%20Promulgating%20the%20Civil%20Transactions%20Law.pdf" target="_blank" rel="noopener noreferrer">
              <span className="src-title">Federal Decree by Law promulgating the 2025 Law</span>
              <span className="src-meta">Issuing decree · official Arabic/English PDF</span>
            </a>
          </li>
          <li>
            <a className="sources-item" href="uploads/Federal%20Decree%20Law%20No.%20(5)%20of%201985%20Concerning%20the%20Issuance%20of%20the%20Civil%20Transactions%20Law%20of%20the%20United%20Arab%20Emirates.pdf" target="_blank" rel="noopener noreferrer">
              <span className="src-title">UAE Civil Transactions Law 1985 (repealed)</span>
              <span className="src-meta">Federal Law No. (5) of 1985 · official English PDF · 1,528 articles</span>
            </a>
          </li>
        </ul>
        <p className="sources-note">
          The 2025 corpus rendered in the article-peek panel is extracted from the
          first PDF above; the 1985 corpus is extracted from the third.
        </p>
      </section>

      <section className="sources-section">
        <h2 className="sources-h">Firm commentaries</h2>
        <p className="sources-lede">
          Published commentaries from leading UAE law firms that the
          catalogue draws on.
        </p>
        <ol className="sources-list sources-list-firms">
          {firms.map(({ key, url, title }) => (
            <li key={key}>
              <a className="sources-item" href={url} target="_blank" rel="noopener noreferrer">
                <span className="src-title">{key}</span>
                {title && <span className="src-meta src-article-title">{title}</span>}
              </a>
            </li>
          ))}
        </ol>
      </section>

    </main>
  );
}

// ============ ABOUT VIEW ============

function AboutView({ seed }) {
  return (
    <main className="sources-view">
      <section className="sources-section">
        <h2 className="sources-h">About</h2>
        <p>
          I'm Geoffrey Janes, a UK-qualified Solicitor based in Dubai. I
          work in-house, having moved into law from a career in technology.
          The earlier life left me with the instinct to reach for code where
          others reach for a spreadsheet, and that's shaped how this project
          was built. The synthesis and first-draft structure are AI-assisted,
          the legal editing is mine, and the combination is the point.
        </p>
        <p>
          <a className="about-linkedin" href="https://www.linkedin.com/in/geoffreyjanes/" target="_blank" rel="noopener noreferrer">
            Connect with me on LinkedIn
          </a>
        </p>
        <p className="sources-note">
          Fair warning: I'm dreadful at LinkedIn. The inbox is mostly
          recruiters and webinar invitations, and I check it rarely. If you
          have something concrete to say, the feedback form below will reach
          me faster.
        </p>
      </section>

      <section className="sources-section" id="feedback">
        <h2 className="sources-h">Feedback</h2>
        <p className="sources-lede">
          Spot an error, a missing case, or a firm commentary that should be
          cited? Tell me.
        </p>
        <FeedbackForm seed={seed} />
      </section>
    </main>
  );
}

// ============ FEEDBACK FORM ============
// POSTs to /api/feedback (Cloudflare Pages Function) which delivers the
// message to the configured routing address via Email Workers SEND_EMAIL.
// `seed` is optional pre-fill text (used when the user clicks "Send feedback
// on this card" from inside a card and lands here with that card's context).

function FeedbackForm({ seed }) {
  const [name, setName]       = useState("");
  const [email, setEmail]     = useState("");
  const [message, setMessage] = useState(seed || "");
  const [website, setWebsite] = useState(""); // honeypot
  const [status, setStatus]   = useState("idle"); // idle | sending | sent | error
  const [errorMsg, setErrorMsg] = useState("");

  // If a new seed arrives (user clicks a different card's feedback link),
  // prepend it to whatever is already in the textarea so existing typing
  // isn't destroyed.
  useEffect(() => {
    if (!seed) return;
    setMessage(prev => prev.includes(seed) ? prev : (seed + (prev ? "\n\n" + prev : "")));
  }, [seed]);

  async function handleSubmit(e) {
    e.preventDefault();
    const msg = message.trim();
    if (!msg) return;
    setStatus("sending");
    setErrorMsg("");
    try {
      const res = await fetch("/api/feedback", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          name: name.trim(),
          email: email.trim(),
          message: msg,
          website, // honeypot, real users leave it blank
        }),
      });
      if (!res.ok) {
        let detail = "Send failed.";
        try { const j = await res.json(); if (j.error) detail = j.error; } catch {}
        throw new Error(detail);
      }
      setStatus("sent");
      setName(""); setEmail(""); setMessage("");
    } catch (err) {
      setStatus("error");
      setErrorMsg(String(err.message || err));
    }
  }

  if (status === "sent") {
    return (
      <div className="feedback-form feedback-sent">
        <p><strong>Thank you.</strong> Your message is on its way. I'll read it as soon as I can.</p>
        <button type="button" className="feedback-reset" onClick={() => setStatus("idle")}>
          Send another
        </button>
      </div>
    );
  }

  const disabled = status === "sending" || !message.trim();

  return (
    <form className="feedback-form" onSubmit={handleSubmit}>
      {/* honeypot, hidden from real users and screen readers; bots fill it
          in and the backend silently drops their message */}
      <div className="feedback-honeypot" aria-hidden="true">
        <label>
          Website (leave this blank)
          <input
            type="text"
            tabIndex={-1}
            autoComplete="off"
            value={website}
            onChange={(e) => setWebsite(e.target.value)}
          />
        </label>
      </div>
      <div className="feedback-row">
        <label className="feedback-field">
          <span className="feedback-label">Name <span className="opt">(optional)</span></span>
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
            autoComplete="name"
            disabled={status === "sending"}
          />
        </label>
        <label className="feedback-field">
          <span className="feedback-label">Email <span className="opt">(optional)</span></span>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            autoComplete="email"
            inputMode="email"
            disabled={status === "sending"}
          />
        </label>
      </div>
      <label className="feedback-field">
        <span className="feedback-label">Message</span>
        <textarea
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          rows={6}
          required
          disabled={status === "sending"}
        />
      </label>
      <div className="feedback-actions">
        <button type="submit" className="feedback-submit" disabled={disabled}>
          {status === "sending" ? "Sending..." : "Send feedback"}
        </button>
        {status === "error" && (
          <span className="feedback-error">Sorry, {errorMsg} Please try again.</span>
        )}
      </div>
    </form>
  );
}

// ============ TAG FILTER (slicer dropdown) ============

function TagFilter({ activeTags, onToggle, onClear, hasActive }) {
  const [open, setOpen] = useState(false);
  const wrapRef = useRef(null);

  // close on outside click
  useEffect(() => {
    if (!open) return;
    const onDown = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  return (
    <div className={"filter-bar" + (hasActive ? " is-active" : "")} ref={wrapRef}>
      <button
        type="button"
        className={"filter-toggle" + (open ? " open" : "")}
        onClick={() => setOpen(o => !o)}
        aria-expanded={open}
        aria-haspopup="listbox"
      >
        {hasActive ? <span className="filter-pulse" aria-hidden="true"></span> : null}
        <span className="filter-toggle-label">Filter by function</span>
        {hasActive && (
          <span className="filter-toggle-active">
            {activeTags.map(k => <span key={k} className="ft-chip" style={{ color: "var(--t-" + k + ")", borderColor: "var(--t-" + k + ")" }}>{k}</span>)}
          </span>
        )}
        <span className="filter-toggle-caret" aria-hidden="true">▾</span>
      </button>

      {open && (
        <div className="filter-dropdown" role="listbox" aria-multiselectable="true">
          <div className="filter-dropdown-head">
            <span>Function tags</span>
            {hasActive && (
              <button type="button" className="filter-dropdown-clear" onClick={onClear}>
                Clear all
              </button>
            )}
          </div>
          <ul className="filter-dropdown-list">
            {TAG_KEYS.map(k => {
              const isActive = activeTags.includes(k);
              return (
                <li key={k}>
                  <button
                    type="button"
                    role="option"
                    aria-selected={isActive}
                    className={"filter-option" + (isActive ? " active" : "")}
                    onClick={() => onToggle(k)}
                  >
                    <span className="filter-option-check" aria-hidden="true">
                      {isActive ? "✓" : ""}
                    </span>
                    <span className="filter-option-dot" style={{ background: "var(--t-" + k + ")" }}></span>
                    <span className="filter-option-key">{k}</span>
                    <span className="filter-option-label">{DATA.tags[k].label}</span>
                  </button>
                </li>
              );
            })}
          </ul>
        </div>
      )}
    </div>
  );
}

// ============ INDEX VIEW ============
// Compact grid of every card. Click a tile to flip it open in-place
// and reveal the full card content. Filter dimming applies the same
// way as in Read mode.

function IndexTile({ card, dimmed, baMode }) {
  const [open, setOpen] = useState(false);
  const isSymbol = card.articlePrimary === "—";

  // Esc to close when open
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open]);

  return (
    <article
      className={"index-tile" + (open ? " open" : "") + (dimmed ? " dimmed" : "")}
      data-card-id={card.id}
    >
      <div className="index-tile-flip" key={open ? "back" : "front"}>
        {open ? (
          <div className="index-tile-back">
            <button
              type="button"
              className="index-tile-close"
              onClick={() => setOpen(false)}
              aria-label="Close card"
            >
              ×
            </button>
            <Card card={card} baMode={baMode} activeTags={[]} />
          </div>
        ) : (
          <button
            type="button"
            className="index-tile-front"
            onClick={() => setOpen(true)}
            aria-expanded="false"
          >
            <div className="index-tile-head">
              <span className="index-tile-art">
                {isSymbol ? "§" : "Art. " + card.articlePrimary}
              </span>
              <span className="index-tile-id">{card.id}</span>
            </div>
            <h4 className="index-tile-title">{card.title}</h4>
            <p className="index-tile-headline">{card.headline}</p>
            <div className="index-tile-tags">
              {card.tags.map(t => (
                <span key={t} className="tag" data-tag={t}>{t}</span>
              ))}
            </div>
            <span className="index-tile-affordance" aria-hidden="true">↻ Flip</span>
          </button>
        )}
      </div>
    </article>
  );
}

function IndexView({ activeTags, baMode }) {
  return (
    <main className="index-view">
      {DATA.clusters.map(cluster => {
        return (
          <section key={cluster.id} className="index-cluster" id={"index-" + cluster.id}>
            <header className="index-cluster-head">
              <span className="index-cluster-num">{cluster.num}</span>
              <h3 className="index-cluster-title">{cluster.title}</h3>
              <span className="index-cluster-count">{cluster.cards.length} cards</span>
            </header>
            <div className="index-grid">
              {cluster.cards.map(card => {
                const dimmed = activeTags.length > 0 && !card.tags.some(t => activeTags.includes(t));
                return (
                  <IndexTile
                    key={card.id}
                    card={card}
                    dimmed={dimmed}
                    baMode={baMode}
                  />
                );
              })}
            </div>
          </section>
        );
      })}
    </main>
  );
}

// ============ APP ============

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "baMode": "side-by-side",
  "showDrafting": true
}/*EDITMODE-END*/;

function App() {
  const [activeTags, setActiveTags] = useState([]);
  const [activeCluster, setActiveCluster] = useState(DATA.clusters[0].id);
  const [viewMode, setViewMode] = useState("index");
  const [feedbackSeed, setFeedbackSeed] = useState("");
  const [t, setTweak] = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];

  // Expose a window-level entry point so any card can trigger the feedback
  // form pre-filled with that card's context, without prop-threading.
  useEffect(() => {
    window.openFeedback = ({ cardId, cardTitle } = {}) => {
      const seed = cardId
        ? `Re: Card ${cardId}` + (cardTitle ? ` — ${cardTitle}` : "") + "\n\n"
        : "";
      setFeedbackSeed(seed);
      setViewMode("about");
      // scroll the feedback section into view after the About view renders
      requestAnimationFrame(() => {
        const el = document.getElementById("feedback");
        if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
      });
    };
    return () => { delete window.openFeedback; };
  }, []);

  useEffect(() => {
    document.body.dataset.showDrafting = t.showDrafting ? "on" : "off";
  }, [t.showDrafting]);

  // intersection observer for active cluster, only active in Read mode
  useEffect(() => {
    if (viewMode !== "read") return;
    const opts = { rootMargin: "-30% 0px -60% 0px", threshold: 0 };
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) setActiveCluster(e.target.id);
      });
    }, opts);
    DATA.clusters.forEach(c => {
      const el = document.getElementById(c.id);
      if (el) io.observe(el);
    });
    return () => io.disconnect();
  }, [viewMode]);

  const toggleTag = (tag) => {
    setActiveTags(prev => prev.includes(tag) ? prev.filter(t => t !== tag) : [...prev, tag]);
  };

  const jumpToCard = (cardId) => {
    setViewMode("read");
    // wait one tick for Read view to render, then scroll
    requestAnimationFrame(() => {
      const el = document.getElementById("card-" + cardId);
      if (el) {
        const y = el.getBoundingClientRect().top + window.scrollY - 96;
        window.scrollTo({ top: y, behavior: "smooth" });
      }
    });
  };

  return (
    <>
      <header className="mast">
        <div className="mast-inner">
          <div className="mast-eyebrow">
            <span>{DATA.meta.instrument}</span>
            <span>{DATA.meta.issued}</span>
          </div>
          <h1>The UAE Civil Transactions Law, <em>recast</em>.</h1>
          <p className="mast-sub">A Reform Companion across seven clusters and thirty-two cards. The text of the 1985 Code, the text of the 2025 Code, and the drafting consequences for transactional templates and in-house practice.</p>
          <div className="mast-meta">
            <div className="mast-meta-item">
              <span className="k">In force</span>
              <span className="v">1 June 2026</span>
            </div>
            <div className="mast-meta-item">
              <span className="k">Replaces</span>
              <span className="v">Federal Law No. 5 of 1985</span>
            </div>
            <div className="mast-meta-item">
              <span className="k">Clusters</span>
              <span className="v">7</span>
            </div>
            <div className="mast-meta-item">
              <span className="k">Reform cards</span>
              <span className="v">32</span>
            </div>
          </div>
        </div>
      </header>

      <aside className="how-to-use" aria-label="How to use this catalogue">
        <span className="how-to-use-eyebrow">How to use</span>
        <ul>
          <li><strong>Index</strong>, browse all 32 reform cards on one screen; click a tile to flip it open in place.</li>
          <li><strong>Read</strong>, work through the seven clusters top-to-bottom in narrative order.</li>
          <li><strong>Article numbers</strong> (e.g. <em>Art. 121</em>) are clickable, they open the gazetted statute text in a side panel.</li>
        </ul>
      </aside>

      <div className="sticky-header">
        <ClusterNav
          clusters={DATA.clusters}
          activeId={activeCluster}
          viewMode={viewMode}
          onViewModeChange={(m) => setViewMode(m)}
        />
        {viewMode !== "sources" && viewMode !== "about" && (
          <TagFilter
            activeTags={activeTags}
            onToggle={toggleTag}
            onClear={() => setActiveTags([])}
            hasActive={activeTags.length > 0}
          />
        )}
      </div>

      {viewMode === "read" ? (
        <main>
          {DATA.clusters.map(cluster => (
            <Chapter
              key={cluster.id}
              cluster={cluster}
              baMode={t.baMode}
              activeTags={activeTags}
            />
          ))}
        </main>
      ) : viewMode === "sources" ? (
        <SourcesView />
      ) : viewMode === "about" ? (
        <AboutView seed={feedbackSeed} />
      ) : (
        <IndexView
          activeTags={activeTags}
          baMode={t.baMode}
        />
      )}

      <div className="page-copyright">© Geoffrey Janes 2026. All rights reserved.</div>

      {window.TweaksPanel && (
        <window.TweaksPanel title="Tweaks">
          <window.TweakSection label="Display">
            <window.TweakToggle
              label="Show drafting & process"
              value={t.showDrafting}
              onChange={v => setTweak("showDrafting", v)}
            />
          </window.TweakSection>
          <window.TweakSection label="Before / After">
            <window.TweakRadio
              label="Layout"
              value={t.baMode}
              onChange={v => setTweak("baMode", v)}
              options={[
                { value: "side-by-side", label: "Side by side" },
                { value: "toggle", label: "Toggle" }
              ]}
            />
          </window.TweakSection>
        </window.TweaksPanel>
      )}
    </>
  );
}

// ============ WELCOME OVERLAY ============
// First-visit modal: states the catalogue's purpose, professional audience,
// disclaimer of legal advice and warranty, and acceptance-on-continue. After
// dismissal a flag is written to localStorage so the overlay never reopens
// on the same browser. To force-show during dev: localStorage.removeItem
// ('ctl-welcome-ack').

const WELCOME_ACK_KEY = "ctl-welcome-ack";

function WelcomeOverlay() {
  const [open, setOpen] = useState(() => {
    try { return !localStorage.getItem(WELCOME_ACK_KEY); }
    catch { return true; }
  });

  // Lock body scroll while the overlay is open.
  useEffect(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = prev; };
  }, [open]);

  // Close on Escape too, accessibility.
  useEffect(() => {
    if (!open) return;
    const handler = (e) => { if (e.key === "Escape") acknowledge(); };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [open]);

  function acknowledge() {
    try { localStorage.setItem(WELCOME_ACK_KEY, new Date().toISOString()); }
    catch { /* private mode / disabled storage, silent */ }
    setOpen(false);
  }

  if (!open) return null;

  return (
    <div className="welcome-scrim" role="dialog" aria-modal="true" aria-labelledby="welcome-title">
      <div className="welcome-panel">
        <div className="welcome-eyebrow">
          <span>UAE Civil Transactions Law 2025</span>
          <span>Reform Companion</span>
        </div>
        <h2 id="welcome-title" className="welcome-h">Welcome.</h2>

        <div className="welcome-body">
          <p className="welcome-privacy">
            This site collects no personal data and uses no tracking cookies.
            Any analytics used are general and anonymised.
          </p>

          <p>
            This is a Reform Companion covering the UAE Civil Transactions
            Law 2025 (Federal Decree-Law No. 25 of 2025), which comes into
            force on <strong>1 June 2026</strong>, and the 1985 Code it
            replaces. It pairs the gazetted statutory text with a synthesis
            of leading UAE firm commentaries on the reforms.
          </p>

          <h3 className="welcome-h3">Designed for legal professionals.</h3>
          <p>
            The companion assumes a working familiarity with civil-code
            interpretation, UAE practice, and the contractual instruments in
            play.
          </p>

          <h3 className="welcome-h3">Not legal advice.</h3>
          <p>
            Nothing on this site constitutes legal advice or the establishment
            of a lawyer-client relationship. No warranty, express or implied,
            is given as to the accuracy, completeness or currency of any
            information, interpretation or suggestion. By continuing past this
            notice you accept that you use this work at your own risk.
          </p>
          <p>
            This project draws on the published work of UAE law firms whose
            commentaries have helped shape this catalogue. You can find them,
            together with the methodology and a note on the tools used to
            build this project, under
            <button type="button" className="welcome-inline-link"
              onClick={() => { acknowledge(); }}
            >Sources</button>. Should you need legal advice, engage one of
            those firms or other suitably qualified UAE counsel.
          </p>
        </div>

        <div className="welcome-actions">
          <button type="button" className="welcome-accept" onClick={acknowledge} autoFocus>
            I understand, continue
          </button>
        </div>
      </div>
    </div>
  );
}

const Root = () => {
  let app = (
    <>
      <App />
      <WelcomeOverlay />
    </>
  );
  if (window.ConceptPeekProvider) {
    app = <window.ConceptPeekProvider>{app}</window.ConceptPeekProvider>;
  }
  if (window.ArticlePeekProvider) {
    app = <window.ArticlePeekProvider>{app}</window.ArticlePeekProvider>;
  }
  return app;
};

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