// 37 FORGE — Knowledge Base
// Landing chooser → two libraries:
//   • "public" (Articles / Guides & Insights) — open, prospect / SEO-GEO content with summaries.
//   • "client" (Troubleshooting) — gated: subscribe popup unlocks the library on selection.
// Each library's search bar is revealed only AFTER the user picks it.

const KB_SH = (props) => React.createElement(window.SectionHead, props);
const KB_SUB_KEY = "kb37_sub";

// ─────────────────────────────────────────────────────────────
// TROUBLESHOOTING ACCESS CONTROL — edit these two values.
//   • KB_ACCESS_CODE: the code you give clients (case-insensitive).
//   • KB_CLIENT_DOMAINS: work-email domains that unlock automatically,
//     e.g. ["acmeclinic.com", "example.com"]. Leave [] to require the code.
// A visitor unlocks if their email domain is on the list OR the code matches.
// NOTE: this deters non-clients but is not real security (it runs in the
// browser). For true locking, use SSO / Cloudflare Access / a client portal.
// ─────────────────────────────────────────────────────────────
const KB_ACCESS_CODE = "FORGE-CLIENT";
const KB_CLIENT_DOMAINS = [];
const kbDomainOf = (email) => (email.split("@")[1] || "").trim().toLowerCase();
const kbClientAllowed = (email, code) => {
  const domainOk = KB_CLIENT_DOMAINS.map((d) => d.toLowerCase()).includes(kbDomainOf(email));
  const codeOk = !!code && code.trim().toLowerCase() === KB_ACCESS_CODE.toLowerCase();
  return domainOk || codeOk;
};

// Articles (public) marketing capture — SEPARATE from the client Troubleshooting gate.
const KB_READER_KEY = "kb37_reader";
const KB_READS_KEY = "kb37_reads";
const KB_FREE_READS = 2; // free article reads before we ask once (then remembered)
const kbReaderKnown = () => {
  try { const s = JSON.parse(localStorage.getItem(KB_READER_KEY) || "null"); return !!(s && s.email); } catch (e) { return false; }
};
const kbReadCount = () => {
  try { return parseInt(localStorage.getItem(KB_READS_KEY) || "0", 10) || 0; } catch (e) { return 0; }
};
const kbIsSubscribed = () => {
  try { const s = JSON.parse(localStorage.getItem(KB_SUB_KEY) || "null"); return !!(s && s.email); } catch (e) { return false; }
};

// ---- CLIENT (troubleshooting / how-to) — gated, client access only ----
const KB_CATEGORIES = [
  { key: "getting-started", label: "Getting Started", icon: "fa-rocket", blurb: "Onboarding, first-day setup, and the basics." },
  { key: "security", label: "Security & Phishing", icon: "fa-shield-halved", blurb: "Spotting threats, MFA, and staying safe." },
  { key: "m365", label: "Email & Microsoft 365", icon: "fa-envelope", blurb: "Outlook, Teams, OneDrive, and licensing." },
  { key: "backups", label: "Backups & Recovery", icon: "fa-database", blurb: "Restores, file recovery, and DR drills." },
  { key: "networking", label: "Networking & Wi-Fi", icon: "fa-wifi", blurb: "VPN, Wi-Fi, and connection issues." },
  { key: "devices", label: "Devices & Troubleshooting", icon: "fa-laptop-medical", blurb: "Printers, performance, and quick fixes." },
  { key: "voip", label: "VoIP & Phones", icon: "fa-phone-volume", blurb: "Desk phones, softphones, and voicemail." },
  { key: "accounts", label: "Accounts & Access", icon: "fa-key", blurb: "Passwords, permissions, and provisioning." }
];

