// ─────────────────────────────────────────────────────────────────────────
// content.jsx — blockscanner content schema
//
// HANDOFF NOTE: this is the single source of truth for all copy on the site.
// The backend agent should edit values in this file (and only this file)
// when filling in real content. Page templates render from these objects.
//
// Every product entry has the same shape; every page section has obvious
// named fields. Keep the *shape*; replace the *values*.
// ─────────────────────────────────────────────────────────────────────────

const SITE = window.SITE || {
  name: "blockscanner",
  domain: "blockscanner.org",
  email: "hello@blockscanner.org",
  tagline: "Security research & engineering studio",
  established: "2024",
  domains: "EVM · Solana · Bitcoin · Web infrastructure",
};
window.SITE = SITE;

const NAV = window.NAV || [
  { label: "Products",  href: "/#products" },
  { label: "Research",  href: "/research.html" },
  { label: "About",     href: "/about.html" },
  { label: "Contact",   href: "/#contact" },
];
window.NAV = NAV;

// ─── Feature flags ───────────────────────────────────────────────────────
// Runtime overrides arrive via /api/config/bootstrap.js (loaded synchronously
// before this file) and land on window.FEATURES. The fallback below applies
// only if the bootstrap fetch failed at page load. NEVER reference FEATURES
// at file scope — always read from window.FEATURES inside a component so
// backend toggles (set on window before app boot) actually take effect.
const FEATURES = window.FEATURES || {
  auth:             false,  // Sign-up CTA in nav · /auth/* pages active
  dropdownNav:      false,  // "Products" becomes a hover mega-menu
  dashboardPreview: false,  // "See the platform" tile on landing
  toasts:           false,  // In-page toast notifications
  scheduleCall:     false,  // Extra Calendly CTA on contact form
  liveStatus:       false,  // Live system-status row in footer
  signup_mode:      "invite_only", // invite_only | open | closed
};
// Re-publish the merged object so every consumer sees one identity.
window.FEATURES = FEATURES;

const SCHEDULE_URL = "https://cal.com/blockscanner/intro"; // placeholder

// ─── Landing hero ────────────────────────────────────────────────────────
const HERO = window.HERO || {
  // Tiny line above the headline.
  eyebrow: "Security research & engineering",
  // Use line breaks (\n) where you want a hard break in the headline.
  // Two short, declarative lines reads stronger than three medium ones.
  headline: "Program-analysis engines\nfor systems attackers know better than auditors.",
  // One paragraph below the headline. Aim for ~30 words.
  lede:
    "Blockscanner is the analysis engine — taint analysis feeding symbolic execution, across " +
    "EVM, Solana, and Bitcoin bytecode. Parallel EVM is the throughput layer underneath. " +
    "Live Audit packages both into a managed-service platform on the Anthropic API.",
  // Pair of inline link-style CTAs (Apple-style). Keep these short.
  ctas: [
    { label: "See the engines", href: "#products" },
    { label: "Research index",  href: "/research.html" },
  ],
};
window.HERO = HERO;

