/* global React */
const { useState } = React;

/* ============================================================
   Sign-up screening
   Three-tier check at the gate:
   (1) Personal / disposable email  → reject with friendly retype message
   (2) Competitor firm domain       → reject with polite "not for you" panel
   (3) Tier-2 firm with shipping desk → accept, but tag CRM for review
   (4) Anything else                 → accept normally
   In production these lists live in source, reviewed quarterly.
   Replace with Kickbox / ZeroBounce for catch-all and disposable detection.
   ============================================================ */
const FREE_EMAIL_DOMAINS = new Set([
  "gmail.com", "googlemail.com",
  "hotmail.com", "hotmail.co.uk", "hotmail.fr",
  "outlook.com", "outlook.co.uk", "live.com", "live.co.uk", "msn.com",
  "yahoo.com", "yahoo.co.uk", "yahoo.fr", "ymail.com", "rocketmail.com",
  "icloud.com", "me.com", "mac.com",
  "aol.com", "aim.com",
  "protonmail.com", "proton.me", "pm.me",
  "gmx.com", "gmx.co.uk", "gmx.de",
  "mail.com", "hey.com", "fastmail.com", "tutanota.com", "zoho.com",
  "yandex.com", "yandex.ru",
  "mailinator.com", "guerrillamail.com", "10minutemail.com",
  "tempmail.com", "throwaway.email", "trashmail.com", "yopmail.com",
]);

/* Hard block — English shipping & trade firms + chambers.
   This is the canonical list. Reviewed quarterly. When Jonathan flags a new
   firm to add (or asks to remove one), edit both kits and the source codebase
   in lockstep — never let them drift. */
const COMPETITOR_FIRM_DOMAINS = new Set([
  /* Tier 1 — shipping/trade specialists */
  "hfw.com",                  // Holman Fenwick Willan
  "reedsmith.com",            // Reed Smith
  "clydeco.com",              // Clyde & Co
  "shlegal.com",              // Stephenson Harwood
  "wfw.com",                  // Watson Farley & Williams
  "hilldickinson.com",        // Hill Dickinson
  "bentleys.co.uk",           // Bentleys, Stokes & Lowless
  "cjclaw.com",               // Campbell Johnston Clark
  "tathams.co.uk",            // Tatham & Co
  "mfbsolicitors.com",        // MFB Solicitors
  "thomascooperlaw.com",      // Thomas Cooper LLP
  "mills-co.com",             // Mills & Co Solicitors
  "salvuslaw.com",            // Salvus Law
  "waltonsandmorse.com",      // Waltons & Morse
  "wr.no",                    // Wikborg Rein (London office)
  /* Tier 1 — shipping/commercial chambers */
  "quadrantchambers.com",     // Quadrant Chambers
  "essexcourt.com",           // Essex Court Chambers
  "7kbw.co.uk",               // 7 King's Bench Walk
  "20essexst.com",            // 20 Essex Chambers
  "stonechambers.com",        // Stone Chambers
  "4newsquare.com",           // 4 New Square (added May 2026)
  "brickcourt.co.uk",         // Brick Court Chambers (added May 2026)
  /* Tier 1 — additional specialist & marine practices (added May 2026) */
  "dacbeachcroft.com",        // DAC Beachcroft (marine + insurance)
  "ashfords.co.uk",           // Ashfords (shipping team)
  /* Tier 2 (escalated to hard block per Jonathan, May 2026) —
     Magic Circle + global firms with shipping desks */
  "aoshearman.com",           // A&O Shearman
  "allenovery.com",           // Allen & Overy (legacy)
  "cliffordchance.com",       // Clifford Chance
  "freshfields.com",          // Freshfields
  "linklaters.com",           // Linklaters
  "slaughterandmay.com",      // Slaughter and May
  "hsf.com",                  // Herbert Smith Freehills
  "hoganlovells.com",         // Hogan Lovells
  "nortonrosefulbright.com",  // Norton Rose Fulbright
  "dlapiper.com",             // DLA Piper
  "eversheds-sutherland.com", // Eversheds Sutherland
  "cms-law.com",              // CMS
  "cms.law",                  // CMS (group)
  "bclplaw.com",              // Bryan Cave Leighton Paisner
  "whitecase.com",            // White & Case
  "twobirds.com",             // Bird & Bird
  "lw.com",                   // Latham & Watkins
  "kennedyslaw.com",          // Kennedys (marine practice)
  "penningtonslaw.com",       // Penningtons Manches Cooper
]);