const KB_ARTICLES = [
  { t: "How to set up multi-factor authentication (MFA)", intro: "Multi-factor authentication adds a second check after your password, so a stolen password alone can't get in. Setup takes about 5 minutes.", steps: ["On your computer, sign in to office.com and open My Account, then Security info.","Choose Add sign-in method, then Authenticator app. Install Microsoft Authenticator on your phone if you don't have it.","In the app, tap +, then Work or school account, then Scan a QR code, and scan the code on your screen.","Approve the test notification the app sends to confirm it works.","Set the Authenticator as your default method so future sign-ins just need a tap.","From now on, approve the phone prompt whenever you sign in on a new device."], cat: "security", tags: ["mfa", "authenticator", "login", "2fa"], mins: 4, updated: "May 2026" },
  { t: "Spotting a phishing email: the 7-point checklist", intro: "Most attacks start with a convincing email. Run any suspicious message through this checklist before you click or reply.", steps: ["Sender: does the address really match the company, or is it a look-alike domain?","Tone: is it pushing urgency or fear, like act now or account suspended?","Links: hover over them. Does the real URL match what it claims?","Attachments: were you expecting a file? Unexpected invoices and zips are red flags.","Requests: is it asking for a password, payment, or gift cards? Legitimate teams won't.","Grammar: odd phrasing or generic greetings like Dear Customer are warning signs.","When in doubt, don't click. Forward it to the help desk and we'll check it."], cat: "security", tags: ["phishing", "scam", "email", "fraud"], mins: 5, updated: "May 2026" },
  { t: "What to do if you clicked a suspicious link", intro: "Acting fast limits the damage. If you clicked something you shouldn't have, do this right away.", steps: ["Disconnect from the internet by turning off Wi-Fi or unplugging the network cable.","Do not enter any passwords or information on the page that opened.","If you typed your password anywhere, change it from a different, trusted device.","Call the help desk immediately. Speed matters more than embarrassment.","Don't delete the email. We may need it to see what happened.","Watch for follow-up messages pretending to be IT, and verify before acting."], cat: "security", tags: ["phishing", "incident", "malware", "report"], mins: 3, updated: "Apr 2026" },
  { t: "Resetting your password (self-service portal)", intro: "You can reset your own password any time using the self-service portal, with no waiting on a ticket.", steps: ["Go to passwordreset.microsoftonline.com, or click Can't access your account on the sign-in page.","Enter your work email and complete the captcha.","Verify your identity with the method you set up (phone or Authenticator).","Choose a new password that's long and unique. A passphrase works well.","Sign in everywhere with the new password, including your phone's mail app.","If you're locked out of your verification methods, call the help desk."], cat: "accounts", tags: ["password", "reset", "login", "locked out"], mins: 2, updated: "May 2026" },
  { t: "Requesting access to a shared folder or app", intro: "Need a folder, app, or shared mailbox? Here's the fastest way to get access approved.", steps: ["Identify exactly what you need: the folder path, app name, or mailbox.","Note why you need it. Approvals are quicker with a reason.","Open a help desk ticket (portal, email, or Teams) with those details.","Your manager or the data owner approves the request.","We grant the access and confirm, usually the same day.","Test it and reply on the ticket if anything is still missing."], cat: "accounts", tags: ["access", "permissions", "request", "shared"], mins: 3, updated: "Mar 2026" },
  { t: "New employee setup: what IT needs from you", intro: "Give us a few details ahead of a new hire's start date and they'll be productive on day one.", steps: ["Send us the new hire's full name, start date, job title, and manager.","Tell us which apps and shared resources they'll need (matching a similar role is easiest).","Let us know the hardware: new laptop, reassigned device, or personal device.","Confirm the email address format and any distribution lists to add them to.","We provision the account, license, mailbox, and access before day one.","On their first morning, they sign in and we walk them through setup."], cat: "getting-started", tags: ["onboarding", "new hire", "setup", "provisioning"], mins: 4, updated: "Apr 2026" },
  { t: "Your first day: logging in and finding help", intro: "Welcome aboard. Here's how to get logged in and find help on your first day.", steps: ["Sign in to your computer with the temporary password IT provided.","Set up multi-factor authentication when prompted (see the MFA guide).","Open Outlook and Teams. They sign in automatically once you're logged in.","Bookmark office.com for email, files, and apps from any browser.","Save the help desk number and email so you can reach us fast.","Stuck on anything? Open a ticket. No question is too small."], cat: "getting-started", tags: ["onboarding", "login", "help desk", "basics"], mins: 3, updated: "Feb 2026" },
  { t: "Connecting to the company VPN", intro: "The VPN gives you a secure connection to company resources when you're working remotely.", steps: ["Open the VPN client we installed, or install it from the link in your welcome email.","Enter your work email and password when prompted.","Approve the MFA prompt on your phone.","Wait for the Connected status before opening company apps or files.","Disconnect when you're done to free up the connection.","If it won't connect, check your home internet first, then call us."], cat: "networking", tags: ["vpn", "remote", "connection", "work from home"], mins: 4, updated: "May 2026" },
  { t: "Fixing slow or dropped Wi-Fi", intro: "Slow or dropping Wi-Fi is usually fixable in a couple of minutes. Work through these in order.", steps: ["Check whether other devices are slow too. If so, it's the network, not your laptop.","Move closer to the access point or router. Thick walls kill signal.","Turn Wi-Fi off and back on, or forget the network and reconnect.","Restart your laptop to re-establish a clean connection.","Unplug the router or modem for 30 seconds if the whole office is affected.","Still slow after that? Open a ticket with the time and location."], cat: "networking", tags: ["wifi", "wireless", "slow", "connection"], mins: 5, updated: "Mar 2026" },
  { t: "Setting up Outlook on a new computer", intro: "Setting up Outlook on a new machine takes a couple of minutes once you're signed in to Windows.", steps: ["Open Outlook from the Start menu.","It should detect your work email automatically. Confirm it and click Connect.","Enter your password and approve the MFA prompt.","Let Outlook download your mail. Large mailboxes take a few minutes.","Set your signature under File, Options, Mail, Signatures.","If it doesn't auto-detect, type your full work email and try again, or call us."], cat: "m365", tags: ["outlook", "email", "setup", "microsoft 365"], mins: 4, updated: "May 2026" },
  { t: "Recovering a deleted email in Outlook", intro: "Deleted something by mistake? Outlook keeps it recoverable for a while.", steps: ["In Outlook, open the Deleted Items folder and look for the message.","Right-click it and choose Move, then Inbox or the right folder.","If it's not there, click Recover items recently removed from this folder at the top.","Select the messages and choose Restore.","Check the Junk folder too, in case it was filtered.","Emptied everything and still missing it? Open a ticket. We can often recover further back."], cat: "m365", tags: ["outlook", "recover", "deleted", "email"], mins: 3, updated: "Apr 2026" },
  { t: "Sharing files securely with OneDrive & SharePoint", intro: "Share files the safe way so the right people get access and nothing leaks.", steps: ["Save the file to OneDrive or a SharePoint or Teams site, not your desktop.","Right-click the file and choose Share.","Set the permission. Specific people is safest; avoid Anyone with the link for sensitive files.","Choose view or edit, and add an expiration date for outside parties if needed.","Enter the recipient's email and send. They get a secure link.","Review or revoke access anytime under Manage access."], cat: "m365", tags: ["onedrive", "sharepoint", "share", "files"], mins: 5, updated: "Mar 2026" },
  { t: "Joining and scheduling a Microsoft Teams meeting", intro: "Scheduling and joining a Teams meeting is quick once you know where to click.", steps: ["In Teams or Outlook, open your calendar and click New meeting.","Add a title, the attendees, and a time, then Send.","Attendees get an invite with a Join button.","To join, click Join in the invite or the calendar event a few minutes early.","Check your camera and mic in the preview, then click Join now.","Use the toolbar to mute, share your screen, or chat during the call."], cat: "m365", tags: ["teams", "meeting", "video", "calendar"], mins: 4, updated: "Feb 2026" },
  { t: "Restoring a file from backup", intro: "Lost or changed a file you needed? Most files can be restored quickly from backup.", steps: ["Note the file name, where it lived, and roughly when it was last good.","Check OneDrive or SharePoint version history first: right-click, then Version history.","For files on a server share, ask us for a point-in-time restore.","Open a help desk ticket with those details (name, location, date).","We restore the file and confirm where to find it.","Verify it's the right version before deleting any duplicates."], cat: "backups", tags: ["backup", "restore", "recover", "file"], mins: 4, updated: "May 2026" },
  { t: "Understanding RPO and RTO in plain English", intro: "Two terms come up whenever we talk backups and recovery. Here's what they mean in plain English.", steps: ["RPO (Recovery Point Objective): how much recent data you could lose, measured in time.","A 15-minute RPO means backups run often enough that you'd lose at most 15 minutes of work.","RTO (Recovery Time Objective): how long it takes to be back up and running after a failure.","A 4-hour RTO means critical systems are restored within four hours.","Lower numbers cost more, so we right-size them to what your business actually needs.","Your runbook lists the agreed RPO and RTO for each system."], cat: "backups", tags: ["rpo", "rto", "disaster recovery", "backup"], mins: 6, updated: "Jan 2026" },
  { t: "My computer is running slow — first steps", intro: "A sluggish computer is often a 5-minute fix. Try these before opening a ticket.", steps: ["Restart it. A proper restart clears most slowdowns.","Close apps and browser tabs you're not using. They eat memory.","Check for updates in Windows Update, install pending ones, then restart.","Make sure you have free disk space. Under 10% free will crawl.","Run a quick virus scan if it started suddenly.","Still slow after a restart? Open a ticket with what's slow and when it started."], cat: "devices", tags: ["slow", "performance", "computer", "troubleshooting"], mins: 5, updated: "Apr 2026" },
  { t: "Adding and troubleshooting a printer", intro: "Most printer problems come down to connection or queue issues. Work through these.", steps: ["Check the basics: powered on, paper loaded, no error lights, cables or Wi-Fi connected.","Make sure you're sending to the right printer, not a save-as-PDF or an old device.","Open the print queue, cancel stuck jobs, and try again.","Restart the printer. If it's shared, restart your computer too.","If it's not listed, ask us to add it and we'll send the correct driver or queue.","Note the printer name and location in your ticket so we can find it fast."], cat: "devices", tags: ["printer", "printing", "setup", "troubleshooting"], mins: 4, updated: "Mar 2026" },
  { t: "Forwarding calls and setting up voicemail", intro: "Set up your voicemail and call forwarding from your desk phone or the app.", steps: ["To record a greeting, press the voicemail or message button and follow the prompts.","Set a PIN you'll remember. You'll need it to check messages remotely.","Choose voicemail-to-email so messages land in your inbox as well.","To forward calls, use the Forward option on the phone or in the app.","Pick where calls go: another number, a coworker, or straight to voicemail.","Test it by calling your line, then turn forwarding off when you're back."], cat: "voip", tags: ["voip", "voicemail", "forward", "phone"], mins: 3, updated: "May 2026" },
  { t: "Using the softphone app on your cell phone", intro: "The softphone app turns your cell phone into your work line: same number, anywhere.", steps: ["Install the app we use (the link is in your setup email) from the App Store or Google Play.","Sign in with the work credentials we provided.","Allow microphone and notification permissions so calls ring through.","Make a test call to confirm audio works both ways.","Calls you make show your work number, not your personal one.","Set Do Not Disturb in the app for after hours so work calls don't ring late."], cat: "voip", tags: ["voip", "softphone", "mobile", "app"], mins: 4, updated: "Apr 2026" },
  { t: "How to open a help desk ticket the fast way", intro: "The faster we get the right details, the faster we fix it. Here's how to open a great ticket.", steps: ["Reach us your way: the support portal, email, Teams, or phone for urgent issues.","Say what's happening and what you expected instead.","Include the exact error message. A screenshot is perfect.","Tell us when it started and whether others are affected.","Note how urgent it is (blocked vs minor) so we can prioritize.","You'll get a ticket number. Reply to that thread to add info."], cat: "getting-started", tags: ["ticket", "help desk", "support", "request"], mins: 2, updated: "May 2026" }
];