// ─── Stat band (sits between hero and the TOC) ───────────────────────────
// Four big numbers. Each is one visual beat — short value, single-line label.
// Each stat is clickable: clicking opens an inline detail card (Apple-style)
// with a longer blurb, a few key facts, and a link to the deeper page.
const STATS = window.STATS || [
  {
    value: "9 GGas/s",
    label: "Parallel EVM\nmainnet-replay throughput",
    detail: {
      title: "Throughput, measured on real chain history.",
      body:
        "Parallel EVM (codename reth-turbo) reimplements reth's stages 5–14 on a Block-STM " +
        "scheduler with a 16-way sharded state trie. On a 5.4M-block historical mainnet " +
        "replay it sustains ~9 GGas/s. Independent head-to-head against reth / erigon / geth " +
        "is queued; the bench harness is in-tree and the run is published the same week as " +
        "the v1.0-beta tag.",
      facts: [
        ["Replay",  "5.4M blocks, mainnet"],
        ["Throughput", "~9 GGas/s sustained"],
        ["Storage", "turbo-db, 16-shard LSM"],
        ["Use",     "replay · fuzzing · re-audit · proposer"],
      ],
      cta: { label: "Parallel EVM — deep dive", href: "/product.html?id=parallel-evm" },
    },
  },
  {
    value: "3",
    label: "Bytecodes analyzed\nEVM · SOL · BTC",
    detail: {
      title: "Three bytecodes, one engine.",
      body:
        "EVM and Solana BPF are the primary surface — different state models, different attack " +
        "idioms, one shared analyzer. Bitcoin analysis covers Taproot script paths, Lightning " +
        "channel state, PSBT signing flows, and Schnorr nonce / weak-RNG key extraction. Every " +
        "analyzer ships against all three backends; findings on one inform the others.",
      facts: [
        ["EVM",    "Solidity · Vyper · Yul · raw bytecode"],
        ["Solana", "Anchor · Native · raw BPF"],
        ["Bitcoin","Taproot · Lightning · PSBT · Schnorr"],
        ["Shared", "Lifter → IR → analyzers"],
      ],
      cta: { label: "See the engines", href: "/#products" },
    },
  },
  {
    value: "4",
    label: "Primitives in\nactive development",
    detail: {
      title: "Four primitives, one pipeline.",
      body:
        "Blockscanner — the analysis engine, taint feeding symbolic across three bytecodes. " +
        "Parallel EVM — the throughput layer (reth-turbo). turbo-db — the parallel-first " +
        "storage family underneath. Live Audit — the managed-service platform that orchestrates " +
        "everything via the Anthropic API. Every product builds on the layer below it.",
      facts: [
        ["Analysis",   "Blockscanner engine"],
        ["Throughput", "Parallel EVM (reth-turbo)"],
        ["Storage",    "turbo-db (16-shard LSM)"],
        ["Platform",   "Live Audit (Anthropic API)"],
      ],
      cta: { label: "Read the product index", href: "/#products" },
    },
  },
  {
    value: "7",
    label: "Vulnerability classes\nunder research",
    detail: {
      title: "Validated novel classes, not vague claims.",
      body:
        "Seven high-confidence vulnerability classes — two confirmed mathematically (BN256 " +
        "scalar field overflow in ZK verifiers, identity-precompile balance facade), five " +
        "logically proven from spec mechanics (Solana secp256k1 index redirect, Token-2022 " +
        "confidential-transfer plaintext desync, Token-2022 interest-bearing manipulation, " +
        "BASEFEE-gated liquidation, precompile gas failure on unchecked return). Dozens more " +
        "vectors are tracked at lower validation tiers.",
      facts: [
        ["Proven",          "2 (locally confirmed)"],
        ["Logically proven","5 (spec mechanics)"],
        ["Tracked",         "35+ original + 12 extended"],
        ["Scope",           "EVM · SOL · ZK · L2 · BTC"],
      ],
      cta: { label: "Research index", href: "/research.html" },
    },
  },
];
window.STATS = STATS;