/* Reserved — silent CRM flag without blocking. Empty for now; mechanism
   kept so Jonathan can add firms he wants on the CRM with a review flag
   (e.g. "interesting potential client, but the firm is a competitor in name").
   Production CMS hook: when a signup matches one of these, tag the CRM
   record `review-before-outreach`. */
const REVIEW_FIRM_DOMAINS = new Set([]);

function evaluateSignupEmail(email) {
  if (!email) return { kind: null, ok: false };
  const trimmed = email.trim().toLowerCase();
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
    if (trimmed.length > 4 && trimmed.includes("@")) {
      return { kind: "format", ok: false, message: "Please enter a complete email address." };
    }
    return { kind: null, ok: false };
  }
  const domain = trimmed.split("@")[1];
  if (FREE_EMAIL_DOMAINS.has(domain)) {
    return {
      kind: "personal", ok: false,
      message: "Fathom is for work use. Please sign up with the email your firm gives you — we can't accept personal addresses.",
    };
  }
  if (COMPETITOR_FIRM_DOMAINS.has(domain)) {
    return {
      kind: "competitor", ok: false, domain,
      message: "Fathom is published for clients and counterparties — not for other practitioners.",
    };
  }
  if (REVIEW_FIRM_DOMAINS.has(domain)) {
    return { kind: "review", ok: true, domain };
  }
  return { kind: "ok", ok: true, domain };
}

/* Back-compat shim — old call sites pass through here. */
function validateWorkEmail(email) {
  const r = evaluateSignupEmail(email);
  return r.message || null;
}
window.evaluateSignupEmail = evaluateSignupEmail;
window.validateWorkEmail = validateWorkEmail;

/* Logos as <img> from assets/. Fallbacks for the wordmark are baked SVG. */
function Wordmark({ height = 28 }) {
  return <img src="assets/logos/wordmark.svg" alt="Fathom" style={{ height, display: "block" }} />;
}
function MedallionReversed({ size = 32 }) {
  return <img src="assets/logos/medallion-reversed.svg" alt="" style={{ width: size, height: size }} />;
}

/* ----- Top nav ----- */
function Nav({ active, onNav, signedIn }) {
  return (
    <nav className="nav">
      <div className="nav-inner">
        <a href="#" className="nav-mark" onClick={(e) => { e.preventDefault(); onNav("home"); }}>
          <Wordmark height={26} />
        </a>
        <div className="nav-links">
          <a href="#" className={active === "briefings" ? "active" : ""} onClick={(e) => { e.preventDefault(); onNav("briefings"); }}>Briefings</a>
          <a href="#" className={active === "logbook" ? "active" : ""} onClick={(e) => { e.preventDefault(); onNav("logbook"); }}>The Logbook</a>
          {signedIn ? (
            <a href="#" onClick={(e) => { e.preventDefault(); onNav("dashboard"); }} className="nav-avatar" title="Account">JC</a>
          ) : (
            <a href="#" className="btn btn-secondary nav-cta" onClick={(e) => { e.preventDefault(); onNav("signin"); }}>Sign in</a>
          )}
        </div>
        <button className="nav-burger" aria-label="Menu">≡</button>
      </div>
    </nav>
  );
}

