// SQSEO marketing, free tools kit (Phase 9). Real, working, no-signup tools that
// double as a programmatic-SEO surface and funnel into the app:
//   ToolsHub     — the /tools directory
//   SerpPreview  — Google snippet preview (client-only, pixel-accurate truncation)
//   LlmsTxtGen   — /llms.txt generator (client-only)
//   PageChecker  — title/meta/H1/H2/canonical/OG + AI-crawler access (POST /api/tools/page-signals)
// (GeoAudit + BrandBrief live in their own files and are reused on tool pages.)
(function init(){
if(!window.LongtailIQDesignSystem_ae8f12 || !window.LTQM){return setTimeout(init,30);}
const React = window.React;
const DS = window.LongtailIQDesignSystem_ae8f12;
const { Icon, Button } = DS;
const M = window.LTQM;

// ---- shared bits ----
function ToolHero({ pill, pillIcon, title, gray, sub }) {
  const gridMask = "radial-gradient(125% 90% at 50% 0%, #000 38%, transparent 82%)";
  return (
    <header style={{ position: "relative", overflow: "hidden", background: "var(--grad-mesh)", borderBottom: "1px solid var(--border-subtle)" }}>
      <div className="fx-bg-grid" aria-hidden style={{ position: "absolute", inset: 0, pointerEvents: "none", WebkitMask: gridMask, mask: gridMask, opacity: 0.85 }} />
      <div className="fx-bg-glow" aria-hidden style={{ position: "absolute", inset: 0, pointerEvents: "none", opacity: 0.7 }} />
      <div style={{ position: "relative", maxWidth: 820, margin: "0 auto", padding: "72px 24px 36px", textAlign: "center" }}>
        <div className="lt-reveal" style={{ marginBottom: 16 }}><span className="lt-pill-label"><Icon name={pillIcon} size={14} style={{ color: "var(--text-muted)" }} />{pill}</span></div>
        <h1 className="lt-reveal lt-display" style={{ fontFamily: "var(--font-display)", fontSize: "clamp(2rem,1.5rem+1.8vw,2.9rem)", fontWeight: 700, letterSpacing: "-0.03em", lineHeight: 1.05, color: "var(--ink-900)", maxWidth: 760, margin: "0 auto" }}>
          {title}{gray && <><br/><span style={{ color: "var(--ink-400)" }}>{gray}</span></>}
        </h1>
        {sub && <p className="lt-reveal" style={{ fontSize: 16.5, color: "var(--text-muted)", marginTop: 16, lineHeight: 1.6, maxWidth: 580, marginLeft: "auto", marginRight: "auto" }}>{sub}</p>}
      </div>
    </header>
  );
}
const Section = ({ children, style }) => <section style={{ maxWidth: 820, margin: "0 auto", padding: "40px 24px 72px", ...style }}>{children}</section>;
const field = { width: "100%", height: 46, padding: "0 13px", borderRadius: "var(--r-md)", background: "var(--surface-card)", border: "1px solid var(--border-strong)", fontFamily: "var(--font-sans)", fontSize: 14.5, color: "var(--text-strong)", outline: "none" };
const Label = ({ children }) => <div style={{ fontSize: 12.5, fontWeight: 600, color: "var(--text-body)", marginBottom: 6 }}>{children}</div>;
const SpinnerRow = ({ text }) => (
  <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 12, padding: "36px 0", color: "var(--text-muted)" }}>
    <span className="lt-spinner" style={{ display: "inline-block", width: 20, height: 20, border: "2px solid var(--accent-200)", borderTopColor: "var(--accent-500)", borderRadius: 999 }} /><span style={{ fontSize: 14 }}>{text}</span>
  </div>
);

