;; ---------------------------------------------------------------------------
;; Predictive Component Prefetching
;; ---------------------------------------------------------------------------

(defcomp ()
  (~docs/page :title "Predictive Component Prefetching"

    (~docs/section :title "Context" :id "context"
      (p "Phase 3 of the isomorphic roadmap added client-side routing with component dependency checking. When a user clicks a link, " (code "try-client-route") " checks " (code "has-all-deps?") " — if the target page needs components not yet loaded, the client falls back to a server fetch. This works correctly but misses an opportunity: " (strong "we can prefetch those missing components before the click happens."))
      (p "The page registry already carries " (code ":deps") " metadata for every page. The client already knows which components are loaded via " (code "loaded-component-names") ". The gap is a mechanism to " (em "proactively") " resolve the difference — fetching missing component definitions so that by the time the user clicks, client-side routing succeeds.")
      (p "But this goes beyond just hover-to-prefetch. The full spectrum includes: bundling linked routes' components with the initial page load, batch-prefetching after idle, predicting mouse trajectory toward links, and even splitting the component/data fetch so that " (code ":data") " pages can prefetch their components and only fetch data on click. Each strategy trades bandwidth for latency, and pages should be able to declare which tradeoff they want."))

    (~docs/section :title "Current State" :id "current-state"
      (div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-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") "Layer")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "What exists")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Where")))
          (tbody
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Page registry")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Each page carries " (code ":deps (\"~card\" \"~essay-foo\" ...)"))
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "helpers.py → <script type=\"text/sx-pages\">"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Dep check")
              (td (~tw :tokens "px-3 py-2 text-stone-700") (code "has-all-deps?") " gates client routing")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "orchestration.sx:546-559"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Component bundle")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Per-page inline " (code "<script type=\"text/sx\" data-components>"))
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "helpers.py:715, jinja_bridge.py"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Incremental defs")
              (td (~tw :tokens "px-3 py-2 text-stone-700") (code "components_for_request()") " sends only missing defs in SX responses")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "helpers.py:459-509"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Preload cache")
              (td (~tw :tokens "px-3 py-2 text-stone-700") (code "sx-preload") " prefetches full responses on hover/mousedown")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "orchestration.sx:686-708"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Route matching")
              (td (~tw :tokens "px-3 py-2 text-stone-700") (code "find-matching-route") " matches pathname to page entry")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "router.sx"))))))

    ;; -----------------------------------------------------------------------
    ;; Prefetch strategies
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Prefetch Strategies" :id "strategies"
      (p "Prefetching is a spectrum from conservative to aggressive. The system should support all of these, configured declaratively per link or per page via " (code "defpage") " metadata and " (code "sx-prefetch") " attributes.")

      (div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-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") "Strategy")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Trigger")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "What prefetches")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Latency on click")))
          (tbody
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Eager bundle")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Initial page load")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components for linked routes included in " (code "<script data-components>"))
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Zero — already in memory"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Idle timer")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "After page settles (requestIdleCallback or setTimeout)")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components for visible nav links, batched in one request")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Zero if idle fetch completed"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Viewport")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Link scrolls into view (IntersectionObserver)")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components for that link's route")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Zero if user scrolled before clicking"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Mouse approach")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Cursor moving toward link (trajectory prediction)")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components for predicted target")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Near-zero — fetch starts ~200ms before hover"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Hover")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "mouseover (150ms debounce)")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components for hovered link's route")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Low — typical hover-to-click is 300-500ms"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Mousedown")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "mousedown (0ms debounce)")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components for clicked link's route")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "~80ms — mousedown-to-click gap"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "Components + data")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Any of the above")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Components " (em "and") " page data for " (code ":data") " pages")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "Zero for components; data fetch may still be in flight")))))

      (~docs/subsection :title "Eager Bundle"
        (p "The server already computes per-page component bundles. For key navigation paths — the main nav bar, section nav — the server can include " (em "linked routes' components") " in the initial bundle, not just the current page's.")
        (~docs/code :src (highlight ";; defpage metadata declares eager prefetch targets\n(defpage docs-page\n  :path \"/language/docs/<slug>\"\n  :auth :public\n  :prefetch :eager  ;; bundle deps for all linked pure routes\n  :content (case slug ...))" "lisp"))
        (p "Implementation: " (code "components_for_page()") " already scans the page SX for component refs. Extend it to also scan for " (code "href") " attributes, match them against the page registry, and include those pages' deps in the bundle. The cost is a larger initial payload; the benefit is zero-latency navigation within a section."))

      (~docs/subsection :title "Idle Timer"
        (p "After page load and initial render, use " (code "requestIdleCallback") " (or a fallback " (code "setTimeout") ") to scan visible nav links and batch-prefetch their missing components in a single request.")
        (~docs/code :src (highlight "(define prefetch-visible-links-on-idle\n  (fn ()\n    (request-idle-callback\n      (fn ()\n        (let ((links (dom-query-all \"a[href][sx-get]\"))\n              (all-missing (list)))\n          (for-each\n            (fn (link)\n              (let ((missing (compute-missing-deps\n                (url-pathname (dom-get-attr link \"href\")))))\n                (when missing\n                  (for-each (fn (d) (append! all-missing d))\n                    missing))))\n            links)\n          (when (not (empty? all-missing))\n            (prefetch-components (dedupe all-missing))))))))" "lisp"))
        (p "Called once from " (code "boot-init") " after initial processing. Batches all missing deps into one network request. Low priority — browser handles it when idle."))

      (~docs/subsection :title "Mouse Approach (Trajectory Prediction)"
        (p "Don't wait for the cursor to reach the link — predict where it's heading. Track the last few " (code "mousemove") " events, extrapolate the trajectory, and if it points toward a link, start prefetching before the hover event fires.")
        (~docs/code :src (highlight "(define bind-approach-prefetch\n  (fn (container)\n    ;; Track mouse trajectory within a nav container.\n    ;; On each mousemove, extrapolate position ~200ms ahead.\n    ;; If projected point intersects a link's bounding box,\n    ;; prefetch that link's route deps.\n    (let ((last-x 0) (last-y 0) (last-t 0)\n          (prefetched (dict)))\n      (dom-add-listener container \"mousemove\"\n        (fn (e)\n          (let ((now (timestamp))\n                (dt (- now last-t)))\n            (when (> dt 16) ;; ~60fps throttle\n              (let ((vx (/ (- (event-x e) last-x) dt))\n                    (vy (/ (- (event-y e) last-y) dt))\n                    (px (+ (event-x e) (* vx 200)))\n                    (py (+ (event-y e) (* vy 200)))\n                    (target (dom-element-at-point px py)))\n                (when (and target (dom-has-attr? target \"href\")\n                           (not (get prefetched\n                             (dom-get-attr target \"href\"))))\n                  (let ((href (dom-get-attr target \"href\")))\n                    (set! prefetched\n                      (merge prefetched {href true}))\n                    (prefetch-route-deps\n                      (url-pathname href)))))\n              (set! last-x (event-x e))\n              (set! last-y (event-y e))\n              (set! last-t now))))))))" "lisp"))
        (p "This is the most speculative strategy — best suited for dense navigation areas (section sidebars, nav bars) where the cursor trajectory is a strong predictor. The " (code "prefetched") " dict prevents duplicate fetches within the same container interaction."))

      (~docs/subsection :title "Components + Data (Hybrid Prefetch)"
        (p "The most interesting strategy. For pages with " (code ":data") " dependencies, current behavior is full server fallback. But the page's " (em "components") " are still pure and prefetchable. If we prefetch components ahead of time, the click only needs to fetch " (em "data") " — a much smaller, faster response.")
        (p "This creates a new rendering path:")
        (ol (~tw :tokens "list-decimal pl-5 text-stone-700 space-y-1")
          (li "Prefetch: hover/idle/viewport triggers " (code "prefetch-components") " for the target page")
          (li "Click: client has components, but page has " (code ":data") " — fetch data from server")
          (li "Server returns " (em "only data") " (JSON or SX bindings), not the full rendered page")
          (li "Client evaluates the content expression with prefetched components + fetched data")
          (li "Result: faster than full server render, no redundant component transfer"))
        (~docs/code :src (highlight ";; Declarative: prefetch components, fetch data on click\n(defpage reference-page\n  :path \"/reference/<slug>\"\n  :auth :public\n  :prefetch :components  ;; prefetch components, data stays server-fetched\n  :data (reference-data slug)\n  :content (~reference/attrs-content :attrs attrs))\n\n;; On click, client-side flow:\n;; 1. Components already prefetched (from hover/idle)\n;; 2. GET /reference/attributes → server returns data bindings\n;; 3. Client evals (reference-data slug) result + content expr\n;; 4. Renders locally with cached components" "lisp"))
        (p "This is a stepping stone toward full Phase 4 (client IO bridge) of the isomorphic roadmap — it achieves partial client rendering for data pages without needing a general-purpose client async evaluator. The server is a data service, the client is the renderer."))

      (~docs/subsection :title "Declarative Configuration"
        (p "All strategies configured via " (code "defpage") " metadata and " (code "sx-prefetch") " attributes on links/containers:")
        (~docs/code :src (highlight ";; Page-level: what to prefetch for routes linking TO this page\n(defpage docs-page\n  :path \"/language/docs/<slug>\"\n  :prefetch :eager)      ;; bundle with linking page\n\n(defpage reference-page\n  :path \"/reference/<slug>\"\n  :prefetch :components) ;; prefetch components, data on click\n\n;; Link-level: override per-link\n(a :href \"/language/docs/components\"\n   :sx-prefetch \"idle\")   ;; prefetch after page idle\n\n;; Container-level: approach prediction for nav areas\n(nav :sx-prefetch \"approach\"\n  (a :href \"/language/docs/\") (a :href \"/reference/\") ...)" "lisp"))
        (p "Priority cascade: explicit " (code "sx-prefetch") " on link > " (code ":prefetch") " on target defpage > default (hover). The system never prefetches the same components twice — " (code "_prefetch-pending") " and " (code "loaded-component-names") " handle dedup.")))

    ;; -----------------------------------------------------------------------
    ;; Design
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Implementation Design" :id "design"

      (p "Per the SX host architecture principle: all SX-specific logic goes in " (code ".sx") " spec files and gets bootstrapped. The prefetch logic — scanning links, computing missing deps, managing the component cache — must be specced in " (code ".sx") ", not written directly in JS or Python.")

      (~docs/subsection :title "Phase 1: Component Fetch Endpoint (Python)"
        (p "A new " (strong "public") " endpoint (not " (code "/internal/") " — the client's browser calls it) that returns component definitions by name.")
        (~docs/code :src (highlight "GET /<service-prefix>/sx/components?names=~plans/predictive-prefetch/card,~essay-foo\n\nResponse (text/sx):\n(defcomp ~plans/predictive-prefetch/card (&key title &rest children)\n  (div :class \"border rounded p-4\" (h2 title) children))\n(defcomp ~plans/predictive-prefetch/essay-foo (&key id)\n  (div (~plans/predictive-prefetch/card :title id)))" "http"))
        (p "The server resolves transitive deps via " (code "deps.py") ", subtracts anything listed in the " (code "SX-Components") " request header (already loaded), serializes and returns. This is essentially " (code "components_for_request()") " driven by an explicit " (code "?names=") " param.")
        (p "Cache-friendly: the response is a pure function of component hash + requested names. " (code "Cache-Control: public, max-age=3600") " with the component hash as ETag."))

      (~docs/subsection :title "Phase 2: Client Prefetch Logic (SX spec)"
        (p "New functions in " (code "orchestration.sx") " (or a new " (code "prefetch.sx") " if scope warrants):")

        (div (~tw :tokens "space-y-4")
          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "1. compute-missing-deps")
            (p "Given a pathname, find the page, return dep names not in " (code "loaded-component-names") ". Returns nil if page not found or has data (can't client-route anyway).")
            (~docs/code :src (highlight "(define compute-missing-deps\n  (fn (pathname)\n    (let ((match (find-matching-route pathname _page-routes)))\n      (when (and match (not (get match \"has-data\")))\n        (let ((deps (or (get match \"deps\") (list)))\n              (loaded (loaded-component-names)))\n          (filter (fn (d) (not (contains? loaded d))) deps))))))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "2. prefetch-components")
            (p "Fetch component definitions from the server for a list of names. Deduplicates in-flight requests. On success, parses and registers the returned definitions into the component env.")
            (~docs/code :src (highlight "(define _prefetch-pending (dict))\n\n(define prefetch-components\n  (fn (names)\n    (let ((key (join \",\" (sort names))))\n      (when (not (get _prefetch-pending key))\n        (set! _prefetch-pending\n          (merge _prefetch-pending {key true}))\n        (fetch-components-from-server names\n          (fn (sx-text)\n            (sx-process-component-text sx-text)\n            (dict-remove! _prefetch-pending key)))))))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "3. prefetch-route-deps")
            (p "High-level composition: compute missing deps for a route, fetch if any.")
            (~docs/code :src (highlight "(define prefetch-route-deps\n  (fn (pathname)\n    (let ((missing (compute-missing-deps pathname)))\n      (when (and missing (not (empty? missing)))\n        (log-info (str \"sx:prefetch \"\n          (len missing) \" components for \" pathname))\n        (prefetch-components missing)))))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "4. Trigger: link hover")
            (p "On mouseover of a boosted link, prefetch its route's missing components. Debounced 150ms to avoid fetching on quick mouse-throughs.")
            (~docs/code :src (highlight "(define bind-prefetch-on-hover\n  (fn (link)\n    (let ((timer nil))\n      (dom-add-listener link \"mouseover\"\n        (fn (e)\n          (clear-timeout timer)\n          (set! timer (set-timeout\n            (fn () (prefetch-route-deps\n              (url-pathname (dom-get-attr link \"href\"))))\n            150))))\n      (dom-add-listener link \"mouseout\"\n        (fn (e) (clear-timeout timer))))))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "5. Trigger: viewport intersection (opt-in)")
            (p "More aggressive strategy: when a link scrolls into view, prefetch its route's deps. Opt-in via " (code "sx-prefetch=\"visible\"") " attribute.")
            (~docs/code :src (highlight "(define bind-prefetch-on-visible\n  (fn (link)\n    (observe-intersection link\n      (fn () (prefetch-route-deps\n        (url-pathname (dom-get-attr link \"href\"))))\n      true 0)))" "lisp")))

          (div
            (h4 (~tw :tokens "font-semibold text-stone-700") "6. Integration into process-elements")
            (p "During the existing hydration pass, for each boosted link:")
            (~docs/code :src (highlight ";; In process-elements, after binding boost behavior:\n(when (and (should-boost-link? link)\n           (dom-get-attr link \"href\"))\n  (bind-prefetch-on-hover link))\n\n;; Explicit viewport prefetch:\n(when (dom-has-attr? link \"sx-prefetch\")\n  (bind-prefetch-on-visible link))" "lisp")))))

      (~docs/subsection :title "Phase 3: Boundary Declaration"
        (p "Two new IO primitives in " (code "boundary.sx") " (browser-only):")
        (~docs/code :src (highlight ";; IO primitives (browser-only)\n(io fetch-components-from-server (names callback) -> void)\n(io sx-process-component-text (sx-text) -> void)" "lisp"))
        (p "These are thin wrappers around " (code "fetch()") " + the existing component script processing logic already in the boundary adapter."))

      (~docs/subsection :title "Phase 4: Bootstrap"
        (p (code "bootstrap_js.py") " picks up the new functions from the spec and emits them into " (code "sx-browser.js") ". The two new boundary IO functions get implemented in the JS boundary adapter — the hand-written glue code that the bootstrapper doesn't generate.")
        (~docs/code :src (highlight "// fetch-components-from-server: calls the endpoint\nfunction fetchComponentsFromServer(names, callback) {\n  const url = `${routePrefix}/sx/components?names=${names.join(\",\")}`;\n  const headers = {\n    \"SX-Components\": loadedComponentNames().join(\",\")\n  };\n  fetch(url, { headers })\n    .then(r => r.ok ? r.text() : \"\")\n    .then(text => callback(text))\n    .catch(() => {}); // silent fail — prefetch is best-effort\n}\n\n// sx-process-component-text: parse defcomp/defmacro into env\nfunction sxProcessComponentText(sxText) {\n  if (!sxText) return;\n  const frag = document.createElement(\"div\");\n  frag.innerHTML =\n    `<script type=\"text/sx\" data-components>${sxText}<\\/script>`;\n  Sx.processScripts(frag);\n}" "javascript"))))

    ;; -----------------------------------------------------------------------
    ;; Request flow
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Request Flow" :id "request-flow"
      (p "End-to-end example: user hovers a link, components prefetch, click goes client-side.")
      (~docs/code :src (highlight "User hovers link \"/language/docs/sx-manifesto\"\n  |\n  +-- bind-prefetch-on-hover fires (150ms debounce)\n  |\n  +-- compute-missing-deps(\"/language/docs/sx-manifesto\")\n  |     +-- find-matching-route -> page with deps:\n  |     |     [\"~essay-sx-manifesto\", \"~doc-code\"]\n  |     +-- loaded-component-names -> [\"~nav\", \"~footer\", \"~doc-code\"]\n  |     +-- missing: [\"~essay-sx-manifesto\"]\n  |\n  +-- prefetch-components([\"~essay-sx-manifesto\"])\n  |     +-- GET /sx/components?names=~essay-sx-manifesto\n  |     |     Headers: SX-Components: ~plans/environment-images/nav,~footer,~doc-code\n  |     +-- Server resolves transitive deps\n  |     |     (also needs ~plans/predictive-prefetch/rich-text, subtracts already-loaded)\n  |     +-- Response:\n  |           (defcomp ~plans/predictive-prefetch/essay-sx-manifesto ...) \n  |           (defcomp ~plans/predictive-prefetch/rich-text ...)\n  |\n  +-- sx-process-component-text registers defcomps in env\n  |\n  +-- User clicks link\n        +-- try-client-route(\"/language/docs/sx-manifesto\")\n        +-- has-all-deps? -> true (prefetched!)\n        +-- eval content -> DOM\n        +-- Client-side render, no server roundtrip" "text")))

    ;; -----------------------------------------------------------------------
    ;; File changes
    ;; -----------------------------------------------------------------------

    (~docs/section :title "File Changes" :id "file-changes"
      (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") "File")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Change")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Phase")))
          (tbody
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/helpers.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "New " (code "sx_components_endpoint()") " route handler")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "1"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/infrastructure/factory.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Register " (code "/sx/components") " route on all SX apps")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "1"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/orchestration.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Prefetch functions: compute-missing-deps, prefetch-components, prefetch-route-deps, bind-prefetch-on-hover, bind-prefetch-on-visible")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "2"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/boundary.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Declare " (code "fetch-components-from-server") ", " (code "sx-process-component-text"))
              (td (~tw :tokens "px-3 py-2 text-stone-600") "3"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/bootstrap_js.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Emit new spec functions, boundary adapter stubs")
              (td (~tw :tokens "px-3 py-2 text-stone-600") "4"))))))

    ;; -----------------------------------------------------------------------
    ;; Non-goals & rollout
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Non-Goals (This Phase)" :id "non-goals"
      (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
        (li (strong "Analytics-driven prediction") " — no ML models or click-frequency heuristics. Trajectory prediction uses geometry, not statistics.")
        (li (strong "Cross-service prefetch") " — components are per-service. A link to a different service domain is always a server navigation.")
        (li (strong "Service worker caching") " — could layer on later, but basic fetch + in-memory registration is sufficient.")
        (li (strong "Full client-side data evaluation") " — the components+data strategy fetches data from the server, it doesn't replicate server IO on the client. That's Phase 4 of the isomorphic roadmap.")))

    (~docs/section :title "Rollout" :id "rollout"
      (p "Incremental, each step independently valuable:")
      (ol (~tw :tokens "list-decimal pl-5 text-stone-700 space-y-2")
        (li (strong "Component endpoint") " — purely additive. Refactor " (code "components_for_request()") " to accept explicit " (code "?names=") " param.")
        (li (strong "Core spec functions") " — " (code "compute-missing-deps") ", " (code "prefetch-components") ", " (code "prefetch-route-deps") " in orchestration.sx. Testable in isolation.")
        (li (strong "Hover prefetch") " — wire " (code "bind-prefetch-on-hover") " into " (code "process-elements") ". All boosted links get it automatically. Console logs show activity.")
        (li (strong "Idle batch prefetch") " — call " (code "prefetch-visible-links-on-idle") " from " (code "boot-init") ". One request prefetches all visible nav deps after page settles.")
        (li (strong "Viewport + approach") " — opt-in via " (code "sx-prefetch") " attributes. Trajectory prediction for dense nav areas.")
        (li (strong "Eager bundles") " — extend " (code "components_for_page()") " to include linked routes' deps. Heavier initial payload, zero-latency nav.")
        (li (strong "Components + data split") " — new server response mode returning data bindings only. Client renders with prefetched components. Bridges toward Phase 4.")))

    (~docs/section :title "Relationship to Isomorphic Roadmap" :id "relationship"
      (p "This plan sits between Phase 3 (client-side routing) and Phase 4 (client async & IO bridge) of the "
        (a :href "/sx/(etc.(plan.isomorphic-architecture))" (~tw :tokens "text-violet-700 underline") "isomorphic architecture roadmap")
        ". It extends Phase 3 by making more navigations go client-side without needing any IO bridge — purely by ensuring component definitions are available before they're needed.")
      (div (~tw :tokens "rounded border border-amber-200 bg-amber-50 p-3 mt-2")
        (p (~tw :tokens "text-amber-800 text-sm") (strong "Depends on: ") "Phase 3 (client-side routing with deps checking). No dependency on Phase 4.")))))
;; ---------------------------------------------------------------------------
;; Plan Status Overview
;; ---------------------------------------------------------------------------