/* ----- Footer ----- */
function Footer() {
  return (
    <footer className="footer">
      <div className="container">
        <h4>FATHOM — Shipping and trade law explained.</h4>
        <p className="reg">
          Fathom is a content brand owned and operated by Jonathan Cooke. Jonathan Cooke is a solicitor of England and Wales,
          regulated by the Solicitors Regulation Authority (SRA No. [—]), currently practising at [Firm]. The information published
          on this site is general guidance and is not legal advice. No solicitor-client relationship is created by reading or
          downloading material on this site. Any retainer, if instructed, will be with [Firm] and will be subject to conflict
          checks, anti-money-laundering checks and a signed engagement letter.
        </p>
        <div className="footer-links">
          <a href="#">Privacy</a>
          <a href="#">Terms</a>
          <a href="#">Cookies</a>
          <a href="#">Accessibility</a>
          <a href="#">Sitemap</a>
          <a href="#">Contact</a>
        </div>
      </div>
    </footer>
  );
}

/* ----- Hero (uses brand-pattern.svg) ----- */
function Hero({ onPrimary, onSecondary }) {
  return (
    <section className="hero">
      <img className="hero-pattern" src="assets/logos/brand-pattern-white.svg" alt="" />
      <div className="hero-fallback">
        <Wordmark height={56} />
        <p className="editorial" style={{ fontSize: 18, marginTop: 12 }}>Shipping and trade law explained.</p>
      </div>
      <div className="hero-buttons">
        <button className="btn btn-primary" onClick={onPrimary}>Browse Briefings</button>
        <button className="btn btn-secondary" onClick={onSecondary}>Sign up — free</button>
      </div>
    </section>
  );
}

/* ----- Briefing card (used on home + index + related) ----- */
function BriefingCard({ b, onOpen, signedIn }) {
  return (
    <article className="card b-card" onClick={() => onOpen(b)} style={{ cursor: "pointer" }}>
      <div className="eyebrow b-eyebrow">BRIEFING · {b.topic}</div>
      <span className="b-title">{b.title}</span>
      <div className="b-ticks">
        {[5, 6, 7, 8, 9, 10].map((h, i) => <span key={i} style={{ height: h }} />)}
      </div>
      <div className="b-meta">
        <time>{b.minutes} min · {b.date}</time>
        <span className="btn btn-ghost" style={{ padding: "6px 12px", fontSize: 12 }}>{signedIn ? "Open →" : "Sign in →"}</span>
      </div>
    </article>
  );
}

/* ----- Sticky right-rail CTA ----- */
function CtaRail({ onOpenBridge }) {
  return (
    <aside className="cta-rail">
      <h3>Speak to a lawyer about this</h3>
      <hr className="brass-rule" />
      <p>30-minute call.<br />No obligation. On the house.</p>
      <button className="btn btn-on-navy" onClick={onOpenBridge}>Open the Bridge</button>
    </aside>
  );
}

/* ----- Sticky mobile CTA bar ----- */
function CtaBar({ onOpenBridge }) {
  return (
    <div className="cta-bar">
      <div>
        <strong style={{ display: "block", fontSize: 14 }}>Speak to a lawyer</strong>
        <span className="editorial" style={{ fontSize: 12 }}>30 min · free</span>
      </div>
      <button className="btn btn-on-navy" onClick={onOpenBridge}>Open the Bridge</button>
    </div>
  );
}

/* ----- Inline markup parser -----
   Turns the lightweight markup used in content.json into React nodes:
     **bold**      -> <strong>
     _italic_      -> <em> in the serif face (used for case names etc.)
     [^1]          -> footnote superscript linking to #fn1
   Authors only ever type these three; everything else is plain text. */
function renderInline(text, keyPrefix = "i") {
  if (typeof text !== "string") return text;
  const nodes = [];
  const re = /(\*\*([^*]+)\*\*)|(_([^_]+)_)|(\[\^(\d+)\])/g;
  let last = 0, m, k = 0;
  while ((m = re.exec(text))) {
    if (m.index > last) nodes.push(text.slice(last, m.index));
    if (m[2] !== undefined) {
      nodes.push(React.createElement("strong", { key: keyPrefix + (k++) }, m[2]));
    } else if (m[4] !== undefined) {
      nodes.push(React.createElement("em", { key: keyPrefix + (k++), style: { fontFamily: "var(--font-serif)" } }, m[4]));
    } else if (m[6] !== undefined) {
      nodes.push(React.createElement("a", { key: keyPrefix + (k++), className: "footnote-num", href: "#fn" + m[6] }, m[6]));
    }
    last = re.lastIndex;
  }
  if (last < text.length) nodes.push(text.slice(last));
  return nodes;
}