// ─── Products (5 cards) ──────────────────────────────────────────────────
// id           — unique slug, used as anchor and in URLs
// eyebrow      — small uppercase line above the title
// short        — short name for TOC / footer / cross-refs
// title        — the headline of the section
// body         — one paragraph (~3–5 sentences). Plain string.
// facts        — array of [label, value] pairs, rendered in a 2-col grid
// figureKey    — key for the SVG visual: "saas" | "revm" | "symbolic" | "taint" | "research"
// caption      — sentence under the figure
// footnote     — optional small italic note at end of section (null to hide)
const PRODUCTS = window.PRODUCTS || [
  {
    id: "saas",
    eyebrow: "Managed-service audit platform · private beta",
    short: "Live Audit",
    title: "Live audit, operated by us, on the Anthropic API.",
    body:
      "Operator-only managed-service: clients submit targets and authorization, our team runs " +
      "the platform end-to-end, clients receive finished reports. Lead reasoning is Claude " +
      "Opus 4.7; parallel Sonnet 4.6 subagents handle deep-dives. Three-layer context discipline " +
      "(Programmatic Tool Calling for fan-out, parallel `messages.create()` subagents, server-side " +
      "compaction) keeps each engagement at ~$5 of model cost.",
    facts: [
      ["Lead model", "Claude Opus 4.7"],
      ["Subagents",  "Sonnet 4.6, parallel `messages.create()`"],
      ["Retrieval",  "pgvector + BM25 + Voyage rerank"],
      ["Re-audit",   "lineage-aware engagements"],
    ],
    figureKey: "saas",
    caption: "Commit-cadence re-analysis across a protocol's history.",
    footnote:
      "Private beta — operator-only. First demo target is a Sushi engagement; client-facing " +
      "engagement API is pre-MVP.",
    // Long-form content rendered by product.html?id=saas
    deepDive: {
      tagline: "Managed-service audit platform on the Anthropic API.",
      overview:
        "Most audits are point-in-time. A protocol is audited, then ships fifty pull " +
        "requests over the next six months, and the report quietly becomes a description " +
        "of a codebase that no longer exists.\n\n" +
        "Live Audit is the managed-service answer: our team operates the platform on your " +
        "authorization, every engagement is orchestrated by a Claude Opus 4.7 lead loop with " +
        "parallel Sonnet 4.6 subagents, and every finding ships with reasoning a human auditor " +
        "could put their name on. Re-audits inherit lineage from prior engagements so you " +
        "aren't paying to rediscover the same surface every quarter.",
      principles: [
        ["Operator-only by design.",    "You authorize a target, we run the platform, you receive the report. No client console, no shared model context."],
        ["Three-layer context discipline.", "PTC for fan-out, parallel subagents for deep dives, server-side compaction as safety net. Without this, the model cost balloons 10–50×."],
        ["Reasoning, not just signatures.", "Findings ship with concrete reproduction inputs, exploitability ranking, and a remediation patch — not just a detector firing."],
        ["Re-audit lineage.",            "Every engagement can branch from a prior one. The system carries its own institutional memory across audits of the same protocol."],
      ],
      methodology:
        "The platform is a TypeScript orchestrator on Node 22 LTS, talking to the Anthropic API " +
        "via the official SDK. RAG corpus lives in Postgres 16 + pgvector — hybrid retrieval " +
        "(pgvector ANN + tsvector BM25 + RRF + Voyage `rerank-2.5`) over 14 registered corpora " +
        "including the live `alex-traces` set (1,445 sessions). Every tool invocation passes " +
        "through guardrail middleware (scope, write-size, retry, state, isolation, " +
        "asset-grounding) and lands in an append-only audit log. Findings progress through a " +
        "state machine (draft → validated → reported) with artifact-verification gates at " +
        "each transition.",
      examples: [
        { kind: "diagram", figure: "saas", caption: "Per-commit analysis cadence across a protocol's history." },
        { kind: "code", lang: "text",
          body:
            "Finding BS-AUDIT-2401  ·  HIGH  ·  introduced in 4a7c9d\n" +
            "Vault.withdraw() can be called by an account whose authority\n" +
            "flag is unset when the vault is in `paused` state. Reachable\n" +
            "from external calldata. Reproduces with deposit() → setAuth(false)\n" +
            "→ setPaused(true) → withdraw().",
          caption: "A finding as it lands in a PR review." },
      ],
    },
  },
  {
    id: "parallel-evm",
    eyebrow: "Execution + storage · Rust",
    short: "Parallel EVM",
    title: "Parallel EVM execution + parallel-first storage.",
    body:
      "Two engines, one stack. The executor (codename reth-turbo) is a reimplementation of " +
      "reth's stages 5–14 on a Block-STM scheduler with a 16-way sharded state trie — drop-in " +
      "follower today, MEV-boost / Flashbots-bundle proposer behind it. Underneath sits turbo-db, " +
      "a parallel-first storage family (16-shard LSM + B+ tree + integrated Merkle Patricia Trie, " +
      "26 product crates). On a 5.4M-block historical mainnet replay the stack sustains ~9 GGas/s.",
    facts: [
      ["Throughput", "~9 GGas/s, 5.4M-block replay"],
      ["Executor",   "reth-turbo, Block-STM, 16-shard trie"],
      ["Storage",    "turbo-db, lock-free reads, mmap'd indices"],
      ["Use",        "replay · fuzzing · re-audit · proposer"],
    ],
    figureKey: "parallel-evm",
    caption: "Throughput on a 5.4M-block historical mainnet replay.",
    featureNumber: "~9 GGas/s",
    featureLabel: "sustained on a 5.4M-block\nmainnet replay",
    footnote:
      "Independent head-to-head versus reth / erigon / geth is queued for the v1.0-beta " +
      "publication; the bench harness is in-tree.",
    deepDive: {
      tagline: "The throughput layer underneath everything we ship.",
      overview:
        "Auditing is bottlenecked on execution. Replaying a protocol's history, running a " +
        "fuzzing campaign, or re-auditing every commit all require running the EVM at a " +
        "speed sequential clients were never designed for.\n\n" +
        "reth-turbo reimplements reth's stages 5–14 on a Block-STM scheduler with a 16-way " +
        "sharded state trie. turbo-db underneath uses lock-free reads, crossbeam-skiplist " +
        "memtables, and mmap'd B+ tree indices. On the `defi_heavy_bench` workload, single-block " +
        "execution dropped from 43 ms/block to 1.64 ms/block over the 0.7→0.13 release series. " +
        "End-to-end, the pipeline sustains ~9 GGas/s on a 5.4M-block mainnet replay.",
      principles: [
        ["Optimistic by default.",       "Schedule transactions as if no conflict exists. Detect and re-run on actual conflict (Block-STM, MVDS, NEMO scheduler)."],
        ["Sharded state, not flat.",     "16-way sharded state trie keeps the contention surface small enough that the parallel path actually wins on real DeFi workloads."],
        ["Cache-aware commit pipeline.", "The commit stage is engineered to keep hot state in L2/L3. Cold paths don't poison the cache."],
        ["Storage and exec, co-designed.", "turbo-db is shaped around exactly the read/write patterns reth-turbo emits — not a generic KV with a contract layer on top."],
      ],
      methodology:
        "Block execution: a work-stealing scheduler dispatches transactions speculatively " +
        "against the latest committed state, each worker recording its read/write set. A " +
        "serial commit thread merges results in block order, re-running any transaction whose " +
        "read set intersects a committed write set. State trie root computation parallelizes " +
        "across the 16 shards (measured 3.3–3.6× speedup on storage-roots). Proposer mode " +
        "layers in a MEV-boost adapter, Flashbots bundle pipeline, web3signer-backed signing, " +
        "and a payload builder; both native `eth_sendBundle` and `private_sendTransaction` are " +
        "exposed on the RPC surface.",
      examples: [
        { kind: "diagram", figure: "parallel-evm", caption: "Throughput on a 5.4M-block mainnet replay." },
        { kind: "code", lang: "rust",
          body:
            "use reth_turbo::{ParallelExecutor, StateDb};\n\n" +
            "let db = StateDb::from_snapshot(snap)?;\n" +
            "let exec = ParallelExecutor::new(db, threads(num_cpus::get()));\n" +
            "let results = exec.run_block(block)?;\n" +
            "// ~9 GGas/s sustained on the 5.4M-block replay harness.",
          caption: "Calling the parallel executor." },
      ],
    },
  },
  {
    id: "blockscanner",
    eyebrow: "Program analysis · EVM + Solana + Bitcoin",
    short: "Blockscanner",
    title: "Taint analysis feeding symbolic execution, across three bytecodes.",
    body:
      "The engine. Taint analysis identifies which untrusted sources reach sensitive sinks; " +
      "symbolic execution then resolves the exact preconditions and adversarial inputs that " +
      "trigger each path. The taint engine's output is the symbolic engine's input — the two " +
      "are one pipeline, not two products. EVM bytecode and Solana BPF are the primary surface " +
      "(35+ EVM detectors, 30+ Solana detectors); Bitcoin coverage spans Taproot script paths, " +
      "Lightning channel state, PSBT signing flows, and Schnorr nonce / weak-RNG key extraction " +
      "(30+ Bitcoin detectors). Every finding ships with a concrete reproduction witness, not " +
      "a heuristic warning.",
    facts: [
      ["Pipeline",  "taint → symbolic (Z3-backed)"],
      ["Targets",   "EVM bytecode · Solana BPF · Bitcoin"],
      ["Detectors", "35+ EVM · 30+ SOL · 30+ BTC"],
      ["Output",    "concrete exploit witness + flow trace"],
    ],
    figureKey: "blockscanner",
    caption: "Taint identifies the sink; the symbolic engine resolves the path to reach it.",
    footnote: null,
    deepDive: {
      tagline: "One engine, two analyzers chained, three bytecodes.",
      overview:
        "Most onchain bugs are flow bugs. Untrusted data reaches a privileged check; a " +
        "low-significance value reaches an arithmetic operation that decides who gets paid; " +
        "calldata from a foreign program reaches an account's authority byte. Pattern-matching " +
        "tools miss these because they require following data, not matching syntax.\n\n" +
        "Blockscanner runs taint analysis first — every untrusted source carries a color, " +
        "every sensitive sink declares what colors are forbidden, every flow that mixes them " +
        "is a candidate finding. The symbolic engine then takes those candidates and resolves " +
        "exactly what inputs satisfy the path — backward-tracing through a Z3-backed solver " +
        "from the sink to its concrete trigger. The output is a calldata payload (or Solana " +
        "transaction, or Bitcoin script witness) that reproduces the bug on a forked node.",
      principles: [
        ["Taint feeds symbolic.",             "Two analyzers, one pipeline. Symbolic execution starts at sinks the taint engine flagged — not at function entries, which is why we don't drown in unreachable paths."],
        ["Concrete witnesses, not warnings.", "Every finding ships with the calldata / transaction / script witness that reproduces it on a forked node."],
        ["Three bytecodes, one engine.",      "EVM, Solana BPF, and Bitcoin script share the analyzer; only the lifter and the sink catalog differ per backend."],
        ["Field-sensitive, inter-procedural.","Taint is tracked per struct field (not per allocation) and follows flows across contract / program / transaction boundaries."],
      ],
      methodology:
        "Bytecode is lifted to a shared IR. The taint analyzer walks the IR with " +
        "category-specific source/sink catalogs per backend — on EVM, sources are calldata / " +
        "external-call returns / cross-owner storage reads, sinks are balance writes and " +
        "authority modifiers; on Solana, sources are program inputs and foreign-program CPI " +
        "returns, sinks are owner / authority / rent writes; on Bitcoin, sources are witness " +
        "stack inputs, sinks are signature verification with attacker-influenced nonces. " +
        "Flagged sinks then feed the symbolic engine, which builds an SMT formula by walking " +
        "backward from the sink through the IR, asking Z3 at each branch whether the predicate " +
        "is satisfiable. Reaching a feasible source yields a finding with a concrete model.",
      examples: [
        { kind: "diagram", figure: "blockscanner", caption: "Taint identifies the sink; the symbolic engine resolves the path to reach it." },
        { kind: "code", lang: "text",
          body:
            "Finding BS-T-2401  ·  HIGH  ·  Solana\n" +
            "Taint:    calldata[0..32] (source: external)\n" +
            "            → cpi_invoke(target_program, accounts[3])\n" +
            "              → accounts[3].owner = $source  (sink: authority)\n" +
            "Symbolic: backward from sink — SAT under\n" +
            "            calldata[0..32] = 0xdeadbeef..., target_program = $X\n" +
            "Witness:  one transaction with the above calldata rewrites the\n" +
            "          target account's owner to a value the attacker controls.",
          caption: "Taint-flagged sink resolved by the symbolic engine into a one-transaction witness." },
      ],
    },
  },
  {
    id: "research",
    eyebrow: "Research · under disclosure cadence",
    short: "Research",
    title: "Vulnerability classes under research, on a private-first cadence.",
    body:
      "An internal vector registry tracking 35+ original vulnerability classes plus a 12-vector " +
      "extended set added recently. Seven are high-confidence (two mathematically proven, five " +
      "logically proven from spec mechanics); the rest sit at lower validation tiers awaiting " +
      "specific test infrastructure. Scope spans EVM (ZK precompile field overflow, BASEFEE " +
      "liquidation, Token-2022 / ERC-4337 / Uniswap V4 hook mechanics), Solana (secp256k1 index " +
      "redirect, Token-2022 confidential-transfer desync, program-cache invalidation, ALT " +
      "poisoning), and Bitcoin (Taproot, Lightning, PSBT, Schnorr). Pre-disclosure entries are " +
      "held in the internal index; class-level summaries publish once research is validated " +
      "and any affected parties have patched.",
    facts: [
      ["Tracked",         "35+ original · 12 extended"],
      ["Proven",          "2 mathematically · 5 logically"],
      ["Validation tiers","proven · logical · needs-test · monitoring"],
      ["Disclosure",      "private-first; class-level publication"],
    ],
    figureKey: "research",
    caption: "Internal class index — pre-disclosure entries redacted.",
    footnote: null,
    deepDive: {
      tagline: "Validated novel classes, on a private-first disclosure cadence.",
      overview:
        "Some of what our analyzers find isn't in any public advisory, audit checklist, or " +
        "open-source scanner. The internal vector registry tracks each candidate through " +
        "explicit validation tiers — proven (mathematically confirmed locally), logically " +
        "proven (spec mechanics imply the attack but it needs a runtime test), needs-test " +
        "(specific testnet / client / fork required), monitoring (persistent daemon, not a " +
        "one-shot detector), and rarely-scanned known classes (real categories that no public " +
        "tool reliably catches).\n\n" +
        "Pre-disclosure entries stay private until the affected parties have a patched build " +
        "deployed. Class-level summaries publish once that window closes; instance-level details " +
        "stay with verification partners under NDA. The cadence is private-first, not " +
        "embargo-as-PR — the validation tiers matter more than the marketing.",
      principles: [
        ["Validate before build.",            "Every novel vector is confirmed mathematically, via local test, or via fork simulation BEFORE a detector is implemented. We don't ship detectors for unvalidated theories."],
        ["Class-level publication.",          "We publish what kind of bug it is, not where it lives — so the pattern spreads, not the exploit."],
        ["Private-first cadence.",            "Affected parties get the report and a patch window. Public writeup follows the window, not the discovery."],
        ["Credit researchers.",               "External submitters are named on the advisory unless they ask not to be."],
      ],
      methodology:
        "Each candidate from the engines lands in the vector registry with a validation tier " +
        "and a proof plan. Mathematical proofs run locally (e.g., the BN256 scalar field " +
        "overflow proof is `ecmul(G, 100) == ecmul(G, 100 + r)` confirmed on a deployed " +
        "verifier). Logical proofs are walked through spec mechanics and require a runtime " +
        "test before being upgraded to proven (e.g., the Solana secp256k1 instruction-index " +
        "redirect crafts a transaction where the program reads precompile output from `ix[N]` " +
        "instead of `ix[M]`). Vectors that need specific infrastructure (Pectra devnet, " +
        "Firedancer, Token-2022 testnet, Cancun-fork RPC) sit at `needs-test` until the harness " +
        "is built. Monitoring-class vectors become persistent daemons rather than one-shot " +
        "scanners — Lido oracle frontrun, Curve gauge weight TOCTOU, Wormhole VAA replay, etc.",
      examples: [
        { kind: "diagram", figure: "research", caption: "Internal class index — pre-disclosure entries redacted." },
      ],
    },
  },
  {
    id: "web-infra",
    eyebrow: "Web infrastructure · v2 in progress",
    short: "Web Infra",
    title: "WAF-bypass fuzzer — coming back, highly improved.",
    body:
      "A custom WAF-bypass fuzzer with documented stored-XSS bypass vectors through Cloudflare " +
      "on a controlled target (51 documented scan runs against disboard.org). The first " +
      "generation served owner-conducted research; the v2 rewrite — broader payload generation, " +
      "modern WAF-feature coverage, automated bypass-class classification — is the current " +
      "web-infrastructure track alongside the onchain work. Engagement availability returns " +
      "with v2.",
    facts: [
      ["Scope",     "WAF bypass · web-app vulnerabilities"],
      ["v1 result", "documented Cloudflare XSS bypass vectors"],
      ["v2 focus",  "broader payloads · modern WAF features"],
      ["Status",    "v2 in development"],
    ],
    figureKey: "web-infra",
    caption: "Bypass fuzzer outputs ranked by WAF response class.",
    footnote: null,
    deepDive: {
      tagline: "WAF-bypass fuzzer, v2 in progress.",
      overview:
        "The original WAF-bypass fuzzer was authored alongside the onchain analyzers and " +
        "served research engagements on owner-conducted targets — 51 documented scan runs " +
        "against a Yii2/PHP form behind Cloudflare produced a catalog of stored-XSS bypass " +
        "vectors.\n\n" +
        "The v2 rewrite broadens payload generation (modern XSS classes, prototype-pollution " +
        "vectors, JSON / GraphQL surfaces), adds automated bypass-class classification by " +
        "response-header fingerprint, and aims for first-class support on current Cloudflare / " +
        "AWS / Akamai feature sets. Web-infrastructure engagements pause until v2 ships.",
      principles: [
        ["Controlled targets only.",    "Engagement scope and authorization are signed before any fuzzing. No untargeted scans."],
        ["Bypass classes, not strings.","Findings are categorized by which WAF feature was evaded, not by the raw payload that worked. The class is the artifact; the payload changes weekly."],
        ["Composable with the onchain stack.", "When a web-infra finding touches blockchain infrastructure (RPC frontend, custody portal, signer UI), the report ties into the relevant onchain analyzer's surface."],
      ],
      methodology:
        "v2 generates payloads from a feature-class catalog (script-injection, CSS-injection, " +
        "SVG-injection, polyglot-payloads, parser-confusion) rather than from a corpus of known " +
        "strings. Each candidate is dispatched against the target with response-header " +
        "fingerprinting; bypasses are classified by which WAF subsystem was evaded. Bypass " +
        "classes that survive across multiple WAF vendors are escalated for coordinated " +
        "disclosure to the affected vendor.",
      examples: [
        { kind: "diagram", figure: "web-infra", caption: "Bypass-class distribution across documented WAF features." },
      ],
    },
  },
];
window.PRODUCTS = PRODUCTS;