// ============================ Tools hub ============================
const TOOLS = [
  { id: "geo-audit", icon: "radar", t: "Free GEO Audit", d: "Score how well ChatGPT, Perplexity, Gemini & Google AI Overviews can cite your page across 13 signals.", href: "/tools/geo-audit", live: true },
  { id: "serp-preview", icon: "search", t: "SERP Snippet Preview", d: "See exactly how your title and meta description render in Google, with pixel-accurate truncation.", href: "/tools/serp-preview", live: true },
  { id: "page-checker", icon: "list-checks", t: "Page & AI-crawler Checker", d: "Check title, meta, H1s, canonical and whether robots.txt blocks the AI crawlers.", href: "/tools/page-checker", live: true },
  { id: "llms-txt", icon: "file-code", t: "llms.txt Generator", d: "Generate a clean /llms.txt to point LLMs at your best content (the emerging standard).", href: "/tools/llms-txt", live: true },
  { id: "brief", icon: "wand-sparkles", t: "Live Content Brief", d: "Drop in a domain and get a real content brief written for your top opportunity.", href: "/#demo", live: true },
  { id: "keywords", icon: "layers", t: "Longtail Keyword Tool", d: "Turn one seed into hundreds of scored longtails and AI-search prompts. Free forever.", href: "/app/register.html", live: true },
];
function ToolCard({ tool }) {
  const [hover, setHover] = React.useState(false);
  const onMove = M.useSpotlight ? M.useSpotlight() : undefined;
  return (
    <a href={tool.href} className="fx-spotlight" onMouseMove={onMove} onMouseEnter={()=>setHover(true)} onMouseLeave={()=>setHover(false)}
      style={{ display: "block", height: "100%", position: "relative", border: "1px solid var(--border-subtle)", borderRadius: "var(--r-lg)", padding: 20, background: "var(--paper)", textDecoration: "none",
        transition: "border-color var(--dur-base), transform var(--dur-base)", borderColor: hover ? "var(--ink-200)" : "var(--border-subtle)", transform: hover ? "translateY(-2px)" : "none" }}>
      <span style={{ position: "relative", zIndex: 2, display: "inline-grid", placeItems: "center", width: 40, height: 40, borderRadius: 11, background: "var(--accent-50)", color: "var(--accent-600)", marginBottom: 14 }}><Icon name={tool.icon} size={19} strokeWidth={1.8} /></span>
      <div style={{ position: "relative", zIndex: 2, fontSize: 15.5, fontWeight: 700, color: "var(--text-strong)", letterSpacing: "-0.01em", marginBottom: 6 }}>{tool.t}</div>
      <p style={{ position: "relative", zIndex: 2, fontSize: 13, color: "var(--text-muted)", lineHeight: 1.55, marginBottom: 12 }}>{tool.d}</p>
      <span style={{ position: "relative", zIndex: 2, display: "inline-flex", alignItems: "center", gap: 5, fontSize: 12.5, fontWeight: 600, color: "var(--accent-600)" }}>Open tool <Icon name="arrow-right" size={13} /></span>
    </a>
  );
}
function ToolsHub() {
  const RG = M.RevealGroup || (({ children, className, style }) => <div className={className} style={style}>{children}</div>);
  return (
    <div>
      <ToolHero pill="Free tools · no signup" pillIcon="wrench" title="Free SEO & GEO tools" gray="for the AI-search era."
        sub="Practical, no-nonsense tools to get your site found and cited, in Google and in AI answers. No login, nothing stored." />
      <Section style={{ maxWidth: 1080 }}>
        <RG stagger={70} className="lt-src-grid" style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 16 }} childStyle={{ height: "100%" }}>
          {TOOLS.map((t) => <ToolCard key={t.id} tool={t} />)}
        </RG>
        <div style={{ textAlign: "center", marginTop: 36 }}>
          <M.Magnetic strength={0.4}><Button variant="primary" size="lg" className="fx-sheen" trailingIcon="arrow-right" onClick={()=>{window.location.href="/app/register.html";}}>Open the full toolkit free</Button></M.Magnetic>
        </div>
      </Section>
    </div>
  );
}

