// slides-closing.jsx — Slides 15-21 (use cases 3-5, other features, love, comparison, close)

function Slide16_AI() {
  return <UseCaseSlide
    caseNo={3}
    motif={MOTIF_AI}
    titleWord="AI & Model Training"
    subtitle="Clean training data for security ML"
    problem={{
      applies: "ML · DETECTION · SOC",
      headline: "Training data is locked to customer networks",
      cost: "COST · LEGAL + ETHICAL",
      items: [
        "Can't risk models learning customer PII or names",
        "Can't model attacks you've never seen in the wild",
        "Can't use tooling customers haven't deployed",
        "No ethical way to generate adversarial data at scale",
      ],
    }}
    solution={{
      headline: "Generate clean-room data at will",
      outcome: "DATASET · ON DEMAND · CLEAN",
      items: [
        "Build large complex networks on demand",
        "100% clean data — <span style='color:#c5ae4f'>no customer signal leaks</span>",
        "Simulate any attack pattern you can script",
        "Use any security or logging stack at your disposal",
        "User simulation adds realistic background noise",
      ],
    }}
  />;
}

function Slide17_ThreatEm() {
  return <UseCaseSlide
    caseNo={4}
    motif={MOTIF_THREATEM}
    titleWord="Threat Emulation"
    subtitle="Tuesday. Long weekend. You need an env by EOD."
    problem={{
      applies: "PURPLE TEAM · DETECTION ENG",
      headline: "Stand up a realistic enterprise by hand — again?",
      cost: "COST · A LONG WEEKEND",
      items: [
        "Custom Windows domain",
        "ELK + Elastic Security + Fleet with detection rules",
        "Velociraptor server + clients",
        "PowerShell script-block logging · Sysmon",
        "<span style='color:#8f1d34'>How long would this take you?</span>",
      ],
    }}
    solution={{
      headline: "Ansible roles · CI/CD · reproducible",
      outcome: "SPINUP · MINUTES · UNATTENDED",
      items: [
        "Ansible roles configure VMs — documented, repeatable",
        "Templates managed by Ludus, not by tribal knowledge",
        "Fully documented REST API — plug into CI/CD",
        "Spin up target → test → destroy → power off — all automated",
      ],
    }}
  />;
}

function Slide18_VulnScan() {
  return <UseCaseSlide
    caseNo={5}
    motif={MOTIF_VULN}
    titleWord="Vulnerability Scan Testing"
    subtitle="Regression-test your scanners, not in production"
    problem={{
      applies: "VENDOR · SCANNER · QA",
      headline: "Scan engines are verified by hand — or not at all",
      cost: "COST · SIGNATURE BACKLOG",
      items: [
        "Someone builds a vulnerable service manually",
        "Catalog of target VMs lives \"somewhere\"",
        "Each new signature verified by an engineer, by hand",
        "No automation · no regression testing",
        "What if the next scan <span style='color:#8f1d34'>crashes</span> your customer?",
      ],
    }}
    solution={{
      headline: "CI/CD for your detection logic",
      outcome: "PIPELINE · SCAN → VERIFY → SHIP",
      items: [
        "Roles define vulnerable services · reproducible",
        "Templates versioned alongside scanner code",
        "REST API plugs into CI/CD — scan-and-verify pipelines",
        "Regression library of target envs · ready on demand",
      ],
    }}
  />;
}

function Slide19_ThreatIntel() {
  return <UseCaseSlide
    caseNo={6}
    motif={MOTIF_INTEL}
    titleWord="Threat Intelligence"
    subtitle="Get to stage 2+ when commodity sandboxes can't"
    problem={{
      applies: "THREAT INTEL · MALWARE ANALYSIS"
    , headline: "Adversaries won't detonate on a default sandbox",
      cost: "COST · STAGES 2+ LOST",
      items: [
        "Sandbox detection is ubiquitous and getting better",
        "Environmental keying is increasingly common",
        "Commercial sandboxes can't fingerprint your enterprise",
        "Net result: you never see stage 2, 3, or 4",
      ],
    }}
    solution={{
      headline: "Build a \"digital twin\" of your environment",
      outcome: "TWIN · INDISTINGUISHABLE · PATIENT",
      items: [
        "Same domain, same users, same apps — at-scale",
        "Realistic machines that defeat anti-VM checks",
        "User emulation convinces the attacker it's real",
        "VM introspection for <span style='color:#c5ae4f'>advanced analysis</span>",
      ],
    }}
  />;
}