// ─── Contact ─────────────────────────────────────────────────────────────
const CONTACT = window.CONTACT || {
  eyebrow: "Engagements",
  headline: "Pre-release verification, retrospective analysis, incident response.",
  lede:
    "We work directly with protocol teams, exchanges, and infrastructure companies. " +
    "The form below is the fastest way in — a short paragraph about what you're securing " +
    "is enough to get a reply.",
  // Organization-type options for the select. Adjust freely.
  orgTypes: [
    "Protocol / DeFi team",
    "CEX / custody",
    "Web infrastructure",
    "Enterprise security",
    "Researcher",
    "Verification partner",
    "Other",
  ],
  // Free-response field prompt — the most important input on the form.
  promptText:
    "What are you trying to secure, and what's pushed you past traditional auditing?",
};
window.CONTACT = CONTACT;

// ─── Research index page ─────────────────────────────────────────────────
// COUNTS reflect the vector-registry tallies — 2 mathematically proven, 5
// logically proven from spec mechanics, 47 total tracked (35 original + 12
// extended). DISCLOSURE rows stay redacted at the class level; the registry
// itself is public, instance-level details stay with verification partners.
const RESEARCH = window.RESEARCH || {
  eyebrow: "Research index",
  headline: "Vulnerability classes under research, validated by tier.",
  lede:
    "Blockscanner's analyzers maintain a vector registry — every candidate vulnerability " +
    "class lands here with an explicit validation tier and a proof plan. The registry " +
    "shape is public; pre-disclosure instance-level details stay with verification partners " +
    "under NDA until any affected parties have patched.",
  // Three top-line metrics, real numbers from the vector registry.
  counts: [
    { value: "7",   label: "High-confidence classes\n(2 proven · 5 logically proven)" },
    { value: "47",  label: "Vectors tracked\n(35 original · 12 extended)" },
    { value: "10",  label: "Monitoring-class\n(persistent daemons)" },
  ],
  // Anonymized index table. Class-level rows stay redacted; status reflects
  // the validation tier from DEEP_ANALYSIS_PLAN.
  rows: [
    { ref: "BS-Z01", domain: "EVM",    family: "ZK precompile field overflow",      discovered: "2026 · Q1", status: "proven · pre-disclosure",  protocols: "[redacted]" },
    { ref: "BS-E01", domain: "EVM",    family: "Precompile identity facade",        discovered: "2026 · Q1", status: "proven · pre-disclosure",  protocols: "[redacted]" },
    { ref: "BS-S01", domain: "Solana", family: "Precompile instruction redirect",   discovered: "2026 · Q2", status: "logical · needs runtime",  protocols: "[redacted]" },
    { ref: "BS-S02", domain: "Solana", family: "Token-2022 plaintext desync",       discovered: "2026 · Q2", status: "logical · needs runtime",  protocols: "[redacted]" },
    { ref: "BS-S03", domain: "Solana", family: "Token-2022 rate manipulation",      discovered: "2026 · Q2", status: "logical · needs runtime",  protocols: "[redacted]" },
    { ref: "BS-E02", domain: "EVM",    family: "BASEFEE-gated liquidation",         discovered: "2026 · Q2", status: "logical · needs runtime",  protocols: "[redacted]" },
    { ref: "BS-E03", domain: "EVM",    family: "Precompile gas-failure return",     discovered: "2026 · Q2", status: "logical · needs runtime",  protocols: "[redacted]" },
  ],
  disclosure: {
    heading: "Disclosure policy",
    body:
      "Findings are withheld until affected parties have a patched build deployed. We follow " +
      "a private-first cadence: report, fix, deploy, then publish the class-level summary. " +
      "Instance-level details and affected-protocol lists stay with verification partners " +
      "under NDA — what publishes is the bug class, not the bug.",
    points: [
      ["Step 1", "Private notification to affected parties."],
      ["Step 2", "Patch, deploy, and confirm onchain settlement."],
      ["Step 3", "Quiet window negotiated with the affected party."],
      ["Step 4", "Class-level summary added to the public index; instance-level details stay with partners."],
    ],
  },
  access: {
    heading: "Requesting access",
    body:
      "Scoped access to pre-disclosure entries is available to verification partners, affected " +
      "protocol teams, and security peers under signed NDA. Use the contact form on the " +
      "main page to start a conversation.",
  },
};
window.RESEARCH = RESEARCH;

