(defcomp
  ()
  (~docs/page
    :title "Capabilities"
    (p
      (~tw :tokens "text-stone-500 text-sm italic mb-8")
      "Abstract evaluation contexts — what an expression is allowed to do, without prescribing where it runs.")
    (~docs/section
      :title "The model"
      :id "model"
      (p
        "SX expressions evaluate in contexts that provide named capabilities. A capability is a permission to perform a class of side effect: "
        (code "io")
        " for network/filesystem, "
        (code "mutation")
        " for mutable state, "
        (code "dom")
        " for browser DOM, "
        (code "render")
        " for rendering operations.")
      (p
        "The key insight: capabilities are abstract. "
        (code "io")
        " doesn't mean 'server' — it means 'this context can perform input/output.' A server, an edge worker, and a browser tab can all provide "
        (code "io")
        ". The language doesn't care where the code runs, only what it's allowed to do.")
      (~docs/code
        :src (str
          "(with-capabilities (list \"pure\" \"mutation\")\n  (fn ()\n    (has-capability? \"io\")      ;; false\n    (has-capability? \"pure\")    ;; true\n    (require-capability! \"io\") ;; ERROR: Capability 'io' not available\n    ))")))
    (~docs/section
      :title "Capability primitives"
      :id "primitives"
      (table
        (~tw :tokens "min-w-full text-sm mb-6")
        (thead
          (tr
            (th
              (~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
              "Primitive")
            (th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
        (tbody
          (~tw :tokens "text-stone-600")
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "with-capabilities")
            (td
              (~tw :tokens "py-1")
              "Restrict capabilities for a body. Takes a list of capability names and a thunk."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "has-capability?")
            (td
              (~tw :tokens "py-1")
              "Check if a capability is available. Returns true in unrestricted contexts."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "require-capability!")
            (td
              (~tw :tokens "py-1")
              "Assert a capability. Error with a clear message if not available."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "current-capabilities")
            (td
              (~tw :tokens "py-1")
              "Return the current capability set, or nil if unrestricted.")))))
    (~docs/section
      :title "Effect annotations"
      :id "effects"
      (p "Every function can declare its effect requirements:")
      (~docs/code
        :src (str
          "(define fetch-user :effects (io)\n  (fn (id) (http-get (str \"/users/\" id))))\n\n(define format-name :effects ()\n  (fn (user) (str (get user \"first\") \" \" (get user \"last\"))))"))
      (p
        (code "format-name")
        " is pure — it can run anywhere. "
        (code "fetch-user")
        " requires "
        (code "io")
        " — it can only run in a context that provides that capability. The "
        (em "where")
        " is decided by the host, not the language."))
    (~docs/section
      :title "Standard capabilities"
      :id "standard"
      (table
        (~tw :tokens "min-w-full text-sm mb-6")
        (thead
          (tr
            (th
              (~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
              "Capability")
            (th
              (~tw :tokens "text-left pb-2 font-semibold text-stone-700")
              "What it permits")))
        (tbody
          (~tw :tokens "text-stone-600")
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "pure")
            (td
              (~tw :tokens "py-1")
              "No side effects. Deterministic. Cacheable. Runnable anywhere."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "mutation")
            (td
              (~tw :tokens "py-1")
              "Mutable state: set!, append!, dict-set!, signal mutation."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "io")
            (td
              (~tw :tokens "py-1")
              "External I/O: network, filesystem, timers, promises."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "dom")
            (td
              (~tw :tokens "py-1")
              "Browser DOM operations: create elements, set attributes, event listeners."))
          (tr
            (td (~tw :tokens "pr-4 py-1 font-mono text-xs") "render")
            (td
              (~tw :tokens "py-1")
              "Rendering operations: component expansion, HTML generation, DOM patching.")))))
    (~docs/section
      :title "Why not phases?"
      :id "why-not-phases"
      (p
        "Many frameworks hard-code evaluation phases: 'server' vs 'client', 'build time' vs 'runtime'. This bakes in assumptions about deployment topology.")
      (p
        "SX might run on a server, in a browser, at an edge node, in a WebAssembly sandbox, on another peer's machine, or in a context that doesn't exist yet. Capabilities are the right abstraction because they describe "
        (em "what")
        " an expression needs, not "
        (em "where")
        " it runs.")
      (p
        "A 'server' is just a context that provides "
        (code "(pure mutation io)")
        ". A 'browser' provides "
        (code "(pure mutation io dom render)")
        ". An edge worker might provide "
        (code "(pure io)")
        " but not "
        (code "mutation")
        ". The evaluator doesn't need to know — it just checks capabilities."))
    (~docs/section
      :title "Where code evaluates"
      :id "where"
      (p
        "The same expression can evaluate on the server or in the browser. The capability context determines which is possible, but the "
        (em "code")
        " is identical.")
      (h4 (~tw :tokens "font-semibold mt-6 mb-2") "Server-side fetch")
      (p
        "A component that fetches data during SSR. The server has "
        (code "io")
        " capability, so the fetch runs at render time. The browser receives pre-rendered HTML — no fetch, no loading state, no flash.")
      (~docs/code
        :src (highlight
          "(defcomp ~user-card (&key user-id)\n  :effects (io)\n  (let ((data (fetch-json (str \"/api/users/\" user-id))))\n    (div :class \"rounded border p-4\"\n      (h3 (get data \"name\"))\n      (p :class \"text-sm text-stone-500\" (get data \"email\")))))"
          "lisp"))
      (p
        "This renders to static HTML on the server. The client never sees the fetch. The "
        (code ":effects (io)")
        " annotation documents the requirement — if you try to call this in a "
        (code "pure")
        " context, it fails with a clear error.")
      (h4 (~tw :tokens "font-semibold mt-6 mb-2") "Client-side fetch")
      (p
        "An island that fetches after hydration. The browser has "
        (code "io")
        " too, but the fetch happens reactively — the signal drives the UI update.")
      (~docs/code
        :src (highlight
          "(defisland ~user-profile (&key user-id)\n  (let ((data (signal nil))\n        (loading (signal true)))\n    ;; Fetch on mount — runs in browser\n    (effect (fn ()\n      (fetch-json (str \"/api/users/\" user-id)\n        (fn (result)\n          (reset! data result)\n          (reset! loading false)))))\n    ;; Reactive UI\n    (div :class \"rounded border p-4\"\n      (if (deref loading)\n        (p \"Loading...\")\n        (do\n          (h3 (get (deref data) \"name\"))\n          (p :class \"text-sm text-stone-500\"\n            (get (deref data) \"email\")))))))"
          "lisp"))
      (p
        "Same data, same UI, different evaluation context. The server version is faster (no roundtrip from browser). The client version is interactive (can refetch, react to user actions). The capability model lets the same language express both without separate 'server code' and 'client code' concepts.")
      (h4 (~tw :tokens "font-semibold mt-6 mb-2") "The same component, both ways")
      (p
        "With isomorphic evaluation, a single component can be SSR'd on first load (server provides "
        (code "io")
        "), then hydrated in the browser where the island takes over with reactive signals. The capability set changes but the component definition doesn't.")
      (~docs/code
        :src (highlight
          ";; Server: full capabilities, renders HTML\n;; Browser: same component, hydrates as island\n;; The evaluator checks capabilities, not location\n\n(defcomp ~data-card (&key url)\n  :effects (io)\n  (let ((result (fetch-json url)))\n    (div :class \"p-4 rounded bg-white shadow\"\n      (h3 (get result \"title\"))\n      (p (get result \"body\")))))"
          "lisp"))
      (p
        "On the server, "
        (code "fetch-json")
        " runs synchronously. In the browser, the same call goes through the async adapter. The component doesn't know or care. Capabilities are the abstraction boundary — not 'server' and 'client'."))))