// ---- PUBLIC (guides & insights for prospects / SEO-GEO) — open, with summaries ----
const KB_PUBLIC_CATEGORIES = [
  { key: "buying", label: "Choosing IT Support", icon: "fa-scale-balanced", blurb: "Managed IT vs. break-fix, pricing, and switching." },
  { key: "cyber", label: "Cybersecurity Essentials", icon: "fa-shield-halved", blurb: "Plain-English protection for business owners." },
  { key: "compliance", label: "Compliance & Industry", icon: "fa-clipboard-check", blurb: "HIPAA, PCI, and audit-readiness made simple." },
  { key: "cloud", label: "Cloud & Microsoft 365", icon: "fa-cloud", blurb: "Licensing, migration, and getting the value." },
  { key: "growth", label: "IT for Growth", icon: "fa-arrow-trend-up", blurb: "Scaling, hybrid teams, and planning ahead." },
  { key: "local", label: "DFW & Local", icon: "fa-location-dot", blurb: "Local insight for Dallas–Fort Worth businesses." }
];

const KB_PUBLIC_ARTICLES = [
  { t: "Managed IT vs. break-fix: which is right for your business?", slug: "managed-it-vs-break-fix", cat: "buying", tags: ["managed it", "break-fix", "msp", "compare"], mins: 6, updated: "May 2026",
    sum: "Break-fix bills you when things break; managed IT works to prevent the break. Here's how to tell which model fits your company's size, risk tolerance, and budget." },
  { t: "What should managed IT cost in Dallas–Fort Worth?", slug: "managed-it-cost-dfw", cat: "buying", tags: ["pricing", "cost", "managed it", "dfw", "dallas"], mins: 5, updated: "May 2026",
    sum: "Per-user pricing, what's actually included, and the red flags hiding behind suspiciously cheap quotes — benchmarked for DFW small and mid-sized businesses." },
  { t: "10 signs it's time to switch IT providers", slug: "signs-to-switch-it-providers", cat: "buying", tags: ["switch", "msp", "support", "red flags"], mins: 5, updated: "Apr 2026",
    sum: "Slow tickets, surprise invoices, no documentation, calls that go to voicemail — the warning signs that your current IT partner is quietly holding you back." },
  { t: "The small-business cybersecurity checklist for 2026", slug: "smb-cybersecurity-checklist-2026", cat: "cyber", tags: ["cybersecurity", "checklist", "mfa", "smb"], mins: 7, updated: "May 2026",
    sum: "The dozen controls that stop the vast majority of attacks on small businesses — MFA, EDR, backups, patching, and training — explained without the jargon." },
  { t: "Ransomware: how DFW businesses actually get hit", slug: "ransomware-dfw-businesses", cat: "cyber", tags: ["ransomware", "attack", "prevention", "dfw"], mins: 6, updated: "Apr 2026",
    sum: "The real entry points behind local ransomware cases, what recovery actually costs, and the layered defenses that turn your business into a hard target." },
  { t: "HIPAA IT compliance: a plain-English guide for clinics", slug: "hipaa-it-compliance-clinics", cat: "compliance", tags: ["hipaa", "healthcare", "compliance", "clinic"], mins: 8, updated: "Mar 2026",
    sum: "What the HIPAA Security Rule actually expects from your technology — access controls, encryption, backups, and the documentation auditors ask to see." },
  { t: "PCI compliance basics for retail & restaurants", slug: "pci-compliance-retail-restaurants", cat: "compliance", tags: ["pci", "retail", "payments", "restaurant"], mins: 6, updated: "Mar 2026",
    sum: "Network segmentation, secure point-of-sale, and the practical steps that keep cardholder data safe and your business out of expensive scope." },
  { t: "Is Microsoft 365 Business Premium worth it?", slug: "microsoft-365-business-premium-worth-it", cat: "cloud", tags: ["microsoft 365", "licensing", "business premium", "value"], mins: 5, updated: "Apr 2026",
    sum: "The security and device-management features that justify the upgrade — and how to avoid paying for licenses and add-ons your team will never touch." },
  { t: "How to support a hybrid team without the chaos", slug: "support-hybrid-team", cat: "growth", tags: ["hybrid", "remote", "growth", "productivity"], mins: 6, updated: "Feb 2026",
    sum: "Standardized devices, identity-first security, and the cloud setup that lets people work from anywhere without opening holes in your defenses." },
  { t: "Why local IT support beats a national help desk", slug: "local-it-vs-national-helpdesk", cat: "local", tags: ["local", "dfw", "support", "on-site"], mins: 5, updated: "May 2026",
    sum: "On-site response in under an hour, engineers who already know your network, and accountability you can drive to — the case for IT down the road, not across the country." }
];