// ─────────────────────────────────────────────────────────────────────────
// Theme + small visuals — generic, won't need to be edited per content change.
// ─────────────────────────────────────────────────────────────────────────

function useScopedTheme(rootRef) {
  const getSystem = () =>
    typeof window !== "undefined" && window.matchMedia &&
    window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
  const [theme, setTheme] = React.useState(getSystem);
  const [userOverride, setUserOverride] = React.useState(false);
  React.useEffect(() => {
    if (userOverride) return;
    const mq = window.matchMedia("(prefers-color-scheme: dark)");
    const handler = (e) => setTheme(e.matches ? "dark" : "light");
    mq.addEventListener?.("change", handler);
    return () => mq.removeEventListener?.("change", handler);
  }, [userOverride]);
  React.useEffect(() => {
    if (rootRef.current) rootRef.current.setAttribute("data-theme", theme);
  }, [theme, rootRef]);
  const toggle = () => { setUserOverride(true); setTheme((t) => t === "dark" ? "light" : "dark"); };
  return [theme, toggle];
}

function ThemeToggle({ theme, onToggle, className }) {
  return (
    <button type="button" className={className} onClick={onToggle}
      aria-label={theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}>
      {theme === "dark" ? (
        <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.2">
          <circle cx="8" cy="8" r="3" />
          <path d="M8 1v2M8 13v2M1 8h2M13 8h2M3 3l1.4 1.4M11.6 11.6 13 13M3 13l1.4-1.4M11.6 4.4 13 3" strokeLinecap="round" />
        </svg>
      ) : (
        <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.2">
          <path d="M13 9.5A5.5 5.5 0 0 1 6.5 3a5.5 5.5 0 1 0 6.5 6.5z" />
        </svg>
      )}
    </button>
  );
}