/* ----- Briefing body renderer -----
   Renders the `body` block array from content.json. Supported block types:
     { type: "h2",  text }
     { type: "p",   text }
     { type: "ul" | "ol", items: […] }
     { type: "table", head: […], rows: [[…]] }
     { type: "footnotes", items: […] }   (auto-numbered; targets of [^n]) */
function BriefingBody({ blocks }) {
  if (!Array.isArray(blocks) || !blocks.length) return null;
  return blocks.map((blk, i) => {
    switch (blk.type) {
      case "h2":
        return <h2 key={i}>{renderInline(blk.text, "h" + i)}</h2>;
      case "ul":
        return <ul key={i}>{(blk.items || []).map((it, j) => <li key={j}>{renderInline(it, "u" + i + "-" + j)}</li>)}</ul>;
      case "ol":
        return <ol key={i}>{(blk.items || []).map((it, j) => <li key={j}>{renderInline(it, "o" + i + "-" + j)}</li>)}</ol>;
      case "table":
        return (
          <table key={i}>
            <thead><tr>{(blk.head || []).map((h, j) => <th key={j}>{renderInline(h, "th" + i + "-" + j)}</th>)}</tr></thead>
            <tbody>{(blk.rows || []).map((r, j) => <tr key={j}>{r.map((c, k) => <td key={k}>{renderInline(c, "td" + i + "-" + j + "-" + k)}</td>)}</tr>)}</tbody>
          </table>
        );
      case "footnotes":
        return (
          <div key={i} className="footnotes">
            <ol>{(blk.items || []).map((f, j) => <li key={j} id={"fn" + (j + 1)}>{renderInline(f, "f" + i + "-" + j)}</li>)}</ol>
          </div>
        );
      default:
        return <p key={i}>{renderInline(blk.text || "", "p" + i)}</p>;
    }
  });
}

/* ----- Markdown body renderer -----
   Renders a Briefing's Markdown body (written in the CMS) to HTML using
   marked (loaded in index.html). Supports headings, paragraphs, bold,
   italics, lists, tables (GFM), blockquotes and links — everything an
   author needs to write a Briefing as a normal document. */
function MarkdownBody({ markdown, style }) {
  const html = React.useMemo(() => {
    const md = markdown || "";
    if (window.marked && typeof window.marked.parse === "function") {
      try { return window.marked.parse(md, { gfm: true, breaks: false }); }
      catch (e) { /* fall through */ }
    }
    /* Fallback if marked hasn't loaded: show paragraphs as plain text. */
    return md.split(/\n{2,}/).map((p) => "<p>" + p.replace(/</g, "&lt;") + "</p>").join("");
  }, [markdown]);
  if (!markdown) return null;
  return <div className="body-prose" style={style} dangerouslySetInnerHTML={{ __html: html }} />;
}

/* ----- Summary box ----- */
function SummaryBox({ children }) {
  return (
    <div className="summary">
      <div className="eyebrow" style={{ paddingTop: 8 }}>SUMMARY</div>
      <p style={{ marginTop: 10, marginBottom: 0 }}>{children}</p>
    </div>
  );
}

/* ----- Three key takeaways ----- */
function Takeaways({ items }) {
  return (
    <div className="takeaways">
      <div className="eyebrow">THREE KEY TAKEAWAYS</div>
      <ol>{items.map((t, i) => <li key={i}>{t}</li>)}</ol>
    </div>
  );
}

/* ----- Sign-up gate (inline, on Briefing detail) -----
   Lead-capture gate. Email-only at this stage — the four-field form
   loads after submit (or on the dedicated /sign-up page). The gate
   screens email against the three-tier blocklist. Personal addresses
   show an inline retype message; competitor firms get a full-panel
   polite rejection. */