const KB_POPULAR = {
  public: ["managed IT cost", "cybersecurity", "HIPAA", "Microsoft 365", "switch providers"],
  client: ["reset password", "VPN", "phishing", "Outlook", "restore a file"]
};

function KnowledgeBasePage({ go }) {
  const [view, setView] = React.useState("home");   // "home" | "public" | "client"
  const [query, setQuery] = React.useState("");
  const [cat, setCat] = React.useState("all");
  const [gate, setGate] = React.useState(null);     // subscribe popup payload
  const [read, setRead] = React.useState(null);     // public reader payload
  const inputRef = React.useRef(null);

  const isClient = view === "client";
  const inLibrary = view !== "home";
  const cats = isClient ? KB_CATEGORIES : KB_PUBLIC_CATEGORIES;
  const source = isClient ? KB_ARTICLES : KB_PUBLIC_ARTICLES;

  const q = query.trim().toLowerCase();
  const filtered = source.filter((a) => {
    if (cat !== "all" && a.cat !== cat) return false;
    if (!q) return true;
    const hay = (a.t + " " + a.tags.join(" ") + " " + a.cat + " " + (a.sum || "")).toLowerCase();
    return q.split(/\s+/).every((tok) => hay.includes(tok));
  });

  const catLabel = (k) => (cats.find((c) => c.key === k) || {}).label || k;
  const catIcon = (k) => (cats.find((c) => c.key === k) || {}).icon || "fa-file-lines";
  const countFor = (k) => source.filter((a) => a.cat === k).length;

  const resetFilters = () => { setCat("all"); setQuery(""); };
  const goHome = () => { setView("home"); resetFilters(); window.scrollTo({ top: 0, behavior: "smooth" }); };

  const enterLibrary = (target) => {
    if (target === "public") { setView("public"); resetFilters(); }
    else {
      // Troubleshooting is client-only — gate at selection.
      if (kbIsSubscribed()) { setView("client"); resetFilters(); }
      else setGate({ mode: "library" });
    }
  };

  const scrollToLibrary = () => {
    const el = document.getElementById("kb-library");
    if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 90, behavior: "smooth" });
  };
  const browseAll = () => { resetFilters(); scrollToLibrary(); };
  const pickCategory = (k) => { setCat(k); setQuery(""); scrollToLibrary(); };
  const runPopular = (term) => { setQuery(term); setCat("all"); requestAnimationFrame(() => scrollToLibrary()); };
  const openArticle = (a) => { setRead({ article: a, variant: isClient ? "client" : "public" }); };

  return (
    <div className="page active" data-screen-label="Knowledge Base">
      {/* Hero */}
      <div className="page-head kb-head">
        <div className="container">
          <div className="crumb">
            <a onClick={() => go("services")}>HOME</a>
            <span className="sep">/</span>
            {inLibrary
              ? <React.Fragment><a onClick={goHome}>KNOWLEDGE BASE</a><span className="sep">/</span><span className="cur">{isClient ? "TROUBLESHOOTING" : "ARTICLES"}</span></React.Fragment>
              : <span className="cur">KNOWLEDGE BASE</span>}
          </div>
          <div className="ph-eyebrow">KNOWLEDGE BASE · GUIDES & CLIENT SUPPORT</div>
          <h1>{inLibrary ? (isClient ? <React.Fragment>Troubleshooting, <span className="accent">step by step.</span></React.Fragment> : <React.Fragment>Guides worth <span className="accent">your time.</span></React.Fragment>) : <React.Fragment>Find an answer <span className="accent">in seconds.</span></React.Fragment>}</h1>
          <p className="lede">{inLibrary
            ? (isClient ? "Search the client support library or browse by topic. Request any guide and we'll send it straight to your inbox." : "Free, no sign-in required — search practical guides for business owners and decision-makers, or browse by topic.")
            : "Pick a library to get started. Free guides for businesses weighing their options, and step-by-step support for our clients."}</p>

          {/* HOME: two nav buttons (no search yet) */}
          {!inLibrary && (
            <div className="kb-choose">
              <button className="kb-choice kb-choice-green" onClick={() => go("resources/m365-audit")}>
                <span className="kb-choice-tag is-free"><i className="fa-solid fa-download"></i>FREE DOWNLOAD</span>
                <div className="kb-choice-ic"><i className="fa-solid fa-file-arrow-down"></i></div>
                <h3>Downloads</h3>
                <p>Grab our Microsoft 365 license audit worksheet — inventory your licenses, cut the waste, and walk into renewal with leverage.</p>
                <span className="kb-choice-foot"><span>1 TOOL</span><span className="kb-choice-go">Get it <i className="fa-solid fa-arrow-right"></i></span></span>
              </button>
              <button className="kb-choice kb-choice-blue" onClick={() => enterLibrary("public")}>
                <span className="kb-choice-tag is-blue"><i className="fa-solid fa-unlock"></i>OPEN TO ALL</span>
                <div className="kb-choice-ic"><i className="fa-solid fa-book-open"></i></div>
                <h3>Articles</h3>
                <p>No-pressure guides on choosing IT, cybersecurity, compliance, and getting the most from your technology. Free to read.</p>
                <span className="kb-choice-foot"><span>{KB_PUBLIC_ARTICLES.length} ARTICLES</span><span className="kb-choice-go">Read now <i className="fa-solid fa-arrow-right"></i></span></span>
              </button>
              <button className="kb-choice kb-choice-gold" onClick={() => enterLibrary("client")}>
                <span className="kb-choice-tag is-client"><i className="fa-solid fa-lock"></i>CLIENT ACCESS</span>
                <div className="kb-choice-ic"><i className="fa-solid fa-screwdriver-wrench"></i></div>
                <h3>Troubleshooting</h3>
                <p>Step-by-step fixes for everyday IT — passwords, Wi-Fi, Outlook, printers, and more. For active 37 Forge clients.</p>
                <span className="kb-choice-foot"><span>{KB_ARTICLES.length} GUIDES</span><span className="kb-choice-go">Unlock <i className="fa-solid fa-arrow-right"></i></span></span>
              </button>
            </div>
          )}

          {/* LIBRARY: switcher + revealed search */}
          {inLibrary && (
            <React.Fragment>
              <div className="kb-switch">
                <button className="kb-back" onClick={goHome}><i className="fa-solid fa-arrow-left"></i>Both libraries</button>
                <div className="kb-seg" role="tablist" aria-label="Knowledge base library">
                  <button className={"kb-seg-btn" + (!isClient ? " active" : "")} role="tab" aria-selected={!isClient} onClick={() => enterLibrary("public")}>
                    <i className="fa-solid fa-book-open"></i>Articles
                  </button>
                  <button className={"kb-seg-btn" + (isClient ? " active" : "")} role="tab" aria-selected={isClient} onClick={() => enterLibrary("client")}>
                    <i className="fa-solid fa-lock"></i>Troubleshooting
                  </button>
                </div>
              </div>

              <div className="kb-search kb-reveal">
                <i className="fa-solid fa-magnifying-glass kb-search-ic"></i>
                <input
                  ref={inputRef}
                  type="text"
                  value={query}
                  onChange={(e) => setQuery(e.target.value)}
                  placeholder={isClient ? "Search support how-tos, e.g. “reset password”…" : "Search guides, e.g. “managed IT cost” or “HIPAA”…"}
                  aria-label="Search the knowledge base"
                />
                {query && (
                  <button className="kb-search-clear" onClick={() => { setQuery(""); inputRef.current && inputRef.current.focus(); }} aria-label="Clear search">
                    <i className="fa-solid fa-xmark"></i>
                  </button>
                )}
                <button className="btn-primary kb-search-btn" onClick={scrollToLibrary}>
                  <i className="fa-solid fa-arrow-down"></i>Results
                </button>
              </div>

              <div className="kb-popular kb-reveal">
                <span className="kb-popular-lab">POPULAR</span>
                <div className="kb-chips">
                  {(isClient ? KB_POPULAR.client : KB_POPULAR.public).map((p) => (
                    <button key={p} className="kb-chip" onClick={() => runPopular(p)}>{p}</button>
                  ))}
                </div>
              </div>
            </React.Fragment>
          )}
        </div>
      </div>

      {/* Library content (only after a library is chosen) */}
      {inLibrary && (
        <React.Fragment>
          <section className="canvas-deep" style={{ borderTop: "1px solid #1f3358" }}>
            <div className="container">
              <KB_SH eyebrow={isClient ? "SUPPORT · BROWSE BY TOPIC" : "ARTICLES · BROWSE BY TOPIC"}
                     title={isClient ? "Start where" : "Get smart"} accent={isClient ? "it hurts." : "before you buy."}
                     desc={isClient
                       ? "Topic areas covering the questions our help desk hears most. Pick one to jump to its guides."
                       : "No-pressure explainers for the decisions every growing business faces. Pick a topic to dig in."} />
              <div className="kb-cat-grid">
                {cats.map((c) => (
                  <button key={c.key} className="kb-cat" onClick={() => pickCategory(c.key)}>
                    <div className="kb-cat-ic"><i className={`fa-solid ${c.icon}`}></i></div>
                    <div className="kb-cat-body">
                      <div className="kb-cat-label">{c.label}</div>
                      <div className="kb-cat-blurb">{c.blurb}</div>
                    </div>
                    <div className="kb-cat-count">{String(countFor(c.key)).padStart(2, "0")}</div>
                  </button>
                ))}
              </div>
            </div>
          </section>

          <section className="canvas" id="kb-library" style={{ borderTop: "1px solid #1f3358" }}>
            <div className="container">
              <div className="kb-lib-head">
                <div>
                  <div className="ph-eyebrow" style={{ marginBottom: "14px" }}>
                    {q ? "SEARCH RESULTS"
                       : cat !== "all" ? "TOPIC · " + catLabel(cat).toUpperCase()
                       : isClient ? "TROUBLESHOOTING LIBRARY" : "ARTICLES · GUIDES & INSIGHTS"}
                  </div>
                  <h2 className="kb-lib-title">
                    {filtered.length} {filtered.length === 1 ? (isClient ? "article" : "guide") : (isClient ? "articles" : "guides")}
                    {q && <span className="kb-lib-q"> for “{query.trim()}”</span>}
                  </h2>
                </div>
                {(q || cat !== "all") && (
                  <button className="kb-reset" onClick={browseAll}>
                    <i className="fa-solid fa-rotate-left"></i>Show everything
                  </button>
                )}
              </div>

              {filtered.length === 0 ? (
                <div className="kb-empty">
                  <div className="kb-empty-ic"><i className="fa-solid fa-magnifying-glass"></i></div>
                  <h3>No {isClient ? "articles" : "guides"} match “{query.trim()}”.</h3>
                  <p>Try a simpler term, or let a real engineer answer it directly.</p>
                  <div className="kb-empty-cta">
                    <button className="btn-primary" onClick={() => go("contact")}><i className="fa-solid fa-headset"></i>{isClient ? "Ask the help desk" : "Talk to us"}</button>
                    <button className="btn-outline" onClick={browseAll}><i className="fa-solid fa-layer-group"></i>Browse everything</button>
                  </div>
                </div>
              ) : (
                <div className={"kb-lib" + (isClient ? "" : " kb-lib-public")}>
                  {filtered.map((a, i) => (
                    <a className={"kb-art" + (isClient ? "" : " kb-art-public")} key={i} onClick={() => openArticle(a)} role="button" tabIndex={0}>
                      <div className="kb-art-ic"><i className={`fa-solid ${catIcon(a.cat)}`}></i></div>
                      <div className="kb-art-main">
                        <div className="kb-art-title">{a.t}</div>
                        {!isClient && a.sum && <div className="kb-art-sum">{a.sum}</div>}
                        <div className="kb-art-meta">
                          <span className={"kb-art-badge " + (isClient ? "is-client" : "is-free")}>
                            <i className={`fa-solid ${isClient ? "fa-lock" : "fa-unlock"}`}></i>{isClient ? "CLIENT ACCESS" : "FREE GUIDE"}
                          </span>
                          <span className="kb-art-cat">{catLabel(a.cat)}</span>
                          <span className="kb-art-dot">·</span>
                          <span>{a.mins} min read</span>
                          <span className="kb-art-dot">·</span>
                          <span>Updated {a.updated}</span>
                        </div>
                      </div>
                      <i className={`fa-solid ${isClient ? "fa-envelope" : "fa-arrow-right"} kb-art-arrow`}></i>
                    </a>
                  ))}
                </div>
              )}
            </div>
          </section>

          <section className="canvas-deep" style={{ borderTop: "1px solid #1f3358" }}>
            <div className="container">
              <div className="svc-cta-grid">
                <div>
                  <div className="svc-cta-eye">{isClient ? "CAN'T FIND IT? · REAL ENGINEERS, NOT BOTS" : "READY TO TALK? · NO PRESSURE, NO SCRIPT"}</div>
                  <h2 className="svc-cta-h">{isClient ? "Still stuck?" : "Like what you read?"} <span className="accent">{isClient ? "We pick up." : "Let's talk."}</span></h2>
                  <p className="svc-cta-p">{isClient
                    ? "The support library covers the common stuff. For everything else, our DFW help desk answers in a median of 12 minutes — by phone, email, or Teams."
                    : "Guides only go so far. Book a 30-minute assessment and we'll give you specific, honest answers about your environment — whether or not you hire us."}</p>
                </div>
                <div className="svc-cta-actions">
                  <button className="btn-primary" onClick={() => go("contact")} style={{ fontSize: "15px", padding: "16px 24px", justifyContent: "center" }}>
                    <i className={`fa-solid ${isClient ? "fa-headset" : "fa-calendar-check"}`}></i>{isClient ? "Contact the help desk" : "Book an assessment"}
                  </button>
                  <a href="tel:2144320333" className="svc-cta-call">OR CALL 214-432-0333 · 24/7</a>
                </div>
              </div>
            </div>
          </section>
        </React.Fragment>
      )}

      {gate && <KBGate payload={gate} onClose={() => setGate(null)} onUnlock={() => { setView("client"); resetFilters(); }} />}
      {read && <KBReader article={read.article} variant={read.variant} catLabel={catLabel} catIcon={catIcon} onClose={() => setRead(null)} go={go} />}
    </div>
  );
}