// ─── SVG figures used in product sections ────────────────────────────────

function FlowDiagram() {
  return (
    <svg viewBox="0 0 600 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <g stroke="currentColor" fill="none" strokeWidth="1">
        <circle cx="60" cy="60" r="18" />
        <circle cx="60" cy="160" r="18" />
        <rect x="260" y="80" width="80" height="60" />
        <circle cx="540" cy="110" r="22" />
        <path d="M78 60 Q170 60 260 100" />
        <path d="M78 160 Q170 160 260 120" />
        <path d="M340 110 L518 110" />
      </g>
      <g fill="currentColor" opacity="0.4" fontFamily="ui-monospace, monospace" fontSize="10" letterSpacing="0.08em">
        <text x="38" y="34">SRC.A</text>
        <text x="38" y="194">SRC.B</text>
        <text x="270" y="74">TRANSFORM</text>
        <text x="522" y="148">SINK</text>
      </g>
    </svg>
  );
}

function CallGraph() {
  const nodes = [[80,110],[220,50],[220,170],[360,110],[500,60],[500,160]];
  const edges = [[0,1],[0,2],[1,3],[2,3],[3,4],[3,5],[1,2]];
  return (
    <svg viewBox="0 0 600 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <g stroke="currentColor" fill="none" strokeWidth="1">
        {edges.map(([a,b], i) => (
          <line key={i} x1={nodes[a][0]} y1={nodes[a][1]} x2={nodes[b][0]} y2={nodes[b][1]} />
        ))}
        {nodes.map(([x,y], i) => (
          <circle key={i} cx={x} cy={y} r={i === 3 ? 14 : 10} fill={i === 3 ? "currentColor" : "none"} />
        ))}
      </g>
      <g fill="currentColor" opacity="0.4" fontFamily="ui-monospace, monospace" fontSize="10" letterSpacing="0.08em">
        <text x="60" y="138">ENTRY</text>
        <text x="346" y="138" fill="currentColor" opacity="1">SOLVE</text>
      </g>
    </svg>
  );
}

