;; ---------------------------------------------------------------------------
;; L4: Render Loop — bouncing ball
;; ---------------------------------------------------------------------------
(defisland ()
  (let ((running (signal true))
        (x (signal 0))
        (dir (signal 1))
        (frames (signal 0)))
    (let ((_eff (effect (fn ()
            (when (deref running)
              (let ((id (set-interval
                  (fn ()
                    (batch (fn ()
                      (swap! frames inc)
                      (when (>= (deref x) 230) (reset! dir -1))
                      (when (<= (deref x) 0) (reset! dir 1))
                      (swap! x (fn (v) (+ v (* (deref dir) 3)))))))
                  16)))
                (fn () (clear-interval id))))))))
      (div (~tw :tokens "rounded border border-violet-200 bg-violet-50 p-4 my-4")
        (div (~tw :tokens "relative h-12 bg-white rounded border border-stone-200 mb-3 overflow-hidden")
          (div (~tw :tokens "absolute top-1 w-10 h-10 rounded-full bg-violet-500 transition-none")
            :style (str "left:" (deref x) "px")))
        (div (~tw :tokens "flex items-center gap-3")
          (button (~tw :tokens "px-3 py-1 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700")
            :on-click (fn (e) (swap! running not))
            (if (deref running) "Pause" "Play"))
          (button (~tw :tokens "px-3 py-1 rounded bg-stone-300 text-stone-700 text-sm hover:bg-stone-400")
            :on-click (fn (e)
              (batch (fn ()
                (reset! running false)
                (reset! x 0)
                (reset! dir 1)
                (reset! frames 0))))
            "Reset")
          (span (~tw :tokens "text-sm text-stone-500 font-mono")
            "frames: " (deref frames)))))))