// ============================================================
// Public reader — open with a metered capture (KB_FREE_READS free, then ask once,
// remembered forever on KB_READER_KEY). Independent of the client Troubleshooting gate.
// ============================================================
function KBReader({ article, variant = "public", catLabel, catIcon, onClose, go }) {
  const isClientGuide = variant === "client";
  const known = isClientGuide || kbReaderKnown();
  const readsAtOpen = kbReadCount();
  const mustCapture = !isClientGuide && !known && readsAtOpen >= KB_FREE_READS;

  const [unlocked, setUnlocked] = React.useState(!mustCapture);
  const [justJoined, setJustJoined] = React.useState(false);

  let saved = null;
  try { saved = JSON.parse(localStorage.getItem(KB_READER_KEY) || localStorage.getItem(KB_SUB_KEY) || "null"); } catch (e) {}
  const [form, setForm] = React.useState({
    first_name: (saved && saved.first_name) || "",
    last_name: (saved && saved.last_name) || "",
    email: (saved && saved.email) || "",
    company: (saved && saved.company) || ""
  });
  const [sending, setSending] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const emailRef = React.useRef(null);
  const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));

  // Free reads remaining AFTER this view (only meaningful when reading for free)
  const readingFree = unlocked && !known;
  const freeLeft = Math.max(0, KB_FREE_READS - (readsAtOpen + 1));

  React.useEffect(() => {
    // Consume one free read if this view is being shown for free.
    if (!mustCapture && !known) {
      try { localStorage.setItem(KB_READS_KEY, String(readsAtOpen + 1)); } catch (e) {}
    }
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    requestAnimationFrame(() => { if (emailRef.current) emailRef.current.focus(); });
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = prev; };
  }, []);

  const submit = async (e) => {
    e.preventDefault();
    if (sending) return;
    if (!form.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) { setErr("A valid email is required."); return; }
    setSending(true); setErr(null);
    const key = document.querySelector('meta[name="web3forms-key"]') && document.querySelector('meta[name="web3forms-key"]').content;
    try {
      if (key) {
        const fd = new FormData();
        fd.append("access_key", key);
        fd.append("subject", `37 Forge KB · Guides subscriber · ${form.email}`);
        fd.append("from_name", `${form.first_name} ${form.last_name}`.trim() || form.email);
        fd.append("source", "Knowledge Base · Articles (metered)");
        fd.append("unlocked_reading", article.t);
        Object.entries(form).forEach(([k, v]) => fd.append(k, v));
        const r = await fetch("https://api.web3forms.com/submit", { method: "POST", body: fd });
        const json = await r.json().catch(() => ({}));
        if (!r.ok || !json.success) throw new Error(json.message || "Something went wrong. Please try again.");
      } else {
        await new Promise((res) => setTimeout(res, 600));
      }
      try { localStorage.setItem(KB_READER_KEY, JSON.stringify(form)); } catch (e) {}
      setJustJoined(true);
      setUnlocked(true);
    } catch (ex) {
      setErr(ex.message || "Could not send. Please try again or call 214-432-0333.");
    } finally {
      setSending(false);
    }
  };

  const teaser = (article.sum || "").split(" ").slice(0, 14).join(" ");

  return ReactDOM.createPortal(
    <div className="kb-modal-overlay" onClick={(e) => { if (e.target.classList.contains("kb-modal-overlay")) onClose(); }}>
      <div className="kb-modal kb-reader" role="dialog" aria-modal="true" aria-label={article.t}>
        <button className="kb-modal-close" onClick={onClose} aria-label="Close"><i className="fa-solid fa-xmark"></i></button>

        {unlocked ? (
          <React.Fragment>
            <div className="kb-modal-eye" style={{ color: isClientGuide ? "var(--accent-bright)" : "#4ade80" }}>
              <i className={`fa-solid ${isClientGuide ? "fa-screwdriver-wrench" : "fa-unlock"}`} style={{ marginRight: "8px" }}></i>{isClientGuide ? "CLIENT GUIDE" : "FREE GUIDE"} · {(catLabel(article.cat) || "").toUpperCase()}
            </div>
            <h3 className="kb-modal-title">{article.t}</h3>
            <div className="kb-reader-meta">
              <span>{article.mins} min read</span><span className="kb-art-dot">·</span><span>Updated {article.updated}</span>
            </div>
            <p className="kb-reader-lead">{isClientGuide ? (article.intro || "Follow the steps below.") : article.sum}</p>
            {isClientGuide && article.steps && (
              <ol className="kb-steps">
                {article.steps.map((st, i) => <li key={i}>{st}</li>)}
              </ol>
            )}
            <div className="kb-reader-note">
              <i className="fa-solid fa-circle-info"></i>
              <span>{isClientGuide
                ? "Followed the steps and still stuck? Our DFW help desk will take it from here."
                : "Read the complete guide on our site — or we'll talk it through with you directly."}</span>
            </div>
            {justJoined && <div className="kb-reader-hint is-joined"><i className="fa-solid fa-check"></i>You're on the list — every guide is open now. No more sign-ups.</div>}
            {!justJoined && readingFree && <div className="kb-reader-hint"><i className="fa-solid fa-book-open"></i>{freeLeft > 0 ? `${freeLeft} free read${freeLeft === 1 ? "" : "s"} left before a quick sign-up.` : "Last free read — we'll ask for your email on the next one."}</div>}
            <div className="kb-thanks-cta" style={{ marginTop: "22px", gap: "12px", flexWrap: "wrap" }}>
              {!isClientGuide && article.slug
                ? <a className="btn-primary" href={`guides/${article.slug}.html`} style={{ textDecoration: "none" }}><i className="fa-solid fa-book-open"></i>Read the full guide</a>
                : <button className="btn-primary" onClick={() => { onClose(); go("contact"); }}><i className={`fa-solid ${isClientGuide ? "fa-headset" : "fa-calendar-check"}`}></i>{isClientGuide ? "Contact the help desk" : "Get specific answers"}</button>}
              <button className="btn-outline" onClick={onClose}><i className="fa-solid fa-layer-group"></i>Back to guides</button>
            </div>
          </React.Fragment>
        ) : (
          <React.Fragment>
            <div className="kb-modal-eye" style={{ color: "#4ade80" }}>
              <i className="fa-solid fa-unlock" style={{ marginRight: "8px" }}></i>FREE · KEEP READING
            </div>
            <h3 className="kb-modal-title">{article.t}</h3>
            <div className="kb-teaser">
              <p className="kb-reader-lead" style={{ margin: 0 }}>{teaser}…</p>
              <div className="kb-teaser-fade"></div>
            </div>
            <p className="kb-modal-sub" style={{ marginTop: "18px" }}>You've used your {KB_FREE_READS} free reads. Drop your email once and every guide stays open — no more interruptions. We'll send the occasional new how-to. No spam, unsubscribe anytime.</p>
            <form onSubmit={submit} noValidate>
              <div className="form-row two">
                <div><label>FIRST NAME</label><input type="text" placeholder="Jane" value={form.first_name} onChange={set("first_name")} /></div>
                <div><label>LAST NAME</label><input type="text" placeholder="Lee" value={form.last_name} onChange={set("last_name")} /></div>
              </div>
              <div className={`form-row ${err ? "error" : ""}`}>
                <label>WORK EMAIL <span className="req">*</span></label>
                <input ref={emailRef} type="email" placeholder="jane@company.com" value={form.email} onChange={set("email")} />
                {err && <div className="err" style={{ display: "block" }}>{err}</div>}
              </div>
              <div className="form-row"><label>COMPANY <span style={{ color: "#6c7d99" }}>OPTIONAL</span></label><input type="text" placeholder="Acme Co." value={form.company} onChange={set("company")} /></div>
              <button type="submit" disabled={sending} className="btn-primary" style={{ width: "100%", height: "50px", marginTop: "6px", opacity: sending ? 0.6 : 1, cursor: sending ? "wait" : "pointer" }}>
                <i className={sending ? "fa-solid fa-spinner fa-spin" : "fa-solid fa-lock-open"}></i>{sending ? "Unlocking…" : "Keep reading — unlock all guides"}
              </button>
              <div className="kb-modal-fine">PRIVATE · ONE-TIME · WE WON'T ASK AGAIN ON THIS DEVICE</div>
            </form>
          </React.Fragment>
        )}
      </div>
    </div>
  , document.body);
}