function SpeedupChart() {
  // Throughput bars in GGas/s, placeholder values until the v1.0-beta head-to-head
  // bench publishes. Update the numbers in lockstep with PRODUCTS[parallel-evm].facts
  // when the real run lands.
  const bars = [["sequential reth", 1.1], ["naive parallel", 3.8], ["reth-turbo", 9.0]];
  const max = 10;
  return (
    <svg viewBox="0 0 600 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <g fontFamily="ui-monospace, monospace" fontSize="11" letterSpacing="0.06em">
        {bars.map(([label, val], i) => {
          const y = 40 + i * 60;
          const w = (val / max) * 420;
          return (
            <g key={i}>
              <text x="0" y={y - 8} fill="currentColor" opacity="0.6">{label.toUpperCase()}</text>
              <rect x="0" y={y} width="420" height="22" fill="none" stroke="currentColor" opacity="0.2" />
              <rect x="0" y={y} width={w} height="22" fill="currentColor" opacity={i === 2 ? 1 : 0.35} />
              <text x={w + 10} y={y + 16} fill="currentColor">{val} GGas/s</text>
            </g>
          );
        })}
      </g>
    </svg>
  );
}

function CommitArt() {
  const commits = [62, 38, 90, 24, 71, 48, 84, 33, 58];
  return (
    <svg viewBox="0 0 600 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <g stroke="currentColor" fill="none" strokeWidth="1">
        <line x1="40" y1="110" x2="560" y2="110" opacity="0.25" />
        {commits.map((v, i) => {
          const x = 40 + i * 65;
          return (
            <g key={i}>
              <line x1={x} y1={110 - v / 2} x2={x} y2={110 + v / 2} />
              <circle cx={x} cy={110 - v / 2} r="3" />
              <circle cx={x} cy={110 + v / 2} r="3" />
            </g>
          );
        })}
      </g>
      <g fill="currentColor" opacity="0.4" fontFamily="ui-monospace, monospace" fontSize="10" letterSpacing="0.08em">
        <text x="40" y="200">commit a1b2c3</text>
        <text x="490" y="200">commit f9e8d7</text>
      </g>
    </svg>
  );
}

