(defcomp
  ()
  (~docs/page
    :title "CEK Machine"
    (~docs/section
      :title "Three registers"
      :id "registers"
      (p
        "The CEK machine makes evaluation explicit. Every step is a pure function from state to state:")
      (ul
        (~tw :tokens "space-y-1 text-stone-600 list-disc pl-5")
        (li (strong "C") "ontrol — the expression being evaluated")
        (li (strong "E") "nvironment — the bindings in scope")
        (li (strong "K") "ontinuation — what to do with the result"))
      (p
        "The tree-walk evaluator uses the same three things, but hides them in the call stack. The CEK makes them "
        (em "data")
        " — inspectable, serializable, capturable."))
    (~docs/section
      :title "Why it matters"
      :id "why"
      (p "Making the continuation explicit enables:")
      (ul
        (~tw :tokens "space-y-1 text-stone-600 list-disc pl-5")
        (li
          (strong "Stepping")
          " — pause evaluation, inspect state, resume")
        (li
          (strong "Serialization")
          " — save a computation mid-flight, restore later")
        (li
          (strong "Delimited continuations")
          " — "
          (code "shift")
          "/"
          (code "reset")
          " capture \"the rest of this expression\" as a value")
        (li
          (strong "Deref-as-shift")
          " — "
          (code "(deref sig)")
          " inside a reactive boundary captures the continuation as the subscriber")))
    (~docs/section
      :title "Default evaluator"
      :id "default"
      (p
        "CEK is the default evaluator on both client (JS) and server (Python). Every "
        (code "eval-expr")
        " call goes through "
        (code "cek-run")
        ". The tree-walk evaluator is preserved as "
        (code "_tree_walk_eval_expr")
        " for test runners that interpret "
        (code ".sx")
        " files.")
      (p "The CEK is defined in two spec files:")
      (ul
        (~tw :tokens "space-y-1 text-stone-600 list-disc pl-5")
        (li
          (code "frames.sx")
          " — frame types (IfFrame, ArgFrame, ResetFrame, ReactiveResetFrame, ...)")
        (li
          (code "cek.sx")
          " — step function, run loop, special form handlers, continuation operations")))
    (~docs/section
      :title "Deref as shift"
      :id "deref-as-shift"
      (p
        "The reactive payoff. When "
        (code "(deref sig)")
        " encounters a signal inside a "
        (code "reactive-reset")
        " boundary:")
      (ol
        (~tw :tokens "space-y-1 text-stone-600 list-decimal pl-5")
        (li
          (strong "Shift")
          " — capture all frames between here and the reactive-reset")
        (li
          (strong "Subscribe")
          " — register the captured continuation as a signal subscriber")
        (li
          (strong "Return")
          " — flow the current signal value through the rest of the expression"))
      (p
        "When the signal changes, the captured continuation is re-invoked with the new value. The "
        (code "update-fn")
        " on the ReactiveResetFrame mutates the DOM. No explicit "
        (code "effect()")
        " wrapping needed.")
      (~docs/code
        :src (highlight
          ";; User writes:\n(div :class (str \"count-\" (deref counter))\n  (str \"Value: \" (deref counter)))\n\n;; CEK sees (deref counter) → signal? → reactive-reset on stack?\n;; Yes: capture (str \"count-\" [HOLE]) as continuation\n;; Register as subscriber. Return current value.\n;; When counter changes: re-invoke continuation → update DOM."
          "lisp")))))