// ============================================================
// Subscribe wall — unlocks the client library (mode "library")
// or emails a specific client article (mode "article").
// ============================================================
function KBGate({ payload, onClose, onUnlock }) {
  const mode = (payload && payload.mode) || "article";
  const article = payload && payload.article;
  const isLibrary = mode === "library";

  let saved = null;
  try { saved = JSON.parse(localStorage.getItem(KB_SUB_KEY) || "null"); } catch (e) {}
  const returning = !!(saved && saved.email);

  const [form, setForm] = React.useState({
    first_name: (saved && saved.first_name) || "",
    last_name: (saved && saved.last_name) || "",
    email: (saved && saved.email) || "",
    company: (saved && saved.company) || "",
    access_code: ""
  });
  const [sending, setSending] = React.useState(false);
  const [sent, setSent] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const emailRef = React.useRef(null);
  const cat = article ? (KB_CATEGORIES.find((c) => c.key === article.cat) || {}) : {};

  const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    requestAnimationFrame(() => { if (emailRef.current && !returning) emailRef.current.focus(); });
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = prev; };
  }, []);

  const submit = async (e) => {
    e.preventDefault();
    if (sending) return;
    if (!form.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) { setErr("A valid email is required."); return; }
    if (isLibrary && !kbClientAllowed(form.email, form.access_code)) {
      setErr("This library is for clients. Use your work email or enter a valid access code.");
      return;
    }
    setSending(true); setErr(null);
    const key = document.querySelector('meta[name="web3forms-key"]') && document.querySelector('meta[name="web3forms-key"]').content;
    try {
      if (key) {
        const fd = new FormData();
        fd.append("access_key", key);
        fd.append("subject", isLibrary ? `37 Forge KB · Client library access · ${form.email}` : `37 Forge KB · Article request · ${article.t}`);
        fd.append("from_name", `${form.first_name} ${form.last_name}`.trim() || form.email);
        fd.append("source", isLibrary ? "Knowledge Base · Troubleshooting library unlock" : "Knowledge Base · Client article request");
        if (article) { fd.append("article_requested", article.t); fd.append("article_topic", cat.label || article.cat); }
        Object.entries(form).forEach(([k, v]) => fd.append(k, v));
        const r = await fetch("https://api.web3forms.com/submit", { method: "POST", body: fd });
        const json = await r.json().catch(() => ({}));
        if (!r.ok || !json.success) throw new Error(json.message || "Something went wrong. Please try again.");
      } else {
        await new Promise((res) => setTimeout(res, 600));
      }
      try { localStorage.setItem(KB_SUB_KEY, JSON.stringify(form)); } catch (e) {}
      setSent(true);
    } catch (ex) {
      setErr(ex.message || "Could not send. Please try again or call 214-432-0333.");
    } finally {
      setSending(false);
    }
  };

  const enterLibrary = () => { if (onUnlock) onUnlock(); onClose(); };

  return ReactDOM.createPortal(
    <div className="kb-modal-overlay" onClick={(e) => { if (e.target.classList.contains("kb-modal-overlay")) onClose(); }}>
      <div className="kb-modal" role="dialog" aria-modal="true" aria-label={isLibrary ? "Unlock the troubleshooting library" : "Get the full article"}>
        <button className="kb-modal-close" onClick={onClose} aria-label="Close"><i className="fa-solid fa-xmark"></i></button>
        {!sent ? (
          <React.Fragment>
            <div className="kb-modal-eye"><i className="fa-solid fa-lock" style={{ marginRight: "8px" }}></i>{isLibrary ? "CLIENT ACCESS · ONE-TIME" : (returning ? "DELIVER TO YOUR INBOX" : "CLIENT GUIDE · SENT TO YOU")}</div>
            <h3 className="kb-modal-title">{isLibrary ? "Unlock the troubleshooting library." : (returning ? "Want this one in your inbox?" : "Get the full step-by-step guide.")}</h3>
            <div className="kb-req">
              <div className="kb-req-ic"><i className={`fa-solid ${isLibrary ? "fa-screwdriver-wrench" : (cat.icon || "fa-file-lines")}`}></i></div>
              <div className="kb-req-main">
                <div className="kb-req-lab">{isLibrary ? "YOU'RE UNLOCKING" : "YOU'RE REQUESTING"}</div>
                <div className="kb-req-title">{isLibrary ? "37 Forge Troubleshooting" : article.t}</div>
                <div className="kb-req-meta">{isLibrary ? `${KB_ARTICLES.length} step-by-step guides` : `${cat.label || ""} · ${article.mins} min read`}</div>
              </div>
            </div>
            <p className="kb-modal-sub">{isLibrary
              ? "Our troubleshooting guides are for 37 Forge clients. Add your details once to unlock the whole library — we'll also email you new how-tos as they're published. No spam, unsubscribe anytime."
              : (returning ? "We've got your details on file — confirm below and we'll send the full guide straight over." : "Tell us where to send it and we'll email the complete walkthrough — plus the occasional new how-to. No spam, unsubscribe anytime.")}</p>
            <form onSubmit={submit} noValidate>
              <div className="form-row two">
                <div>
                  <label>FIRST NAME</label>
                  <input type="text" placeholder="Jane" value={form.first_name} onChange={set("first_name")} />
                </div>
                <div>
                  <label>LAST NAME</label>
                  <input type="text" placeholder="Lee" value={form.last_name} onChange={set("last_name")} />
                </div>
              </div>
              <div className={`form-row ${err ? "error" : ""}`}>
                <label>WORK EMAIL <span className="req">*</span></label>
                <input ref={emailRef} type="email" placeholder="jane@company.com" value={form.email} onChange={set("email")} />
                {err && <div className="err" style={{ display: "block" }}>{err}</div>}
              </div>
              <div className="form-row">
                <label>COMPANY <span style={{ color: "#6c7d99" }}>OPTIONAL</span></label>
                <input type="text" placeholder="Acme Co." value={form.company} onChange={set("company")} />
              </div>
              {isLibrary && (
                <div className="form-row">
                  <label>CLIENT ACCESS CODE <span style={{ color: "#6c7d99" }}>IF NEEDED</span></label>
                  <input type="text" placeholder="e.g. FORGE-CLIENT" value={form.access_code} onChange={set("access_code")} autoComplete="off" spellCheck="false" />
                  <div className="kb-gate-help"><i className="fa-solid fa-circle-info"></i>Use your company work email, or the access code your 37 Forge engineer gave you.</div>
                </div>
              )}
              <button type="submit" disabled={sending} className="btn-primary" style={{ width: "100%", height: "50px", marginTop: "6px", opacity: sending ? 0.6 : 1, cursor: sending ? "wait" : "pointer" }}>
                <i className={sending ? "fa-solid fa-spinner fa-spin" : (isLibrary ? "fa-solid fa-unlock" : "fa-solid fa-paper-plane")}></i>{sending ? "Working…" : (isLibrary ? "Unlock the library" : (returning ? "Send it to me" : "Send me the article"))}
              </button>
              <div className="kb-modal-fine">PRIVATE · WE ONLY USE THIS TO {isLibrary ? "GIVE YOU ACCESS" : "SEND WHAT YOU ASKED FOR"}</div>
            </form>
          </React.Fragment>
        ) : (
          <div className="kb-thanks">
            <div className="kb-thanks-ic"><i className={`fa-solid ${isLibrary ? "fa-lock-open" : "fa-paper-plane"}`}></i></div>
            <div className="kb-modal-eye" style={{ color: "#4ade80" }}>{isLibrary ? "ACCESS GRANTED" : "ON ITS WAY"}</div>
            <h3 className="kb-modal-title">{isLibrary ? "You're in." : "Check your inbox."}</h3>
            <p className="kb-modal-sub">{isLibrary
              ? <React.Fragment>The full troubleshooting library is unlocked on this device. We've sent a confirmation to <strong style={{ color: "#eef2f8" }}>{form.email}</strong>.</React.Fragment>
              : <React.Fragment>We're sending <strong style={{ color: "#eef2f8" }}>“{article.t}”</strong> to <strong style={{ color: "#eef2f8" }}>{form.email}</strong>. It should land within a few minutes — peek in spam if it's shy.</React.Fragment>}</p>
            <div className="kb-thanks-cta">
              {isLibrary
                ? <button className="btn-primary" onClick={enterLibrary}><i className="fa-solid fa-arrow-right"></i>Enter the library</button>
                : <button className="btn-outline" onClick={onClose}><i className="fa-solid fa-layer-group"></i>Browse more articles</button>}
            </div>
          </div>
        )}
      </div>
    </div>
  , document.body);
}

Object.assign(window, { KnowledgeBasePage });