function SignupGate({ onSubmit }) {
  const [email, setEmail] = useState("");
  const [touched, setTouched] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  const result = (touched || submitted) ? evaluateSignupEmail(email) : null;
  const isCompetitor = submitted && result?.kind === "competitor";
  const inlineError = result && !result.ok && result.kind !== "competitor" ? result.message : null;
  const handleSubmit = (e) => {
    e.preventDefault();
    setTouched(true);
    setSubmitted(true);
    const r = evaluateSignupEmail(email);
    if (!r.ok || r.kind === "competitor") return;
    onSubmit(email);
  };

  if (isCompetitor) {
    return (
      <div className="gate gate-rejected">
        <div className="eyebrow" style={{ color: "var(--brass)" }}>WITH REGRET</div>
        <div className="gate-title" style={{ marginTop: 6 }}>{result.message}</div>
        <p className="gate-sub">
          The library, podcast and call-booking are for people facing shipping and trade matters as part of their commercial role. If you'd like to discuss the content with Jonathan directly, he'd be happy to hear from you.
        </p>
        <p style={{ marginTop: 16 }}>
          <a href="mailto:the-bridge@fathom.law" className="btn btn-secondary">Email Jonathan directly</a>
        </p>
        <button className="btn btn-link" style={{ marginTop: 4 }} onClick={() => { setEmail(""); setSubmitted(false); setTouched(false); }}>
          Use a different email
        </button>
      </div>
    );
  }

  return (
    <div className="gate">
      <div className="gate-title">Sign up to keep reading</div>
      <p className="gate-sub">Free. Two minutes. The rest of this Briefing, every other Briefing, the Soundings podcast, and a calendar to book a call when you need one.</p>
      <form className="gate-form" onSubmit={handleSubmit} noValidate>
        <input
          className={"input" + (inlineError ? " input-error" : "")}
          placeholder="you@firm.com"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          onBlur={() => setTouched(true)}
          autoComplete="email"
          inputMode="email"
        />
        <button className="btn btn-primary" type="submit">Sign up — free</button>
      </form>
      {inlineError && <div className="gate-error">{inlineError}</div>}
      <p className="gate-consent">
        By signing up you agree we may email you about Briefings related to what you read, and occasionally to suggest a call when it looks useful. Unsubscribe in one click, any time.
      </p>
      <div className="gate-alt">Already a member? <a href="#" style={{ color: "var(--brass)", textDecoration: "underline", textDecorationColor: "color-mix(in oklab, var(--brass) 40%, transparent)" }}>Sign in</a></div>
    </div>
  );
}

/* ----- Author bio ----- */
function AuthorBio() {
  return (
    <div className="bio">
      <div className="avatar">JC</div>
      <div>
        <strong>Jonathan Cooke</strong>
        <em>Solicitor of England and Wales · Shipping and international trade</em>
        <a href="#">Speak to me about this →</a>
      </div>
    </div>
  );
}

/* ----- Soundings audio player ----- */
function AudioPlayer({ title }) {
  const [playing, setPlaying] = useState(false);
  return (
    <div className="audio">
      <button className="audio-play" onClick={() => setPlaying(p => !p)} aria-label={playing ? "Pause" : "Play"}>
        {playing ? "❚❚" : "▶"}
      </button>
      <div className="audio-track">
        <div className="audio-bar">
          <i style={{ width: "30%" }}></i>
          <b style={{ left: "30%" }}></b>
        </div>
        <div className="audio-time"><span>04:48</span><span>16:32</span></div>
      </div>
      <span className="audio-rate">1×</span>
    </div>
  );
}

/* ----- Listen-on row ----- */
function ListenRow() {
  return (
    <div className="listen-row">
      <span className="label-static">Listen on:</span>
      <a className="btn btn-ghost" href="#">Apple Podcasts</a>
      <a className="btn btn-ghost" href="#">Spotify</a>
      <a className="btn btn-ghost" href="#">RSS feed</a>
    </div>
  );
}

/* ----- Filter chip row ----- */
function ChipRow({ items, active, onPick }) {
  return (
    <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
      {items.map(c => (
        <button key={c} className={"chip" + (active === c ? " active" : "")} onClick={() => onPick(c)}>
          {c}
        </button>
      ))}
    </div>
  );
}

