;; --- Main page component ---
(defcomp (&key components page-plans)
  (div (~tw :tokens "space-y-8")
    (div (~tw :tokens "border-b border-stone-200 pb-6")
      (h1 (~tw :tokens "text-2xl font-bold text-stone-900") "Affinity Annotations")
      (p (~tw :tokens "mt-2 text-stone-600")
        "Phase 7a: components declare where they prefer to render. The "
        (code (~tw :tokens "bg-stone-100 px-1 rounded text-violet-700") "render-target")
        " function in deps.sx combines the annotation with IO analysis to produce a per-component boundary decision."))

    ;; Syntax
    (~docs/section :title "Syntax" :id "syntax"
      (p "Add " (code ":affinity") " between the params list and the body:")
      (~docs/code :src (highlight "(defcomp ~affinity-demo/my-component (&key title)\n  :affinity :client   ;; or :server, or omit for :auto\n  (div title))" "lisp"))
      (p "Three values:")
      (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
        (li (code ":auto") " (default) — runtime decides from IO dependency analysis")
        (li (code ":client") " — always render client-side; IO calls proxied to server")
        (li (code ":server") " — always render server-side; never sent to client as SX")))

    ;; Live components
    (~docs/section :title "Live Components" :id "live"
      (p "These components are defined with different affinities. The server analyzed them at registration time:")

      (div (~tw :tokens "space-y-4 mt-4")
        (~affinity-demo/aff-demo-auto)
        (~affinity-demo/aff-demo-client)
        (~affinity-demo/aff-demo-server)
        (~affinity-demo/aff-demo-io-auto)
        (~affinity-demo/aff-demo-io-client)))

    ;; Analysis table from server
    (~docs/section :title "Server Analysis" :id "analysis"
      (p "The server computed these render targets at component registration time:")
      (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") "Component")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Affinity")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "IO Deps")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Render Target")))
          (tbody
            (map (fn (c)
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") (get c "name"))
                (td (~tw :tokens "px-3 py-2")
                  (span :class (str "inline-block px-2 py-0.5 rounded text-xs font-bold uppercase "
                    (case (get c "affinity")
                      "client" "bg-blue-100 text-blue-700"
                      "server" "bg-amber-100 text-amber-700"
                      "bg-stone-100 text-stone-600"))
                    (get c "affinity")))
                (td (~tw :tokens "px-3 py-2 text-stone-600")
                  (if (> (len (get c "io-refs")) 0)
                    (span (~tw :tokens "text-red-600 font-medium")
                      (join ", " (get c "io-refs")))
                    (span (~tw :tokens "text-green-600") "none")))
                (td (~tw :tokens "px-3 py-2")
                  (span :class (str "inline-block px-2 py-0.5 rounded text-xs font-bold uppercase "
                    (if (= (get c "render-target") "client")
                      "bg-green-100 text-green-700"
                      "bg-orange-100 text-orange-700"))
                    (get c "render-target")))))
              components)))))

    ;; Decision matrix
    (~docs/section :title "Decision Matrix" :id "matrix"
      (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") "Affinity")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Has IO?")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Render Target")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Why")))
          (tbody
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono") ":auto")
              (td (~tw :tokens "px-3 py-2") "No")
              (td (~tw :tokens "px-3 py-2 font-bold text-green-700") "client")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Pure — can render anywhere"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono") ":auto")
              (td (~tw :tokens "px-3 py-2") "Yes")
              (td (~tw :tokens "px-3 py-2 font-bold text-orange-700") "server")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "IO must resolve server-side"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono") ":client")
              (td (~tw :tokens "px-3 py-2") "No")
              (td (~tw :tokens "px-3 py-2 font-bold text-green-700") "client")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Explicit + pure"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono") ":client")
              (td (~tw :tokens "px-3 py-2") "Yes")
              (td (~tw :tokens "px-3 py-2 font-bold text-green-700") "client")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Override — IO proxied via /sx/io/"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono") ":server")
              (td (~tw :tokens "px-3 py-2") "No")
              (td (~tw :tokens "px-3 py-2 font-bold text-orange-700") "server")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Override — auth-sensitive"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono") ":server")
              (td (~tw :tokens "px-3 py-2") "Yes")
              (td (~tw :tokens "px-3 py-2 font-bold text-orange-700") "server")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Both affinity and IO say server"))))))

    ;; Per-page render plans
    (~docs/section :title "Page Render Plans" :id "plans"
      (p "Phase 7b: render plans are pre-computed at registration time for each page. The plan maps every component needed by the page to its render target.")

      (when (> (len page-plans) 0)
        (div (~tw :tokens "space-y-4 mt-4")
          (map (fn (plan)
            (div (~tw :tokens "rounded border border-stone-200 p-4")
              (div (~tw :tokens "flex items-center justify-between mb-3")
                (div
                  (span (~tw :tokens "font-mono font-medium text-stone-800") (get plan "name"))
                  (span (~tw :tokens "text-stone-400 ml-2 text-sm") (get plan "path")))
                (div (~tw :tokens "flex gap-2")
                  (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-orange-100 text-orange-700")
                    (str (get plan "server-count") " server"))
                  (span (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-100 text-green-700")
                    (str (get plan "client-count") " client"))))
              (when (> (get plan "server-count") 0)
                (div (~tw :tokens "mb-2")
                  (span (~tw :tokens "text-xs font-medium text-stone-500 uppercase") "Server-expanded: ")
                  (span (~tw :tokens "text-sm font-mono text-orange-700")
                    (join " " (get plan "server")))))
              (when (> (get plan "client-count") 0)
                (div
                  (span (~tw :tokens "text-xs font-medium text-stone-500 uppercase") "Client-rendered: ")
                  (span (~tw :tokens "text-sm font-mono text-green-700")
                    (join " " (get plan "client")))))))
            page-plans))))

    ;; How it integrates
    (~docs/section :title "How It Works" :id "how"
      (ol (~tw :tokens "list-decimal list-inside text-stone-700 space-y-2")
        (li (code "defcomp") " parses " (code ":affinity") " annotation between params and body")
        (li "Component object stores " (code "affinity") " field (\"auto\", \"client\", or \"server\")")
        (li (code "compute-all-io-refs") " scans transitive IO deps at registration time")
        (li (code "render-target") " in deps.sx combines affinity + IO analysis → \"server\" or \"client\"")
        (li "Server partial evaluator (" (code "_aser") ") checks " (code "render_target") ":")
        (ul (~tw :tokens "list-disc pl-8 text-stone-600")
          (li "\"server\" → expand component, embed rendered HTML")
          (li "\"client\" → serialize as SX, let client render"))
        (li "Client routing uses same info: IO deps in page registry → proxy registration")))

    ;; Verification
    (div (~tw :tokens "rounded-lg border border-amber-200 bg-amber-50 p-4 text-sm space-y-2")
      (p (~tw :tokens "font-semibold text-amber-800") "How to verify")
      (ol (~tw :tokens "list-decimal list-inside text-amber-700 space-y-1")
        (li "View page source — components with render-target \"server\" are expanded to HTML")
        (li "Components with render-target \"client\" appear as " (code "(~plans/content-addressed-components/name ...)") " in the SX wire format")
        (li "Navigate away and back — client-routable pure components render instantly")
        (li "Check the analysis table above — it shows live data from the server's component registry")))))
