(defcomp
  ()
  (~docs/page
    :title "SXTP Protocol"
    (~docs/section
      :title "Overview"
      :id "overview"
      (p
        "SXTP — SX Transfer Protocol — is HTTP reimagined where the wire format "
        (em "is")
        " the language. Requests, responses, headers, cookies, status conditions, and bodies are all s-expressions. There is no text framing, no content-type negotiation, no URL query-string encoding.")
      (p "Design principles:")
      (ul
        (~tw :tokens "list-disc list-inside space-y-2 mt-2")
        (li
          (strong "SX all the way")
          " — every datum on the wire is a valid SX value")
        (li
          (strong "Open verb set")
          " — any symbol is a legal verb, not just GET/POST/PUT/DELETE")
        (li
          (strong "Structured metadata")
          " — headers and cookies are dicts, not flat strings")
        (li
          (strong "Capability-scoped")
          " — requests declare required capabilities")
        (li
          (strong "Content-addressed")
          " — responses can be cached by hash")
        (li
          (strong "Streamable")
          " — chunked responses are sequences of expressions")))
    (~docs/section
      :title "Requests"
      :id "requests"
      (p
        "A request is a list beginning with the symbol "
        (code "request")
        ". All fields are keyword arguments.")
      (~docs/code
        :src (highlight "(request :verb navigate :path \"/\")" "lisp"))
      (p "Full request with all fields:")
      (~docs/code
        :src (highlight
          "(request\n  :verb       navigate\n  :path       \"/geography/capabilities\"\n  :headers    {:accept \"text/sx\" :language \"en\"}\n  :cookies    {:session \"tok_abc123\" :prefs {:theme \"dark\"}}\n  :params     {:page 1 :per-page 20}\n  :capabilities (fetch query)\n  :body       nil)"
          "lisp"))
      (div
        (~tw :tokens "overflow-x-auto rounded border border-stone-200 mt-4")
        (table
          (~tw :tokens "w-full text-left text-sm")
          (thead
            (tr
              (~tw :tokens "border-b border-stone-200 bg-stone-100")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Field")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Description")))
          (tbody
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":verb")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Symbol — the action to perform (required)"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":path")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "String — resource path (required)"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":headers")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Dict — structured request metadata"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":cookies")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Dict — client state, values can be any SX type"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":params")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Dict — query parameters as typed values"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":capabilities")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "List — capabilities this request requires"))
            (tr
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") ":body")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Any SX value — request payload"))))))
    (~docs/section
      :title "Responses"
      :id "responses"
      (p
        "A response is a list beginning with the symbol "
        (code "response")
        ".")
      (~docs/code
        :src (highlight
          "(response :status ok\n  :headers {:content-type \"text/sx\" :cache :immutable}\n  :set-cookie {:session {:value \"tok_xyz\" :max-age 3600 :path \"/\"}}\n  :body (page :title \"Home\" (h1 \"Welcome\")))"
          "lisp"))
      (p
        "The body isn't serialized HTML that needs parsing — it's a live component tree the browser evaluates directly."))
    (~docs/section
      :title "Verbs"
      :id "verbs"
      (p
        "Unlike HTTP's fixed set, any symbol is a valid verb. Convention defines common verbs; domains add their own.")
      (div
        (~tw :tokens "overflow-x-auto rounded border border-stone-200 mt-4")
        (table
          (~tw :tokens "w-full text-left text-sm")
          (thead
            (tr
              (~tw :tokens "border-b border-stone-200 bg-stone-100")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Verb")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Purpose")))
          (tbody
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "navigate")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Retrieve a page for display — analogous to GET for documents"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "fetch")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Retrieve data — analogous to GET for APIs"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "query")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Structured query — body contains a query expression"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "mutate")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Change state — analogous to POST/PUT/PATCH"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "create")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Create a new resource"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "delete")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Remove a resource"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "subscribe")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Open a streaming channel for real-time updates"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "inspect")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Retrieve metadata about a resource (capabilities, schema)"))
            (tr
              (td (~tw :tokens "px-3 py-2 text-stone-700 font-mono") "ping")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Liveness check")))))
      (p (~tw :tokens "mt-4") "Domains define their own verbs freely:")
      (~docs/code
        :src (highlight
          "(request :verb publish  :path \"/blog/draft-123\")\n(request :verb checkout :path \"/cart\")\n(request :verb render   :path \"/artdag/node/abc\" :params {:format \"png\"})\n(request :verb federate :path \"/outbox\" :body (activity ...))"
          "lisp")))
    (~docs/section
      :title "What HTTP got wrong"
      :id "http-comparison"
      (div
        (~tw :tokens "overflow-x-auto rounded border border-stone-200")
        (table
          (~tw :tokens "w-full text-left text-sm")
          (thead
            (tr
              (~tw :tokens "border-b border-stone-200 bg-stone-100")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "HTTP pain")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "SXTP answer")))
          (tbody
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Fixed verb set (GET/POST/PUT/DELETE)")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Any symbol is a verb"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Headers are flat string pairs")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Headers are dicts — nested, typed"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Cookies are encoded strings")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Cookies are SX values"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Body requires content-type negotiation")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Body is always SX — rendering is the client's job"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "URL query strings (?a=1&b=2)")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Params are part of the request expression"))
            (tr
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Separate mechanisms for streaming")
              (td
                (~tw :tokens "px-3 py-2 text-stone-600")
                "Streaming is just :stream true + chunk sequences"))))))
    (~docs/section
      :title "Status and conditions"
      :id "status"
      (p
        "Status is a symbol, not a number. Conditions replace error codes with structured, informative values.")
      (~docs/code
        :src (highlight
          "(response :status not-found\n  :body (condition :type resource-not-found\n                   :path \"/blog/nonexistent\"\n                   :message \"No such post\"\n                   :retry false))"
          "lisp"))
      (p "Conditions are extensible — domains define their own:")
      (~docs/code
        :src (highlight
          "(condition :type payment-declined\n           :reason :insufficient-funds\n           :provider \"sumup\")"
          "lisp")))
    (~docs/section
      :title "Streaming"
      :id "streaming"
      (p
        "A streaming response sets "
        (code ":stream true")
        ". The body becomes a sequence of chunk expressions.")
      (~docs/code
        :src (highlight
          ";; Ordered chunks\n(response :status ok :stream true)\n(chunk :seq 0 :body (tr (td \"Row 1\") (td \"data\")))\n(chunk :seq 1 :body (tr (td \"Row 2\") (td \"data\")))\n(chunk :done true)\n\n;; Server-sent events via subscribe\n(request :verb subscribe :path \"/events/live\")\n\n(event :type new-event :id \"evt-42\"\n  :body (div :class \"event-card\" (h3 \"Jazz Night\")))\n(event :type update :id \"evt-42\"\n  :body {:attendees 51})\n(event :type heartbeat :time 1711612800)"
          "lisp")))
    (~docs/section
      :title "Capabilities"
      :id "capabilities"
      (p
        "Requests declare the capabilities they need. The server checks these against the session's granted capabilities. Insufficient capabilities produce "
        (code "(response :status forbidden)")
        ".")
      (~docs/code
        :src (highlight
          ";; Client declares\n(request :verb query :path \"/events\"\n  :capabilities (fetch db:read))\n\n;; Server grants on auth\n(response :status ok\n  :set-cookie {:capabilities {:value (fetch query db:read mutate)\n                               :max-age 86400\n                               :secure true}})"
          "lisp"))
      (p "Inspect what a resource requires:")
      (~docs/code
        :src (highlight
          "(request :verb inspect :path \"/cart/checkout\")\n\n(response :status ok\n  :body {:required-capabilities (mutate cart:checkout)\n         :available-verbs (inspect mutate)\n         :params-schema {:shipping-address \"dict\"\n                         :payment-method \"symbol\"}})"
          "lisp")))
    (~docs/section
      :title "Caching"
      :id "caching"
      (p
        "Content-addressed caching. The response hash "
        (em "is")
        " the cache key. No ETags, no Last-Modified — just SX content hashes.")
      (~docs/code
        :src (highlight
          ";; Server provides hash\n(response :status ok\n  :headers {:content-hash \"sha3-abc123...\"\n            :cache :immutable}\n  :body ...)\n\n;; Client validates\n(request :verb fetch :path \"/geography/capabilities\"\n  :headers {:if-match \"sha3-abc123...\"})\n\n(response :status not-modified)"
          "lisp"))
      (p
        "Three cache policies: "
        (code ":immutable")
        " (content-addressed, never changes), "
        (code ":revalidate")
        " (check hash before using), "
        (code ":none")
        " (dynamic content)."))
    (~docs/section
      :title "Wire format"
      :id "wire-format"
      (p
        "On the wire, each message is a length-prefixed SX expression. Length is a decimal integer as ASCII, followed by newline. The SX expression is UTF-8 encoded.")
      (~docs/code
        :src (highlight "43\n(request :verb ping :path \"/\" :body nil)" "text"))
      (p
        "Connections are persistent — multiple request/response pairs on the same connection. Pipelining is allowed. TLS is the transport security layer: "
        (code "sxtp://")
        " is plaintext (port 5380), "
        (code "sxtps://")
        " is TLS (port 5381)."))
    (~docs/section
      :title "URI scheme"
      :id "uri"
      (p "The browser translates URIs into request expressions:")
      (~docs/code
        :src (highlight
          "sxtps://blog.rose-ash.com/geography/capabilities\n\n;; becomes\n\n(request :verb navigate\n  :path \"/geography/capabilities\"\n  :headers {:host \"blog.rose-ash.com\"})"
          "lisp")))
    (~docs/section
      :title "Examples"
      :id "examples"
      (p "Page navigation:")
      (~docs/code
        :src (highlight
          "(request :verb navigate :path \"/geography/capabilities\"\n  :headers {:host \"sx.rose-ash.com\" :accept \"text/sx\"})\n\n(response :status ok\n  :headers {:content-type \"text/sx\"\n            :content-hash \"sha3-9f2a...\"}\n  :body (page :title \"Capabilities\"\n          (h1 \"Geography Capabilities\")\n          (~capability-list :domain \"geography\")))"
          "lisp"))
      (p "Structured query:")
      (~docs/code
        :src (highlight
          "(request :verb query :path \"/events\"\n  :capabilities (fetch db:read)\n  :params {:after \"2026-03-01\" :limit 10}\n  :body (filter (events) (fn (e) (> (:attendees e) 50))))\n\n(response :status ok\n  :headers {:cache :revalidate}\n  :body ((event :id \"evt-42\" :title \"Jazz Night\" :attendees 87)\n         (event :id \"evt-55\" :title \"Art Walk\" :attendees 120)))"
          "lisp"))
      (p "Creating a resource:")
      (~docs/code
        :src (highlight
          "(request :verb create :path \"/blog/posts\"\n  :capabilities (mutate blog:publish)\n  :cookies {:session \"tok_abc123\"}\n  :body {:title \"SXTP Protocol\"\n         :body (article (h1 \"SXTP\") (p \"Everything is SX.\"))\n         :tags (\"protocol\" \"sx\" \"web\")})\n\n(response :status created\n  :headers {:location \"/blog/posts/sxtp-protocol\"\n            :content-hash \"sha3-ff01...\"}\n  :body {:id \"post-789\"\n         :path \"/blog/posts/sxtp-protocol\"\n         :created-at 1711612800})"
          "lisp")))
    (~docs/section
      :title "Specification"
      :id "spec"
      (p
        "The formal specification lives in "
        (code "applications/sxtp/spec.sx")
        " — a self-describing SX file where the field definitions are themselves SX data structures that the protocol can introspect."))))