function EmbargoArt() {
  return (
    <svg viewBox="0 0 600 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <g stroke="currentColor" fill="none" strokeWidth="1" opacity="0.5">
        <rect x="100" y="40" width="400" height="140" />
        <line x1="100" y1="60" x2="500" y2="60" />
      </g>
      <g fill="currentColor" fontFamily="ui-monospace, monospace" letterSpacing="0.1em">
        <text x="115" y="55" fontSize="10" opacity="0.6">VECTOR REGISTRY · INTERNAL</text>
        <text x="115" y="90" fontSize="14">██████████████</text>
        <text x="115" y="115" fontSize="14">██████████████████</text>
        <text x="115" y="140" fontSize="14">█████████████</text>
        <text x="115" y="165" fontSize="14">████████████████████</text>
      </g>
    </svg>
  );
}

// Bypass-class distribution visual for the WAF fuzzer card. Renders four
// horizontal bars representing different bypass-class buckets — value
// labels are deliberately omitted so the figure communicates "distribution
// across feature classes" rather than implying specific bypass counts.
function WafBarsArt() {
  const bars = [["script-injection", 0.78], ["css/svg-injection", 0.55], ["polyglot", 0.42], ["parser-confusion", 0.31]];
  return (
    <svg viewBox="0 0 600 220" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <g fontFamily="ui-monospace, monospace" fontSize="11" letterSpacing="0.06em">
        {bars.map(([label, val], i) => {
          const y = 30 + i * 45;
          const w = val * 420;
          return (
            <g key={i}>
              <text x="0" y={y - 6} fill="currentColor" opacity="0.6">{String(label).toUpperCase()}</text>
              <rect x="0" y={y} width="420" height="16" fill="none" stroke="currentColor" opacity="0.2" />
              <rect x="0" y={y} width={w} height="16" fill="currentColor" opacity={0.35 + i * 0.05} />
            </g>
          );
        })}
      </g>
    </svg>
  );
}

const FIGURES = {
  saas:           CommitArt,
  "parallel-evm": SpeedupChart,
  blockscanner:   FlowDiagram,
  research:       EmbargoArt,
  "web-infra":    WafBarsArt,
};

Object.assign(window, {
  SITE, NAV, FEATURES, SCHEDULE_URL, HERO, STATS, PRODUCTS, CONTACT, RESEARCH,
  useScopedTheme, ThemeToggle,
  FIGURES, FlowDiagram, CallGraph, SpeedupChart, CommitArt, EmbargoArt, WafBarsArt,
});
