(defcomp
  ()
  (~docs/page
    :title "Reactive Islands Plan"
    (~docs/section
      :title "Context"
      :id "context"
      (p
        "SX already has a sliding bar for "
        (em "where")
        " rendering happens — server-side HTML, SX wire format for client rendering, or any point between. This is the isomorphism bar. It controls the render boundary.")
      (p
        "There is a second bar, orthogonal to the first: "
        (em "how state flows.")
        " On one end, all state lives on the server — every user action is a round-trip, every UI update is a fresh render. This is the htmx model. On the other end, state lives on the client — signals, subscriptions, fine-grained DOM updates without server involvement. This is the React model.")
      (p
        "These two bars are independent. You can have server-rendered HTML with client state (SSR + hydrated React). You can have client-rendered components with server state (current SX). The combination creates four quadrants:")
      (div
        (~tw :tokens "overflow-x-auto mt-4 mb-4")
        (table
          (~tw :tokens "w-full text-sm text-left")
          (thead
            (tr
              (~tw :tokens "border-b border-stone-200")
              (th (~tw :tokens "py-2 px-3 font-semibold text-stone-700") "")
              (th
                (~tw :tokens "py-2 px-3 font-semibold text-stone-700")
                "Server State")
              (th
                (~tw :tokens "py-2 px-3 font-semibold text-stone-700")
                "Client State")))
          (tbody
            (~tw :tokens "text-stone-600")
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "py-2 px-3 font-semibold text-stone-700")
                "Server Rendering")
              (td (~tw :tokens "py-2 px-3") "Pure hypermedia (htmx)")
              (td (~tw :tokens "py-2 px-3") "SSR + hydrated islands (Next.js)"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "py-2 px-3 font-semibold text-stone-700")
                "Client Rendering")
              (td (~tw :tokens "py-2 px-3") "SX wire format (current)")
              (td
                (~tw :tokens "py-2 px-3 font-semibold text-violet-700")
                "Reactive islands (this plan)")))))
      (p
        "Today SX occupies the bottom-left quadrant — client-rendered components with server state. This plan adds the bottom-right: "
        (strong "reactive islands")
        " with client-local signals. A page can mix all four quadrants. Most content stays hypermedia. Interactive regions opt into reactivity. The author controls where each component sits on both bars."))
    (~docs/section
      :title "The Spectrum"
      :id "spectrum"
      (p
        "Four levels of client interactivity. Each is independently valuable. Each is opt-in per component.")
      (~docs/subsection
        :title "Level 0: Pure Hypermedia"
        (p
          "The default. "
          (code "sx-get")
          ", "
          (code "sx-post")
          ", "
          (code "sx-swap")
          ". Server renders everything. Client swaps fragments. No client state. No JavaScript state management. This is where 90% of a typical application should live."))
      (~docs/subsection
        :title "Level 1: Local DOM Operations"
        (p
          "Imperative escape hatches for micro-interactions too small for a server round-trip: toggling a menu, switching a tab, showing a tooltip. "
          (code "toggle!")
          ", "
          (code "set-attr!")
          ", "
          (code "on-event")
          ". No reactive graph. Just do the thing directly."))
      (~docs/subsection
        :title "Level 2: Reactive Islands"
        (p
          (code "defisland")
          " components with local signals. Fine-grained DOM updates — no virtual DOM, no diffing, no component re-renders. A signal change updates only the DOM nodes that read it. Islands are isolated by default. The server can render their initial state."))
      (~docs/subsection
        :title "Level 3: Connected Islands"
        (p
          "Islands that share state via signal props or named stores ("
          (code "def-store")
          " / "
          (code "use-store")
          "). Plus event bridges for htmx lake-to-island communication. This is where SX starts to feel like React — but only in the regions that need it. The surrounding page remains hypermedia.")))
    (~docs/section
      :title "htmx Lakes"
      :id "lakes"
      (p
        "An htmx lake is server-driven content "
        (em "inside")
        " a reactive island. The island provides the reactive boundary; the lake content is swapped via "
        (code "sx-get")
        "/"
        (code "sx-post")
        " like normal hypermedia.")
      (p
        "This works because signals live in JavaScript closures, not in the DOM. When a swap replaces lake content, the island's signals are unaffected. The lake can communicate back to the island via the "
        (a
          :href "/sx/(geography.(reactive.(event-bridge)))"
          :sx-get "/sx/(geography.(reactive.(event-bridge)))"
          :sx-target "#sx-content"
          :sx-select "#sx-content"
          :sx-swap "outerHTML"
          :sx-push-url "true"
          (~tw :tokens "text-violet-700 underline")
          "event bridge")
        ".")
      (~docs/subsection
        :title "Navigation scenarios"
        (div
          (~tw :tokens "space-y-3")
          (div
            (~tw :tokens "rounded border border-green-200 bg-green-50 p-3")
            (div (~tw :tokens "font-semibold text-green-800") "Swap inside island")
            (p
              (~tw :tokens "text-sm text-stone-600 mt-1")
              "Lake content replaced. Signals survive. Effects can rebind to new DOM. User state intact."))
          (div
            (~tw :tokens "rounded border border-green-200 bg-green-50 p-3")
            (div (~tw :tokens "font-semibold text-green-800") "Swap outside island")
            (p
              (~tw :tokens "text-sm text-stone-600 mt-1")
              "Different part of page updated. Island completely unaffected. User state intact."))
          (div
            (~tw :tokens "rounded border border-amber-200 bg-amber-50 p-3")
            (div (~tw :tokens "font-semibold text-amber-800") "Swap replaces island")
            (p
              (~tw :tokens "text-sm text-stone-600 mt-1")
              "Island disposed. Local signals lost. Named stores persist — new island reconnects via use-store."))
          (div
            (~tw :tokens "rounded border border-stone-200 p-3")
            (div (~tw :tokens "font-semibold text-stone-800") "Full page navigation")
            (p
              (~tw :tokens "text-sm text-stone-600 mt-1")
              "Everything cleared. clean slate. clear-stores wipes the registry.")))))
    (~docs/section
      :title "Reactive DOM Rendering"
      :id "reactive-rendering"
      (p
        "The existing "
        (code "renderDOM")
        " function walks the AST and creates DOM nodes. Inside an island, it becomes signal-aware:")
      (~docs/subsection
        :title "Text bindings"
        (~docs/code
          :src (highlight
            ";; (span (deref count)) creates:\n;; const text = document.createTextNode(sig.value)\n;; effect(() => text.nodeValue = sig.value)"
            "lisp"))
        (p "Only the text node updates. The span is untouched."))
      (~docs/subsection
        :title "Attribute bindings"
        (~docs/code
          :src (highlight
            ";; (div :class (str \"panel \" (if (deref open?) \"visible\" \"hidden\")))\n;; effect(() => div.className = ...)"
            "lisp")))
      (~docs/subsection
        :title "Conditional fragments"
        (~docs/code
          :src (highlight
            ";; (when (deref show?) (~details)) creates:\n;; A marker comment node, then:\n;; effect(() => show ? insert-after(marker, render(~details)) : remove)"
            "lisp"))
        (p
          "Equivalent to SolidJS's "
          (code "Show")
          " — but falls out naturally from the evaluator."))
      (~docs/subsection
        :title "List rendering"
        (~docs/code
          :src (highlight
            "(map (fn (item) (li :key (get item \"id\") (get item \"name\")))\n  (deref items))"
            "lisp"))
        (p
          "Keyed elements are reused and reordered. Unkeyed elements are morphed.")))
    (~docs/section
      :title "Status"
      :id "status"
      (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") "Task")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Status")
              (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") "Signal runtime")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "signals.sx: signal, deref, reset!, swap!, computed, effect, batch"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Named stores (L3)")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "signals.sx: def-store, use-store, clear-stores"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Event bridge")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "signals.sx: emit-event, on-event, bridge-event"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Event bindings")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "adapter-dom.sx: :on-click (fn ...) → domListen"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "data-sx-emit")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "orchestration.sx: auto-dispatch custom events from server content"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Client hydration")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "boot.sx: hydrate-island, dispose-island, post-swap wiring"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Bootstrapping")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "All functions transpiled to JS and Python, platform primitives implemented"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Island disposal")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "boot.sx, orchestration.sx: effects/computeds auto-register disposers, pre-swap cleanup"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Reactive list")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "adapter-dom.sx: map + deref auto-upgrades to reactive-list"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "Input binding + keyed lists")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "adapter-dom.sx: :bind signal, :key attr"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Portals")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "adapter-dom.sx: portal render-dom form"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Error boundaries")
              (td (~tw :tokens "px-3 py-2 text-green-700 font-medium") "Done")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "adapter-dom.sx: error-boundary render-dom form"))
            (tr
              (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Suspense")
              (td (~tw :tokens "px-3 py-2 text-stone-500 font-medium") "N/A")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "covered by existing primitives"))
            (tr
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Transitions")
              (td (~tw :tokens "px-3 py-2 text-stone-500 font-medium") "N/A")
              (td
                (~tw :tokens "px-3 py-2 text-stone-700")
                "covered by existing primitives"))))))
    (~docs/section
      :title "Design Principles"
      :id "principles"
      (ol
        (~tw :tokens "space-y-3 text-stone-600 list-decimal list-inside")
        (li
          (strong "Islands are opt-in.")
          " "
          (code "defcomp")
          " remains the default. Components are inert unless you choose "
          (code "defisland")
          ". No reactive overhead for static content.")
        (li
          (strong "Signals are values, not hooks.")
          " Create them anywhere. Pass them as arguments. Store them in dicts. No rules about calling order or conditional creation.")
        (li
          (strong "Fine-grained, not component-grained.")
          " A signal change updates the specific DOM node that reads it. The island does not re-render. There is no virtual DOM and no diffing beyond the morph algorithm already in SxEngine.")
        (li
          (strong "The server is still the authority.")
          " Islands handle client interactions. The server handles auth, data, routing. The server can push state into islands via OOB swaps. Islands can submit data to the server via "
          (code "sx-post")
          ".")
        (li
          (strong "Spec-first.")
          " Signal semantics live in "
          (code "signals.sx")
          ". Bootstrapped to JS and Python. The same primitives will work in future hosts — Go, Rust, native.")
        (li
          (strong "No build step.")
          " Reactive bindings are created at runtime during DOM rendering. No JSX compilation, no Babel transforms, no Vite plugins."))
      (p
        (~tw :tokens "mt-4")
        "The recommendation from the "
        (a
          :href "/sx/(etc.(essay.client-reactivity))"
          (~tw :tokens "text-violet-700 underline")
          "Client Reactivity")
        " essay was: \"Tier 4 probably never.\" This plan is what happens when the answer changes. The design avoids every footgun that essay warns about — no useState cascading to useEffect cascading to Context cascading to a state management library. Signals are one primitive. Islands are one boundary. The rest is composition."))))