// ─── SLIDE 20 · OTHER FEATURES (docs-style, interactive) ───────
function Slide20_OtherFeatures() {
  const features = [
    {
      id: "blueprints",
      flagship: true,
      nav: "Blueprints",
      emoji: "🗺️",
      tagline: "Save, share, and re-apply range configs in one command",
      body: "A saved range config a teammate applies instantly. Onboarding, workshops, approved configs your team can self-serve — all versioned, all shareable.",
      snippet: [
        "ludus blueprint create --id ad-lab",
        "ludus blueprint share group ad-lab sec-team",
        "ludus blueprint apply ad-lab",
      ],
      path: "/docs/using-ludus/blueprints",
    },
    {
      id: "ai",
      flagship: true,
      nav: "AI Assistants",
      emoji: "🤖",
      tagline: "Control Ludus from Claude, Codex, Cursor — via MCP + skills",
      body: "The MCP server exposes every Ludus API action to coding assistants; skills bundle range-config, troubleshooting, CLI and environment knowledge. Talk to your range in plain English.",
      snippet: [
        "# in your coding assistant:",
        '"snapshot all my VMs, start testing mode, allow example.com"',
        "# the agent translates to the right ludus commands",
      ],
      path: "/docs/using-ludus/mcp",
    },
    {
      id: "webui",
      flagship: true,
      nav: "Web UI",
      emoji: "🖥️",
      tagline: "Drag-and-drop topology design · SSO-ready",
      body: "Self-hostable, offline-capable Next.js GUI on the same REST API. Run it behind SSO (PocketBase). Pro and Enterprise tiers.",
      media: "assets/web-ui.gif",
      mediaAlt: "Ludus Web UI — drag-and-drop topology designer",
      host: "ludus.cloud",
      path: "/#pro",
    },
    {
      id: "templates",
      nav: "Automated templates",
      emoji: "📜",
      tagline: "Packer-built VMs from checksum-verified ISOs",
      body: "13 templates shipped out of the box — Windows, Linux, Kali, FLARE-VM, macOS. Bring your own with a Packer HCL file; they build unattended on first run.",
      snippet: [
        "ludus templates list",
        "ludus templates build",
      ],
      path: "/docs/using-ludus/templates",
    },
    {
      id: "testing",
      nav: "Testing mode",
      emoji: "🛡️",
      tagline: "Snapshot, isolate, allow live C2 by domain or IP",
      body: "Pre-snapshot every VM, block the internet, reconnect only the domains or IPs you need for live C2 — then revert to clean state. OPSEC-safe by default.",
      snippet: [
        "ludus testing start",
        "ludus testing allow -d c2.example.com",
        "ludus testing stop    # reverts all snapshots",
      ],
      path: "/docs/quick-start/testing-mode",
    },
    {
      id: "api",
      nav: "REST API",
      emoji: "🔌",
      tagline: "Every CLI action is an OpenAPI 3.0 HTTP call",
      body: "Wire Ludus into CI/CD, dashboards, or your own tooling. Full OpenAPI 3.0 spec ships with the server — no scraping, no guessing.",
      snippet: [
        'curl -H "X-API-KEY: $KEY" \\',
        "     https://ludus:8080/range",
        "# spec at https://<ludus>:8080/swagger",
      ],
      path: "/docs",
    },
    {
      id: "net",
      nav: "VLANs + firewall",
      emoji: "🌐",
      tagline: "Up to 254 /24 networks with arbitrary per-port rules",
      body: "Model real enterprise topologies — flat networks, segmented DMZs, pivots. Rules are declared in YAML and applied by the Ludus router VM.",
      snippet: [
        "network:",
        "  rules:",
        "    - name: allow RDP from ops",
        "      vlan_src: 10",
        "      vlan_dst: 20",
        "      protocol: tcp",
        "      ports: 3389",
        "      action: ACCEPT",
      ],
      path: "/docs/infrastructure-operations/networking",
    },
    {
      id: "groups",
      nav: "User groups",
      emoji: "👥",
      tagline: "Onboard the whole team in one command",
      body: "Create a group, add members, grant it range access — a new analyst gets their lab in a single assign. Group managers run it day-to-day without admin rights.",
      snippet: [
        "ludus group create sec-team",
        "ludus group add user sec-team alice",
        "ludus group add range sec-team CLIENT-ENG",
      ],
      path: "/docs/using-ludus/sharing",
    },
    {
      id: "cluster",
      nav: "Cluster support",
      emoji: "🏘️",
      tagline: "Scale a range across multiple Proxmox nodes",
      body: "Auto-detects a Proxmox cluster and spreads VMs by RAM and CPU headroom. Range networks span nodes via Proxmox SDN + VXLAN, and templates built on one node clone across the whole cluster.",
      snippet: [
        "# auto-detects your Proxmox cluster",
        "defaults:",
        "  target_node: pve1",
        "ludus:",
        "  - vm_name: \"{{ range_id }}-dc01\"",
        "    target_node: pve2   # pin to a node",
      ],
      path: "/docs/infrastructure-operations/cluster",
    },
    {
      id: "roles",
      nav: "Ansible roles",
      emoji: "🎭",
      tagline: "Any of 35,000+ Galaxy roles, plus your own",
      body: "Install from Ansible Galaxy, a git URL, or a local directory. Any role compatible with the VM's OS works — no special Ludus wrapping required.",
      snippet: [
        "# from Ansible Galaxy",
        "ludus ansible role add badsectorlabs.ludus_adcs",
        "",
        "# or from a local directory",
        "ludus ansible role add -d ./my_custom_role",
      ],
      path: "/docs/using-ludus/roles",
    },
    {
      id: "cloud",
      nav: "Runs anywhere",
      emoji: "☁️",
      tagline: "Bare metal · Proxmox · Azure · AWS · GCP · Hyper-V · Fusion",
      body: "Anywhere Debian 12/13 or Proxmox 8/9 will run — deploy on the metal in a SCIF, on an AWS .metal EC2, or on a GCP nested-virt VM. Same experience.",
      snippet: [
        "# Any of:",
        "# bare metal · Proxmox 8/9 · Azure Dv3/Ev3",
        "# AWS *.metal · GCP nested-virt",
        "# Hyper-V · VMware Fusion",
      ],
      path: "/docs/category/deployment-options",
    },
    {
      id: "enterprise",
      nav: "Enterprise roles",
      emoji: "🏢",
      tagline: "Curated catalog on top of the OSS core",
      body: "Detection, C2, access, and identity integrations maintained by Bad Sector Labs. Drop them into any range config — no glue code, no one-off installers. Pro and Enterprise tiers.",
      snippet: [
        "# DETECTION & OBSERVABILITY",
        "velociraptor · zeek · sysmon · mde · chronicle-exporter",
        "",
        "# COMMAND & CONTROL · ACCESS",
        "mythic · guacamole · sso (pocketbase)",
        "",
        "# IDENTITY & REALISM",
        "bulk-ad-content · ghosts · unconstrained-delegation",
        "",
        "# OPERATIONS",
        "outbound-wireguard · kms · anti-sandbox",
      ],
      path: "/docs/category/enterprise",
    },
  ];

  const [sel, setSel] = React.useState(0);
  const active = features[sel];
  const { slideNo } = React.useContext(DeckCtx);

  // Up/Down arrow nav within the sidebar — only when this slide is active.
  // deck-stage.js binds Left/Right/Home/End/R/0-9 but leaves Up/Down free.
  // slideNo comes from DeckCtx so the guard tracks reordering automatically.
  React.useEffect(() => {
    const onKey = (e) => {
      if (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) return;
      if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
      const stage = document.querySelector("deck-stage");
      if (!stage || stage.index !== slideNo - 1) return;
      e.preventDefault();
      e.stopPropagation();
      if (e.key === "ArrowDown") {
        setSel((s) => Math.min(s + 1, features.length - 1));
      } else {
        setSel((s) => Math.max(s - 1, 0));
      }
    };
    window.addEventListener("keydown", onKey, true);
    return () => window.removeEventListener("keydown", onKey, true);
  }, [features.length, slideNo]);

  return (
    <Slide section="CAPABILITIES">
      <Content>
        <NumberedEyebrow>ALSO IN THE BOX</NumberedEyebrow>
        <Title style={{marginTop: 16}}>Highlights from the toolkit</Title>
        <div style={{fontSize: TS.body, color: DK_FG_MUTED, marginTop: 14, maxWidth: 1100}}>
          Click any entry to preview. Source links open the full docs in a new tab.
        </div>

        <div style={{
          flex: 1, minHeight: 0, marginTop: 32,
          display: "grid",
          gridTemplateColumns: "400px 1fr",
          gap: 24,
        }}>
          {/* ── Docs-style sidebar ───────────────────────────── */}
          <div style={{
            position: "relative",
            border: `1px solid ${DK_BORDER}`,
            background: "rgba(13,17,23,0.55)",
            display: "flex", flexDirection: "column",
            overflow: "hidden",
          }}>
            <Brackets size={10} color="rgba(197,174,79,0.35)" inset={-1} thickness={1}/>
            <div style={{
              padding: "16px 22px 14px",
              borderBottom: `1px solid ${DK_BORDER}`,
              fontSize: 11, letterSpacing: "0.3em", color: DK_FG_MUTED,
              display: "flex", justifyContent: "space-between",
            }}>
              <span>DOCS · USING LUDUS</span>
              <span style={{color: GOLD}}>
                {String(sel+1).padStart(2,"0")} / {String(features.length).padStart(2,"0")}
              </span>
            </div>
            <div style={{flex: 1, overflowY: "auto", padding: "6px 0"}}>
              {features.map((f, i) => {
                const on = i === sel;
                return (
                  <button
                    key={f.id}
                    onClick={() => setSel(i)}
                    style={{
                      width: "100%", textAlign: "left",
                      background: on ? "rgba(197,174,79,0.10)" : "transparent",
                      color: on ? DK_FG : DK_FG_MUTED,
                      border: "none",
                      borderLeft: `2px solid ${on ? GOLD : "transparent"}`,
                      padding: "7px 20px",
                      display: "flex", alignItems: "center", gap: 12,
                      cursor: "pointer",
                      fontFamily: "inherit",
                      transition: "background 120ms, color 120ms",
                    }}
                  >
                    <span style={{
                      fontSize: 11, color: on ? GOLD : DK_FG_MUTED,
                      letterSpacing: "0.15em", minWidth: 22,
                    }}>{String(i+1).padStart(2,"0")}</span>
                    <span style={{fontSize: 18, width: 22, textAlign: "center"}}>{f.emoji}</span>
                    <span style={{
                      fontSize: 17, fontWeight: on ? 600 : 500,
                      flex: 1,
                    }}>{f.nav}</span>
                    {f.flagship && (
                      <span style={{
                        fontSize: 9, letterSpacing: "0.25em",
                        color: on ? DK_BG : GOLD,
                        background: on ? GOLD : "transparent",
                        border: on ? "none" : `1px solid ${GOLD}`,
                        padding: "2px 6px",
                        fontWeight: 700,
                      }}>NEW</span>
                    )}
                    <span style={{
                      color: on ? GOLD : "transparent",
                      fontSize: 13,
                    }}>▸</span>
                  </button>
                );
              })}
            </div>
            <div style={{
              borderTop: `1px solid ${DK_BORDER}`,
              padding: "10px 22px",
              fontSize: 10, letterSpacing: "0.25em", color: DK_FG_MUTED,
              display: "flex", justifyContent: "space-between",
            }}>
              <span>↑ ↓ · CLICK TO EXPLORE</span>
              <span style={{color: GOLD}}>v2</span>
            </div>
          </div>

          {/* ── Docs-style content pane ─────────────────────── */}
          <div style={{
            position: "relative",
            border: `1px solid ${active.flagship ? GOLD : DK_BORDER}`,
            background: "rgba(13,17,23,0.55)",
            padding: "24px 40px 28px",
            display: "flex", flexDirection: "column",
            minHeight: 0,
          }}>
            <Brackets
              size={12}
              color={active.flagship ? GOLD : "rgba(197,174,79,0.45)"}
              inset={-1}
              thickness={active.flagship ? 1.5 : 1}
            />

            {/* Breadcrumb */}
            <div style={{
              fontSize: 12, letterSpacing: "0.18em",
              color: DK_FG_MUTED,
              borderBottom: `1px solid ${DK_BORDER_SOFT}`,
              paddingBottom: 14,
              display: "flex", gap: 8, flexWrap: "wrap",
            }}>
              <span>{active.host || "docs.ludus.cloud"}</span>
              {active.path.split("/").filter(Boolean).map((p, i, a) => (
                <React.Fragment key={i}>
                  <span style={{color: DK_BORDER}}>/</span>
                  <span style={{color: i === a.length-1 ? GOLD : DK_FG_MUTED}}>{p}</span>
                </React.Fragment>
              ))}
            </div>

            {/* Title + tagline */}
            <div style={{marginTop: 20, display: "flex", alignItems: "flex-start", gap: 16}}>
              <div style={{fontSize: 46, lineHeight: 1, paddingTop: 4}}>{active.emoji}</div>
              <div style={{flex: 1, minWidth: 0}}>
                <div style={{
                  fontSize: 40, fontWeight: 600, color: DK_FG,
                  lineHeight: 1.1, letterSpacing: "-0.01em",
                }}>
                  {active.nav}
                </div>
                <div style={{
                  fontSize: 19, color: GOLD, marginTop: 8,
                  lineHeight: 1.3,
                }}>
                  {active.tagline}
                </div>
              </div>
              {active.flagship && (
                <div style={{
                  fontSize: 10, letterSpacing: "0.3em", color: DK_BG,
                  background: GOLD, padding: "5px 10px", fontWeight: 700,
                  alignSelf: "flex-start",
                  marginTop: 4,
                }}>NEW IN v2</div>
              )}
            </div>

            {/* Body */}
            <div style={{
              fontSize: 19, color: DK_FG_MUTED,
              lineHeight: 1.5, marginTop: 20,
              maxWidth: 960,
            }}>
              {active.body}
            </div>

            {/* Snippet OR media preview */}
            {active.media ? (
              <div style={{
                marginTop: 22,
                display: "flex", justifyContent: "center",
                flex: "none",
              }}>
                <div style={{
                  border: `1px solid ${DK_BORDER}`,
                  background: "#0a0c10",
                  padding: 8,
                  width: "100%",
                  maxWidth: 500,
                }}>
                  <img
                    src={active.media}
                    alt={active.mediaAlt || ""}
                    style={{
                      display: "block",
                      width: "100%",
                      height: "auto",
                    }}
                  />
                </div>
              </div>
            ) : (
              <div style={{
                marginTop: 22,
                border: `1px solid ${DK_BORDER}`,
                background: "#0a0c10",
                padding: "14px 20px 16px",
                fontSize: 15, lineHeight: 1.65,
              }}>
                <div style={{
                  fontSize: 10, letterSpacing: "0.3em",
                  color: DK_FG_MUTED, marginBottom: 8,
                  display: "flex", justifyContent: "space-between",
                }}>
                  <span>local:~$</span>
                  <span>{active.id.toUpperCase()}</span>
                </div>
                {active.snippet.map((line, i) => {
                  const isComment = line.trim().startsWith("#") || line.trim().startsWith('"');
                  return (
                    <div key={i} style={{
                      color: isComment ? DK_FG_MUTED : DK_FG,
                      whiteSpace: "pre-wrap",
                      fontFamily: "inherit",
                    }}>{line}</div>
                  );
                })}
              </div>
            )}

            {/* Footer: docs link (real anchor, opens in new tab) */}
            <div style={{
              marginTop: "auto", paddingTop: 18,
              borderTop: `1px solid ${DK_BORDER_SOFT}`,
              display: "flex", justifyContent: "space-between",
              alignItems: "center",
              fontSize: 13, letterSpacing: "0.15em",
              color: DK_FG_MUTED,
            }}>
              <span>READ MORE →</span>
              <a
                href={`https://${active.host || "docs.ludus.cloud"}${active.path}`}
                target="_blank"
                rel="noopener noreferrer"
                style={{
                  color: GOLD,
                  textDecoration: "none",
                  borderBottom: `1px dotted ${GOLD}`,
                  paddingBottom: 1,
                }}
              >{active.host || "docs.ludus.cloud"}{active.path}</a>
            </div>
          </div>
        </div>
      </Content>
    </Slide>
  );
}

