;; Static layout — takes &rest children where the three suspense slots go.
(defcomp (&rest children)
  (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") "Streaming & Suspense Demo")
      (p (~tw :tokens "mt-2 text-stone-600")
        "This page uses " (code (~tw :tokens "bg-stone-100 px-1 rounded text-violet-700") ":stream true")
        " in its defpage declaration. The browser receives the page skeleton instantly, "
        "then three IO sources resolve at staggered intervals (1s, 3s, 5s)."))

    ;; Slot: suspense placeholders (or resolved content)
    (div (~tw :tokens "grid gap-4") children)

    ;; Flow diagram
    (div (~tw :tokens "space-y-4")
      (h2 (~tw :tokens "text-lg font-semibold text-stone-800") "Streaming Flow")
      (div (~tw :tokens "grid gap-3")
        (map (fn (item)
          (div (~tw :tokens "flex items-start gap-3 rounded-lg border border-stone-200 bg-white p-4")
            (div (~tw :tokens "flex-shrink-0 w-8 h-8 rounded-full bg-violet-100 flex items-center justify-center text-violet-700 font-bold text-sm")
              (get item "label"))
            (p (~tw :tokens "text-stone-700 text-sm pt-1") (get item "detail"))))
          (list
            {:label "Shell" :detail "HTML shell with three suspense placeholders sent immediately"}
            {:label "Boot" :detail "sx-browser.js loads, renders fallback skeletons"}
            {:label "1s" :detail "Fast API responds — first skeleton replaced with green box"}
            {:label "3s" :detail "Database query completes — second skeleton replaced with blue box"}
            {:label "5s" :detail "ML inference finishes — third skeleton replaced with amber box"}))))

    ;; How it works
    (div (~tw :tokens "rounded-lg border border-violet-200 bg-violet-50 p-5 space-y-3")
      (h2 (~tw :tokens "text-lg font-semibold text-violet-900") "How Multi-Stream Works")
      (ol (~tw :tokens "list-decimal list-inside text-violet-800 space-y-2 text-sm")
        (li "Server evaluates " (code ":data") " — gets an " (em "async generator"))
        (li "HTML shell with three " (code "~shared:pages/suspense") " placeholders sent immediately")
        (li "Generator yields first chunk after 1s — server sends " (code "__sxResolve(\"stream-fast\", ...)"))
        (li "Generator yields second chunk after 3s — " (code "__sxResolve(\"stream-medium\", ...)"))
        (li "Generator yields third chunk after 5s — " (code "__sxResolve(\"stream-slow\", ...)"))
        (li "Each resolve replaces its skeleton independently")))

    ;; Technical details
    (div (~tw :tokens "rounded-lg border border-stone-200 bg-stone-50 p-4 text-sm space-y-2")
      (p (~tw :tokens "font-semibold text-stone-800") "Implementation details")
      (ul (~tw :tokens "list-disc list-inside text-stone-600 space-y-1")
        (li (code "defpage :stream true") " — opts the page into chunked transfer encoding")
        (li (code ":data") " helper is an async generator — each " (code "yield") " resolves a different suspense slot")
        (li "Each yield includes " (code "_stream_id") " matching a " (code "~shared:pages/suspense :id") " in the shell")
        (li (code ":content") " expression is re-evaluated with each yield's bindings")
        (li "Headers stream concurrently — independent of the data generator")
        (li "Future: SSE/WebSocket for re-resolving slots after initial page load")))))