// ============================ SERP snippet preview (client) ============================
let _canvas;
function measure(text, font) {
  if (typeof document === "undefined") return text.length * 8;
  _canvas = _canvas || document.createElement("canvas");
  const ctx = _canvas.getContext("2d");
  ctx.font = font;
  return ctx.measureText(text).width;
}
function truncateToPx(text, px, font) {
  if (measure(text, font) <= px) return text;
  let t = text;
  while (t.length > 1 && measure(t + "…", font) > px) t = t.slice(0, -1);
  return t.replace(/\s+\S*$/, "") + "…";
}
function SerpPreview() {
  const [title, setTitle] = React.useState("Organic hair oil for postpartum hair loss: a complete guide");
  const [desc, setDesc] = React.useState("A clear, dermatologist-reviewed guide to using organic hair oil for postpartum shedding: what works, how to apply it, and when to see a doctor.");
  const [url, setUrl] = React.useState("https://example.com/blog/organic-hair-oil-postpartum");
  const TITLE_PX = 600, DESC_PX = 920;
  const titlePx = Math.round(measure(title, "20px Arial"));
  const descPx = Math.round(measure(desc, "14px Arial"));
  const shownTitle = truncateToPx(title, TITLE_PX, "20px Arial");
  const shownDesc = truncateToPx(desc, DESC_PX * 2, "14px Arial");
  let crumb = url; try { const u = new URL(url); crumb = u.hostname.replace(/^www\./, "") + (u.pathname === "/" ? "" : " › " + u.pathname.split("/").filter(Boolean).join(" › ")); } catch (e) {}
  const Warn = ({ ok, over, under }) => (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 12, fontWeight: 600, color: ok ? "var(--viz-green)" : "var(--amber-600)" }}>
      <Icon name={ok ? "check" : "alert-triangle"} size={12} />{ok ? "Good length" : over ? "Too long, will be cut" : under}
    </span>
  );
  return (
    <div>
      <ToolHero pill="Free tool · no signup" pillIcon="search" title="SERP Snippet Preview" gray="title + meta, pixel-perfect."
        sub="See exactly how your page renders in Google before you publish. Truncation is measured in pixels, just like Google does it." />
      <Section>
        <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 16 }}>
          <div><Label>Page title</Label><input value={title} onChange={(e)=>setTitle(e.target.value)} style={field} />
            <div style={{ marginTop: 6, display: "flex", gap: 14 }}><Warn ok={titlePx <= TITLE_PX && titlePx >= 200} over={titlePx > TITLE_PX} under="A little short" /><span style={{ fontSize: 12, color: "var(--text-faint)" }}>{title.length} chars · ~{titlePx}px / {TITLE_PX}px</span></div></div>
          <div><Label>Meta description</Label><textarea value={desc} onChange={(e)=>setDesc(e.target.value)} rows={3} style={{ ...field, height: "auto", padding: "10px 13px", lineHeight: 1.5, resize: "vertical" }} />
            <div style={{ marginTop: 6, display: "flex", gap: 14 }}><Warn ok={descPx <= DESC_PX * 2 && desc.length >= 70} over={descPx > DESC_PX * 2} under="A little short" /><span style={{ fontSize: 12, color: "var(--text-faint)" }}>{desc.length} chars</span></div></div>
          <div><Label>Page URL</Label><input value={url} onChange={(e)=>setUrl(e.target.value)} style={field} /></div>
        </div>
        {/* the preview */}
        <div style={{ marginTop: 26 }}>
          <Label>Google preview</Label>
          <div style={{ border: "1px solid var(--border-subtle)", borderRadius: "var(--r-lg)", background: "#fff", padding: "18px 20px", maxWidth: 600 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }}>
              <span style={{ width: 26, height: 26, borderRadius: 999, background: "var(--ink-100)", display: "grid", placeItems: "center", flex: "none" }}><Icon name="globe" size={14} style={{ color: "var(--text-muted)" }} /></span>
              <span style={{ fontSize: 12.5, color: "#202124", lineHeight: 1.2 }}>{crumb}</span>
            </div>
            <div style={{ color: "#1a0dab", fontSize: 20, lineHeight: 1.3, fontFamily: "Arial, sans-serif", marginTop: 2 }}>{shownTitle}</div>
            <div style={{ color: "#4d5156", fontSize: 14, lineHeight: 1.58, fontFamily: "Arial, sans-serif", marginTop: 3 }}>{shownDesc}</div>
          </div>
        </div>
        <p style={{ fontSize: 12.5, color: "var(--text-faint)", marginTop: 14 }}>Google rewrites titles/descriptions ~30% of the time, this previews your intended snippet. Aim for a title under ~600px and a description of ~120-158 characters.</p>
      </Section>
    </div>
  );
}