// ─── SLIDE 20 · LOVE FOR LUDUS ─────────────────────────────────
function Slide21_Love() {
  const tweets = [
    { src: "assets/tweet-adam.png",    name: "Adam Brown",      handle: "@coffeegist" },
    { src: "assets/tweet-chris.png",   name: "Bryce Zuccaro",   handle: "@c4ch3c4d3" },
    { src: "assets/tweet-kaitlyn.png", name: "Chris Thompson",  handle: "@_Mayyhem" },
    { src: "assets/tweet-bryce.png",   name: "rcegan",          handle: "@rcegann" },
    { src: "assets/tweet-rcegan.png",  name: "Kaitlyn DeValk",  handle: "@kaitlyn_devalk" },
  ];
  return (
    <Slide section="SIGNAL">
      <Content>
        <div style={{display:"flex", alignItems:"baseline", justifyContent:"space-between"}}>
          <div>
            <NumberedEyebrow>FIELD REPORTS</NumberedEyebrow>
            <Title style={{marginTop: 16}}>Love for Ludus</Title>
            <div style={{fontSize: TS.body, color: DK_FG_MUTED, marginTop: 14, maxWidth: 1100, lineHeight: 1.45}}>
              Unsolicited. Real posts from operators who use it.
            </div>
          </div>
          <div style={{display:"flex", gap: 26, alignItems:"center"}}>
            <div style={{textAlign:"right", borderLeft:`1px solid ${DK_BORDER}`, paddingLeft: 26}}>
              <div style={{fontSize: 12, letterSpacing:"0.3em", color: DK_FG_MUTED}}>SAMPLE</div>
              <div style={{fontSize: 22, color: DK_FG, marginTop: 6, fontWeight: 500}}>5 <span style={{color: DK_FG_MUTED}}>posts</span></div>
            </div>
          </div>
        </div>

        <div style={{
          flex:1, marginTop: 32, minHeight: 0,
          display:"grid",
          gridTemplateColumns: "1.2fr 1fr 1fr",
          gridTemplateRows: "1fr 1fr",
          gap: 20,
        }}>
          {tweets.map((t, i) => (
            <div key={i} style={{
              position:"relative",
              gridColumn: i === 0 ? "1 / 2" : undefined,
              gridRow:    i === 0 ? "1 / 3" : undefined,
              border: `1px solid ${DK_BORDER}`,
              background: "#0d1117",
              padding: 0,
              display:"flex", flexDirection:"column",
              overflow:"hidden",
            }}>
              <Brackets size={12} color={i === 0 ? GOLD : "rgba(197,174,79,0.4)"} inset={-1} thickness={1.5}/>
              <div style={{
                padding:"10px 16px", borderBottom:`1px solid ${DK_BORDER}`,
                display:"flex", justifyContent:"space-between",
                fontSize: 11, letterSpacing:"0.3em", color: GOLD,
              }}>
                <span>TESTIMONIAL · {String(i+1).padStart(2,"0")}</span>
                <span style={{color: DK_FG_MUTED}}>{t.handle}</span>
              </div>
              <div style={{flex: 1, display:"flex", alignItems:"center", justifyContent:"center", padding: 18}}>
                <img src={t.src} alt={t.name}
                     style={{maxWidth:"100%", maxHeight:"100%", objectFit:"contain"}}/>
              </div>
            </div>
          ))}
        </div>
      </Content>
    </Slide>
  );
}