/* ----- Sign-up form (full, four fields) ----- */
function SignupForm({ onDone, prefillEmail = "" }) {
  const [email, setEmail] = useState(prefillEmail);
  const [touched, setTouched] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  const result = (touched || submitted) ? evaluateSignupEmail(email) : null;
  const isCompetitor = submitted && result?.kind === "competitor";
  const inlineError = result && !result.ok && result.kind !== "competitor" ? result.message : null;
  const handleSubmit = (e) => {
    e.preventDefault();
    setTouched(true);
    setSubmitted(true);
    const r = evaluateSignupEmail(email);
    if (!r.ok || r.kind === "competitor") return;
    onDone();
  };

  if (isCompetitor) {
    return (
      <div className="card gate-rejected" style={{ maxWidth: 480, margin: "0 auto" }}>
        <div className="eyebrow" style={{ color: "var(--brass)" }}>WITH REGRET</div>
        <h2 style={{ marginTop: 6 }}>{result.message}</h2>
        <p className="muted" style={{ marginTop: 8, fontSize: 14, lineHeight: 1.55 }}>
          The library, podcast and call-booking are for people facing shipping and trade matters as part of their commercial role. If you'd like to discuss the content with Jonathan directly, he'd be happy to hear from you.
        </p>
        <a href="mailto:the-bridge@fathom.law" className="btn btn-primary btn-block" style={{ marginTop: 16 }}>Email Jonathan directly</a>
        <button className="btn btn-link btn-block" style={{ marginTop: 8 }} onClick={() => { setEmail(""); setSubmitted(false); setTouched(false); }}>
          Use a different email
        </button>
      </div>
    );
  }

  return (
    <form className="card" style={{ maxWidth: 480, margin: "0 auto" }} onSubmit={handleSubmit} noValidate>
      <div className="eyebrow" style={{ color: "var(--brass)" }}>SIGN UP</div>
      <h2 style={{ marginTop: 4 }}>Sign up — free</h2>
      <p className="muted" style={{ marginTop: 6, fontSize: 14 }}>Two minutes. One screen. We'll never share your details.</p>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 16 }}>
        <div>
          <label className="label">Full name</label>
          <input className="input" placeholder="Jane Owens" autoComplete="name" />
        </div>
        <div>
          <label className="label">Work email</label>
          <input
            className={"input" + (inlineError ? " input-error" : "")}
            placeholder="jane@firm.com"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            onBlur={() => setTouched(true)}
            autoComplete="email"
          />
          {inlineError && <div className="field-error">{inlineError}</div>}
        </div>
        <div>
          <label className="label">Firm</label>
          <input className="input" placeholder="Acme Trading Ltd" autoComplete="organization" />
        </div>
        <div>
          <label className="label">Role · location</label>
          <input className="input" placeholder="Operations, London" />
        </div>
      </div>
      <p className="muted" style={{ fontSize: 12, lineHeight: 1.5, marginTop: 14 }}>
        By signing up you agree we may email you about Briefings related to what you read, and occasionally to suggest a call when it looks useful. Unsubscribe in one click, any time. <a href="#" style={{ color: "var(--brass)" }}>Privacy</a>.
      </p>
      <label style={{ display: "flex", gap: 8, alignItems: "flex-start", marginTop: 12, fontSize: 13 }}>
        <input type="checkbox" />
        <span>Also send me <strong>The Logbook</strong> — monthly digest of new Briefings and decisions.</span>
      </label>
      <button type="submit" className="btn btn-primary btn-block" style={{ marginTop: 16 }}>Create account</button>
    </form>
  );
}

Object.assign(window, {
  Wordmark, MedallionReversed,
  Nav, Footer, Hero,
  BriefingCard, CtaRail, CtaBar,
  SummaryBox, Takeaways, SignupGate, SignupForm,
  AuthorBio, AudioPlayer, ListenRow, ChipRow,
  renderInline, BriefingBody, MarkdownBody,
});