// ============================ llms.txt generator (client) ============================
function LlmsTxtGen() {
  const [name, setName] = React.useState("");
  const [summary, setSummary] = React.useState("");
  const [rows, setRows] = React.useState([{ title: "", url: "", note: "" }, { title: "", url: "", note: "" }]);
  const setRow = (i, k, v) => setRows((r) => r.map((x, j) => (j === i ? { ...x, [k]: v } : x)));
  const addRow = () => setRows((r) => [...r, { title: "", url: "", note: "" }]);
  const out = React.useMemo(() => {
    const L = [];
    L.push("# " + (name.trim() || "Your site"));
    L.push("");
    if (summary.trim()) { L.push("> " + summary.trim()); L.push(""); }
    const links = rows.filter((r) => r.title.trim() && r.url.trim());
    if (links.length) {
      L.push("## Key pages");
      for (const r of links) L.push("- [" + r.title.trim() + "](" + r.url.trim() + ")" + (r.note.trim() ? ": " + r.note.trim() : ""));
    }
    return L.join("\n") + "\n";
  }, [name, summary, rows]);
  const copy = () => { try { navigator.clipboard.writeText(out); } catch (e) {} M.toast && M.toast("Copied llms.txt"); };
  const download = () => {
    const blob = new Blob([out], { type: "text/plain;charset=utf-8" });
    const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.download = "llms.txt";
    document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(a.href), 1500);
  };
  return (
    <div>
      <ToolHero pill="Free tool · no signup" pillIcon="file-code" title="llms.txt Generator" gray="guide LLMs to your best content."
        sub="llms.txt is an emerging standard that points AI assistants at the pages you want them to read and cite. Fill this in and drop the file at yoursite.com/llms.txt." />
      <Section>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 22 }} className="lt-feat-grid">
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <div><Label>Site name</Label><input value={name} onChange={(e)=>setName(e.target.value)} placeholder="Acme Analytics" style={field} /></div>
            <div><Label>One-line summary</Label><input value={summary} onChange={(e)=>setSummary(e.target.value)} placeholder="Privacy-first product analytics for SaaS teams." style={field} /></div>
            <Label>Key pages</Label>
            {rows.map((r, i) => (
              <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                <input value={r.title} onChange={(e)=>setRow(i,"title",e.target.value)} placeholder="Page title" style={{ ...field, height: 40, fontSize: 13.5 }} />
                <input value={r.url} onChange={(e)=>setRow(i,"url",e.target.value)} placeholder="https://…" style={{ ...field, height: 40, fontSize: 13.5 }} />
                <input value={r.note} onChange={(e)=>setRow(i,"note",e.target.value)} placeholder="Short note (optional)" style={{ ...field, height: 40, fontSize: 13.5, gridColumn: "1 / -1" }} />
              </div>
            ))}
            <button onClick={addRow} style={{ alignSelf: "flex-start", border: "1px dashed var(--border-strong)", background: "transparent", borderRadius: 8, padding: "8px 12px", fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, color: "var(--text-body)", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 6 }}><Icon name="plus" size={14} /> Add page</button>
          </div>
          <div>
            <Label>Your llms.txt</Label>
            <pre className="lt-scroll" style={{ margin: 0, height: 300, overflow: "auto", padding: 16, background: "var(--ink-950)", color: "#E6E6EA", borderRadius: "var(--r-lg)", fontFamily: "var(--font-mono)", fontSize: 12.5, lineHeight: 1.6, whiteSpace: "pre-wrap" }}>{out}</pre>
            <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
              <Button variant="primary" leadingIcon="download" onClick={download}>Download llms.txt</Button>
              <Button variant="secondary" leadingIcon="clipboard" onClick={copy}>Copy</Button>
            </div>
          </div>
        </div>
      </Section>
    </div>
  );
}