// ─── SLIDE 21 · OSS vs ENTERPRISE ─────────────────────────────
// ─── SLIDE 22 · WHO'S RUNNING LUDUS ─────────────────────────────
function Slide22_Customers() {
  const segments = [
    {
      id: "01",
      tag: "FORTUNE 100",
      title: "Enterprises",
      items: [
        "Internal red teams",
        "Threat emulation",
        "Detection engineering",
      ],
    },
    {
      id: "02",
      tag: "U.S. FEDERAL",
      title: "Government Agencies",
      items: [
        "OCO operators",
        "Threat intelligence",
        "On-prem, behind the firewall",
      ],
    },
    {
      id: "03",
      tag: "R1 RESEARCH",
      title: "Large Public Universities",
      items: [
        "Cybersecurity curricula",
        "Research labs",
        "CTF infrastructure",
      ],
    },
    {
      id: "04",
      tag: "COMMERCIAL & NON-PROFIT",
      title: "Training Providers",
      items: [
        "Hands-on lab environments",
        "Per-student range isolation",
        "Non-profits and commercial courses",
      ],
    },
  ];

  return (
    <Slide section="TRACTION">
      <Content>
        <div>
          <NumberedEyebrow>DEPLOYED WHERE</NumberedEyebrow>
          <Title style={{marginTop: 16}}>Who's running <span style={{color: GOLD}}>Ludus</span></Title>
        </div>

        {/* 2x2 grid */}
        <div style={{
          flex: 1, marginTop: 40, minHeight: 0,
          display:"grid", gridTemplateColumns: "1fr 1fr", gridTemplateRows: "1fr 1fr",
          gap: 24,
        }}>
          {segments.map((s, i) => (
            <div key={i} style={{
              position:"relative",
              border: `1px solid ${DK_BORDER}`,
              padding: "28px 32px",
              display:"flex", flexDirection:"column",
            }}>
              <Brackets size={10} color={GOLD} inset={-1} thickness={1.2}/>

              {/* Tag */}
              <div style={{
                display:"flex", alignItems:"center", gap: 14,
                fontSize: 11, letterSpacing:"0.3em",
              }}>
                <span style={{
                  color: GOLD, fontWeight: 700,
                  padding:"3px 8px",
                  border: `1px solid ${GOLD}`,
                }}>{s.id}</span>
                <span style={{color: GOLD, fontWeight: 700}}>{s.tag}</span>
              </div>

              {/* Title */}
              <div style={{
                fontSize: 42, fontWeight: 700, color: DK_FG,
                lineHeight: 1.05, letterSpacing: "-0.01em",
                marginTop: 20,
              }}>{s.title}</div>

              {/* Activities */}
              <div style={{
                marginTop: 24, flex: 1,
                display:"flex", flexDirection:"column", gap: 4,
              }}>
                {s.items.map((item, j) => (
                  <div key={j} style={{
                    display:"flex", alignItems:"center", gap: 16,
                    padding:"10px 0",
                  }}>
                    <span style={{
                      fontFamily:"'JetBrains Mono', monospace",
                      fontSize: 12, color: GOLD, fontWeight: 700,
                      width: 30,
                    }}>.0{j+1}</span>
                    <span style={{fontSize: 20, color: DK_FG, fontWeight: 500}}>{item}</span>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>
      </Content>
    </Slide>
  );
}

// ─── SLIDE 23 · EDITIONS ─────────────────────────────────
function Slide23_Comparison() {
  // Plan columns — mirrors ludus.cloud/pricing. Pro NFR is surfaced as a
  // footnote (niche audience, keeps the table readable at deck width).
  const plans = [
    { key: "oss",   name: "Open Source",                          subtitle: "Community edition",    price: "$0",     cadence: "forever",         paid: false },
    { key: "pro",   name: "Pro",                                  subtitle: "For professionals",    price: "$3,500", cadence: "per seat / year", paid: true  },
    { key: "entSH", name: "Enterprise", sub2: "Self-Hosted",      subtitle: "Self-hosted at scale", price: "Custom", cadence: "annual contract", paid: true  },
    { key: "entFH", name: "Enterprise", sub2: "Fully Hosted",     subtitle: "We manage it for you", price: "Custom", cadence: "annual contract", paid: true  },
  ];

  // Cell values: "✓" included, "*" included (closed-source plugins),
  // "-" not in plan, "dev" on roadmap. Keys match plans[].key.
  const groups = [
    {
      label: "CORE · all editions",
      rows: [
        { label: "CLI-driven automation",          oss:"✓", pro:"✓", entSH:"✓", entFH:"✓" },
        { label: "Community role library",         oss:"✓", pro:"✓", entSH:"✓", entFH:"✓" },
        { label: "Open-source*",                   oss:"✓", pro:"*", entSH:"*", entFH:"*" },
      ],
    },
    {
      label: "PRO · Pro tier and above",
      rows: [
        { label: "Web UI for visual range design",          oss:"-", pro:"✓", entSH:"✓", entFH:"✓" },
        { label: "Router roles for turnkey networking",     oss:"-", pro:"✓", entSH:"✓", entFH:"✓" },
        { label: "Pro Roles Catalog",                       oss:"-", pro:"✓", entSH:"✓", entFH:"✓" },
      ],
    },
    {
      label: "SUPPORT",
      rows: [
        { label: "Community support",                       oss:"✓", pro:"✓", entSH:"✓", entFH:"✓" },
        { label: "Priority support · dedicated Discord",    oss:"-", pro:"✓", entSH:"✓", entFH:"✓" },
        { label: "SLA-backed email support",                oss:"-", pro:"-", entSH:"✓", entFH:"✓" },
      ],
    },
    {
      label: "ENTERPRISE · enterprise tiers",
      rows: [
        { label: "Enterprise Roles Catalog (e.g. Zeek)",    oss:"-", pro:"-", entSH:"✓", entFH:"✓" },
        { label: "Outbound WireGuard",                      oss:"-", pro:"-", entSH:"✓", entFH:"✓" },
        { label: "Windows licensing via KMS",               oss:"-", pro:"-", entSH:"✓", entFH:"✓" },
        { label: "CTFd integration",                        oss:"-", pro:"-", entSH:"✓", entFH:"✓" },
        { label: "Fully managed infrastructure & deployment", oss:"-", pro:"-", entSH:"-", entFH:"✓" },
      ],
    },
    {
      label: "COMING SOON · in development",
      rows: [
        { label: "Automated user emulation",                oss:"-", pro:"dev", entSH:"dev", entFH:"dev" },
        { label: "Automated attacks",                       oss:"-", pro:"dev", entSH:"dev", entFH:"dev" },
      ],
    },
  ];

  const Mark = ({ v }) => {
    if (v === "✓")   return <span style={{color: GOLD, fontSize: 22}}>✓</span>;
    if (v === "*")   return <span style={{color: GOLD, fontSize: 22}}>✓<span style={{fontSize: 13, verticalAlign:"super", marginLeft: 2}}>*</span></span>;
    if (v === "dev") return <span style={{fontSize: 10, letterSpacing:"0.2em", color:"#d2c472", fontWeight: 600}}>IN DEV</span>;
    return <span style={{color: "rgba(155,169,180,0.4)", fontSize: 22}}>—</span>;
  };

  const totalRows = groups.reduce((n,g)=>n+g.rows.length, 0);
  const gridCols = "2fr 1fr 1fr 1fr 1fr";

  return (
    <Slide section="OFFERING">
      <Content>
        <div style={{display:"flex", alignItems:"baseline", justifyContent:"space-between"}}>
          <div>
            <NumberedEyebrow>EDITIONS</NumberedEyebrow>
            <Title style={{marginTop: 10}} size={48}>Plans &amp; pricing</Title>
          </div>
          <div style={{fontSize: 12, letterSpacing:"0.3em", color: DK_FG_MUTED, textAlign:"right"}}>
            {totalRows} FEATURES · 4 EDITIONS<br/>
            <span style={{color: GOLD}}>same core engine</span>
          </div>
        </div>

        <div style={{marginTop: 14, display:"grid", gridTemplateColumns: gridCols, gridAutoRows: "min-content", border: `1px solid ${DK_BORDER}`}}>
          {/* Plan header row */}
          <div style={{padding:"8px 20px", borderBottom:`1px solid ${DK_BORDER}`, borderRight:`1px solid ${DK_BORDER}`, background:"rgba(25,28,34,0.7)", display:"flex", alignItems:"flex-end"}}>
            <div style={{fontSize: 11, letterSpacing:"0.3em", color: DK_FG_MUTED}}>CAPABILITY</div>
          </div>
          {plans.map((p, pi) => {
            const isLast = pi === plans.length - 1;
            return (
              <div key={p.key} style={{
                padding:"8px 14px 6px",
                borderBottom: p.paid ? `1px solid ${GOLD}` : `1px solid ${DK_BORDER}`,
                borderRight: isLast ? "none" : `1px solid ${DK_BORDER}`,
                background: p.paid ? "rgba(197,174,79,0.08)" : "rgba(25,28,34,0.7)",
                textAlign:"center",
              }}>
                <div style={{fontSize: 15, fontWeight: 700, color: p.paid ? GOLD : DK_FG, lineHeight: 1.1}}>
                  {p.name}{p.sub2 ? <><br/>{p.sub2}</> : null}
                </div>
                <div style={{fontSize: 10, color: DK_FG_MUTED, marginTop: 3, letterSpacing:"0.08em"}}>{p.subtitle}</div>
                <div style={{fontSize: 16, fontWeight: 700, color: DK_FG, marginTop: 4, letterSpacing:"-0.01em"}}>{p.price}</div>
                <div style={{fontSize: 10, color: DK_FG_MUTED, marginTop: 1, letterSpacing:"0.08em"}}>{p.cadence}</div>
              </div>
            );
          })}

          {groups.map((g, gi) => (
            <React.Fragment key={gi}>
              {/* Group divider spanning all 5 cols */}
              <div style={{
                gridColumn: "1 / -1",
                padding: "4px 20px",
                borderTop: gi === 0 ? "none" : `1px solid ${DK_BORDER}`,
                borderBottom: `1px solid ${DK_BORDER}`,
                background: "#0d1117",
                fontSize: 10, letterSpacing:"0.4em", color: GOLD, fontWeight: 700,
              }}>
                {g.label}
              </div>
              {g.rows.map((r, i) => {
                const last = gi === groups.length - 1 && i === g.rows.length - 1;
                return (
                  <React.Fragment key={i}>
                    <div style={{
                      padding:"2px 20px",
                      borderBottom: last ? "none" : `1px solid ${DK_BORDER_SOFT}`,
                      borderRight: `1px solid ${DK_BORDER}`,
                      fontSize: 13, color: DK_FG,
                      display:"flex", alignItems:"center",
                    }}>{r.label}</div>
                    {plans.map((p, pi) => {
                      const isLastCol = pi === plans.length - 1;
                      return (
                        <div key={p.key} style={{
                          padding:"2px 12px",
                          borderBottom: last ? "none" : `1px solid ${DK_BORDER_SOFT}`,
                          borderRight: isLastCol ? "none" : `1px solid ${DK_BORDER}`,
                          textAlign:"center",
                          background: p.paid ? "rgba(197,174,79,0.05)" : "transparent",
                          display:"flex", alignItems:"center", justifyContent:"center",
                        }}><Mark v={r[p.key]}/></div>
                      );
                    })}
                  </React.Fragment>
                );
              })}
            </React.Fragment>
          ))}
        </div>

        <div style={{marginTop: 8, display:"flex", justifyContent:"space-between", alignItems:"center", gap: 24}}>
          <div style={{display:"flex", gap: 18, fontSize: 11, letterSpacing:"0.12em", color: DK_FG_MUTED, flexWrap:"wrap", alignItems:"center"}}>
            <span><span style={{color: GOLD}}>✓</span> Included</span>
            <span><span style={{color: GOLD}}>✓*</span> Core open-source · closed-source plugins</span>
            <span><span style={{color:"rgba(155,169,180,0.5)"}}>—</span> Not available</span>
            <span><span style={{color:"#d2c472", fontWeight: 600}}>IN DEV</span> Roadmap</span>
            <span style={{color: DK_FG_MUTED, textTransform:"none", letterSpacing:"0.02em"}}>Pro NFR (Not For Resale) also available at $0/yr for qualified users.</span>
          </div>
          <div style={{fontSize: 13, letterSpacing:"0.2em", color: GOLD, whiteSpace:"nowrap"}}>
            → ludus.cloud / pricing
          </div>
        </div>
      </Content>
    </Slide>
  );
}

Object.assign(window, {
  Slide16_AI, Slide17_ThreatEm, Slide18_VulnScan, Slide19_ThreatIntel,
  Slide20_OtherFeatures, Slide21_Love, Slide22_Customers, Slide23_Comparison,
});