// ============================ page & AI-crawler checker (backend) ============================
function CheckRow({ label, ok, detail }) {
  return (
    <div style={{ display: "flex", alignItems: "flex-start", gap: 12, padding: "12px 0", borderTop: "1px solid var(--border-subtle)" }}>
      <Icon name={ok ? "check-circle-2" : "alert-circle"} size={18} style={{ color: ok ? "var(--viz-green)" : "var(--amber-600)", flex: "none", marginTop: 1 }} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14, fontWeight: 600, color: "var(--text-strong)" }}>{label}</div>
        <div style={{ fontSize: 13, color: "var(--text-muted)", marginTop: 2, lineHeight: 1.5, wordBreak: "break-word" }}>{detail}</div>
      </div>
    </div>
  );
}
function PageChecker() {
  const [url, setUrl] = React.useState("");
  const [phase, setPhase] = React.useState("idle");
  const [d, setD] = React.useState(null);
  const run = async (e) => {
    e && e.preventDefault();
    if (url.trim().length < 3 || phase === "loading") return;
    setPhase("loading");
    try {
      const res = await fetch("/api/tools/page-signals", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: url.trim() }) });
      const j = await res.json().catch(() => ({}));
      setD(res.ok ? j : { ok: false, error: j.message || "Enter a valid URL." });
    } catch (err) { setD({ ok: false, error: "Could not reach the server." }); }
    setPhase("done");
  };
  return (
    <div>
      <ToolHero pill="Free tool · no signup" pillIcon="list-checks" title="Page & AI-crawler Checker" gray="the on-page basics, checked."
        sub="Paste a URL. We check the title, meta description, headings, canonical, and whether your robots.txt is blocking the AI crawlers that decide if you get cited." />
      <Section>
        <form onSubmit={run} style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, flex: "1 1 300px", minWidth: 240, height: 50, padding: "0 14px", background: "var(--surface-card)", border: "1px solid var(--border-strong)", borderRadius: "var(--r-md)", boxShadow: "var(--shadow-xs)" }}>
            <Icon name="globe" size={18} style={{ color: "var(--accent-500)", flex: "none" }} />
            <input value={url} onChange={(e)=>setUrl(e.target.value)} type="text" inputMode="url" placeholder="yourdomain.com/page" aria-label="Page URL" style={{ flex: 1, minWidth: 0, height: 46, border: "none", outline: "none", background: "transparent", fontFamily: "var(--font-sans)", fontSize: 15, fontWeight: 500, color: "var(--text-strong)" }} />
          </div>
          <Button variant="primary" size="lg" className="fx-sheen" leadingIcon={phase==="loading"?undefined:"list-checks"} disabled={phase==="loading"||url.trim().length<3} onClick={run}>{phase==="loading"?"Checking…":"Check page"}</Button>
        </form>
        {phase === "loading" && <SpinnerRow text="Fetching the page and its robots.txt…" />}
        {phase === "done" && d && !d.ok && <div style={{ marginTop: 18, padding: "14px 16px", border: "1px solid var(--border-strong)", borderRadius: "var(--r-lg)", fontSize: 14, color: "var(--text-body)" }}>{d.error}</div>}
        {phase === "done" && d && d.ok && (
          <div className="fx-enter" style={{ marginTop: 20, border: "1px solid var(--border-subtle)", borderRadius: "var(--r-xl)", background: "var(--surface-card)", boxShadow: "var(--shadow-md)", padding: "6px 20px 16px" }}>
            <CheckRow ok={d.titleLen >= 15 && d.titleLen <= 65} label={"Title (" + d.titleLen + " chars)"} detail={d.title || "No <title> found"} />
            <CheckRow ok={d.metaLen >= 70 && d.metaLen <= 165} label={"Meta description (" + d.metaLen + " chars)"} detail={d.metaDesc || "No meta description found"} />
            <CheckRow ok={d.h1Count === 1} label={"H1 (" + d.h1Count + ")"} detail={d.h1s && d.h1s.length ? d.h1s.join("  ·  ") : "No H1 found"} />
            <CheckRow ok={d.h2Count >= 2} label={"H2 sections (" + d.h2Count + ")"} detail={d.h2Count >= 2 ? "Good structure for extractable passages." : "Add H2 sections so engines can lift a clean answer."} />
            <CheckRow ok={d.canonical} label="Canonical URL" detail={d.canonical ? "Present." : "Add <link rel=canonical>."} />
            <CheckRow ok={d.ogTitle} label="Open Graph" detail={d.ogTitle ? "og:title present." : "Add Open Graph tags for clean shared/cited snippets."} />
            <CheckRow ok={!d.aiBlocked} label="AI crawlers" detail={d.aiBlocked ? "robots.txt blocks an AI crawler (GPTBot / Google-Extended / PerplexityBot). You can't be cited." : (d.hasRobots ? "Not blocked in robots.txt." : "No robots.txt found (crawlers allowed by default).")} />
            <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap", marginTop: 16 }}>
              <M.Magnetic strength={0.4}><Button variant="primary" size="md" className="fx-sheen" trailingIcon="arrow-right" onClick={()=>{window.location.href="/tools/geo-audit";}}>Run the full GEO audit</Button></M.Magnetic>
              <span style={{ fontSize: 12.5, color: "var(--text-faint)" }}>{d.host}</span>
            </div>
          </div>
        )}
      </Section>
    </div>
  );
}

window.LTQM = window.LTQM || {};
Object.assign(window.LTQM, { ToolsHub, SerpPreview, LlmsTxtGen, PageChecker });
})();
