(defcomp
  ()
  (~docs/page
    :title "Spreads"
    (p
      (~tw :tokens "text-stone-500 text-sm italic mb-8")
      "A spread is a value that, when returned as a child of an element, "
      "injects attributes onto its parent instead of rendering as content. "
      "Internally, spreads work through "
      (a
        :href "/sx/(geography.(scopes))"
        (~tw :tokens "text-violet-600 hover:underline")
        "scopes")
      " — every element creates a scope, and spread children emit into it.")
    (~docs/section
      :title "How it works"
      :id "mechanism"
      (p
        "Every element wraps its children in a "
        (code "provide")
        " scope named "
        (code "\"element-attrs\"")
        ". When the renderer encounters a spread child, "
        "it calls "
        (code "emit!")
        " to push the spread's attrs into that scope. "
        "After all children render, the element collects the emitted attrs and merges them. "
        "This is the "
        (a
          :href "/sx/(geography.(provide))"
          (~tw :tokens "text-violet-600 hover:underline")
          "provide/emit!")
        " mechanism — spreads are one instance of it.")
      (p
        "The user-facing API is "
        (code "make-spread")
        " — it creates a spread value from a dict. "
        (code "spread?")
        " tests for one. "
        (code "spread-attrs")
        " extracts the dict. "
        "But the actual delivery from child to parent goes through provide/emit!, not through "
        "return-value inspection.")
      (~geography/spreads/demo-example
        :demo (~geography/spreads/demo-spread-basic)
        :code (highlight
          "(defcomp ~callout (&key type)\n  (make-spread\n    (cond\n      (= type \"info\")\n        {\"style\" \"border-left:4px solid\n                  #3b82f6; background:#eff6ff\"}\n      (= type \"warning\")\n        {\"style\" \"border-left:4px solid\n                  #f59e0b; background:#fffbeb\"}\n      (= type \"success\")\n        {\"style\" \"border-left:4px solid\n                  #10b981; background:#ecfdf5\"})))\n\n;; Child injects attrs onto parent:\n(div (~callout :type \"info\")\n  \"This div gets the callout style.\")"
          "lisp"))
      (p
        (code "class")
        " values are appended (space-joined). "
        (code "style")
        " values are appended (semicolon-joined). "
        "All other attributes overwrite.")
      (p
        "Tolerant "
        (code "emit!")
        " means a spread outside any element context "
        "(in a fragment, "
        (code "begin")
        ", or bare "
        (code "map")
        ") silently vanishes "
        "instead of crashing — there's no provider to emit into, so it's a no-op."))
    (~docs/section
      :title "collect! — the other upward channel"
      :id "collect"
      (p
        "Spreads use "
        (code "scope")
        "/"
        (code "emit!")
        " (scoped, no dedup). "
        (code "collect!")
        "/"
        (code "collected")
        " is also backed by scopes — "
        "a lazy root scope with automatic deduplication. Used for CSS rule accumulation.")
      (~docs/code
        :src (highlight
          ";; Deep inside a component tree:\n(collect! \"cssx\" \".sx-bg-red-500{background:red}\")\n\n;; At the flush point (once, in the layout):\n(let ((rules (collected \"cssx\")))\n  (clear-collected! \"cssx\")\n  (raw! (str \"<style>\" (join \"\" rules) \"</style>\")))"
          "lisp"))
      (p
        "Both are upward communication through the render tree, but with different "
        "semantics — "
        (code "emit!")
        " is scoped to the nearest provider, "
        (code "collect!")
        " is global and deduplicates."))
    (~docs/section
      :title "Reactive spreads"
      :id "reactive"
      (~docs/subsection
        :title "reactive-spread (islands)"
        (p
          "Inside an island, when a spread's value depends on signals, "
          (code "reactive-spread")
          " tracks signal dependencies and surgically "
          "updates the parent element's attributes when signals change.")
        (~geography/spreads/demo-example
          :demo (~geography/spreads/demo-reactive-spread)
          :code (highlight
            "(defisland ~themed-card ()\n  (let ((theme (signal \"violet\")))\n    (div (~cssx/tw :tokens\n           (str \"bg-\" (deref theme)\n                \"-100 p-4\"))\n      (button\n        :on-click\n          (fn (e) (reset! theme \"rose\"))\n        \"change theme\"))))"
            "lisp"))
        (p "When " (code "theme") " changes:")
        (ul
          (~tw :tokens "list-disc pl-5 space-y-1 text-stone-600")
          (li "Old classes are removed from the element")
          (li "New classes are added")
          (li
            "New CSS rules are JIT-generated and flushed to the live stylesheet")
          (li "No re-render. No VDOM. No diffing. Just attr surgery."))))
    (~docs/section
      :title "Orthogonality across boundaries"
      :id "orthogonality"
      (p
        "Spread (via provide/emit!), collect!, and reactive-spread operate at different "
        "levels of the render pipeline. They compose without knowing about each other.")
      (~docs/table
        :headers (list "Primitive" "Direction" "Boundary" "When")
        :rows (list
          (list "spread" "child → parent" "element boundary" "render time")
          (list "collect!" "child → ancestor" "render tree" "render time")
          (list
            "reactive-spread"
            "child → parent"
            "element boundary"
            "signal change")))
      (~docs/subsection
        :title "Server rendering (HTML adapter)"
        (p
          "On the server, "
          (code "~cssx/tw")
          " returns a spread. The HTML renderer "
          "merges the class onto the parent element. "
          (code "collect!")
          " accumulates "
          "CSS rules. "
          (code "~cssx/flush")
          " emits a single "
          (code "<style>")
          " tag. "
          "Result: static HTML with all classes and styles baked in."))
      (~docs/subsection
        :title "Client hydration (DOM adapter)"
        (p
          "On the client, the same "
          (code "~cssx/tw")
          " call inside an island "
          "produces a spread that the DOM adapter merges onto the parent element. "
          "If the spread depends on signals, "
          (code "reactive-spread")
          " wraps it "
          "in an effect. Static spreads are applied once; reactive ones track deps."))
      (~docs/subsection
        :title "Morph (navigation)"
        (p
          "When the server sends new HTML during SX navigation, the morph algorithm "
          "enters islands, finds lakes, updates server content. Reactive spreads are "
          "protected: "
          (code "data-sx-reactive-attrs")
          " tells the morph to skip "
          "attributes managed by signal effects. The server water flows through; "
          "the reactive rocks stay put."))
      (~docs/subsection
        :title "The matrix"
        (~docs/table
          :headers (list "" "Server state" "Client state (signals)")
          :rows (list
            (list
              "Server rendering"
              "spread + collect! (pure hypermedia)"
              "SSR + hydrated reactive-spread")
            (list
              "Client rendering"
              "spread in wire format (aser)"
              "reactive-spread (live)")))))
    (~docs/section
      :title "CSSX: the first application"
      :id "cssx"
      (p (code "~cssx/tw") " is the primary consumer of spreads:")
      (~geography/spreads/demo-example
        :demo (~geography/spreads/demo-cssx-tw)
        :code (highlight
          "(defcomp ~cssx/tw (tokens)\n  (let ((token-list\n          (filter (fn (t) (not (= t \"\")))\n            (split (or tokens \"\") \" \")))\n        (results\n          (map cssx-process-token\n               token-list))\n        (classes (map (fn (r)\n          (get r \"cls\")) results))\n        (rules (map (fn (r)\n          (get r \"rule\")) results)))\n    (for-each (fn (rule)\n      (collect! \"cssx\" rule)) rules)\n    (make-spread\n      {\"class\" (join \" \" classes)})))"
          "lisp"))
      (p
        "It uses "
        (code "make-spread")
        " to inject classes, "
        (code "collect!")
        " to accumulate CSS rules for batch flushing, and "
        "when called inside an island with signal-dependent tokens, "
        (code "reactive-spread")
        " makes it live.")
      (p
        "But "
        (code "~cssx/tw")
        " is just one instance. The same mechanism enables:")
      (ul
        (~tw :tokens "list-disc pl-5 space-y-1 text-stone-600")
        (li
          (code "~aria")
          " — reactive accessibility attributes driven by UI state")
        (li
          (code "~data-attrs")
          " — signal-driven data attributes for coordination")
        (li
          (code "~conditional-attrs")
          " — presence/absence of attributes based on state")
        (li "Any component that needs to inject attributes onto its parent")))
    (~docs/section
      :title "Semantic style variables"
      :id "variables"
      (p
        "Because "
        (code "~cssx/tw")
        " returns a spread, and spreads are values, "
        "you can bind them to names:")
      (~geography/spreads/demo-example
        :demo (~geography/spreads/demo-semantic-vars)
        :code (highlight
          "(let ((card (~cssx/tw :tokens\n        \"bg-stone-50 rounded-lg p-4\n         shadow-sm border\"))\n      (heading (~cssx/tw :tokens\n        \"text-violet-700 text-lg\n         font-bold\"))\n      (body (~cssx/tw :tokens\n        \"text-stone-600 text-sm\")))\n  ;; Reuse everywhere:\n  (div card\n    (h4 heading \"First Card\")\n    (p body \"Same variables.\"))\n  (div card\n    (h4 heading \"Second Card\")\n    (p body \"Consistent look.\")))"
          "lisp"))
      (p
        "These are semantic names wrapping utility tokens. Change the definition, "
        "every use updates. No build step, no CSS-in-JS runtime. Just "
        (code "let")
        ".")
      (p
        "Namespacing prevents clashes — "
        (code "~app/heading")
        " vs "
        (code "~admin/heading")
        " are different components in different namespaces."))
    (~docs/section
      :title "What nothing else does"
      :id "unique"
      (p
        "React can't do this. In React, attributes live in the parent's JSX. "
        "A child component cannot inject attrs onto its parent element. You'd need "
        "to lift state up, pass callbacks, use context, or reach for "
        (code "forwardRef")
        " — all of which couple the child to the parent's rendering logic.")
      (p
        "CSS-in-JS libraries (styled-components, Emotion) create "
        (em "wrapper elements")
        ". They don't inject attrs onto an existing element from a child position. "
        "And they need a build step, a runtime, a theme provider.")
      (p
        "SX does it with "
        (code "provide")
        "/"
        (code "emit!")
        " — render-time dynamic scope "
        "that already existed for other reasons:")
      (ul
        (~tw :tokens "list-disc pl-5 space-y-1 text-stone-600")
        (li
          (strong "provide/emit!")
          " — scoped upward communication (the general mechanism)")
        (li
          (strong "make-spread")
          " — creates spread values that emit into element-attrs providers")
        (li
          (strong "reactive-spread")
          " — wraps emit! in a signal effect for live updates"))
      (p "No new concepts. No new runtime. No new API surface."))
    (~docs/section
      :title "The general primitive"
      :id "provide"
      (p
        "Spreads, collect!, and context are all instances of one mechanism: "
        (code "provide")
        "/"
        (code "emit!")
        " — render-time dynamic scope. "
        (a
          :href "/sx/(geography.(provide))"
          (~tw :tokens "text-violet-600 hover:underline")
          "See the full provide/context/emit! article")
        " for the general primitive, nested scoping, and its other uses.")
      (~docs/table
        :headers (list "Mechanism" "General form" "Direction")
        :rows (list
          (list
            "spread"
            "emit! into element-attrs provider"
            "upward (child → parent)")
          (list
            "collect! / collected"
            "emit! / emitted (global, deduped)"
            "upward (child → scope)")
          (list "theme / config" "context" "downward (scope → child)")))
      (~docs/note
        (p
          (strong "Spec: ")
          "The provide/emit! primitives are declared in "
          (a
            :href "/sx/(language.(spec.(explore.boundary)))"
            (~tw :tokens "font-mono text-violet-600 hover:underline text-sm")
            "boundary.sx")
          " (Tier 5: Dynamic scope). The "
          (code "provide")
          " special form is in "
          (a
            :href "/sx/(language.(spec.(explore.evaluator)))"
            (~tw :tokens "font-mono text-violet-600 hover:underline text-sm")
            "eval.sx")
          ". Element rendering with provide/emit! is visible in all four adapter specs: "
          (a
            :href "/sx/(language.(spec.(explore.adapter-html)))"
            (~tw :tokens "font-mono text-violet-600 hover:underline text-sm")
            "adapter-html")
          ", "
          (a
            :href "/sx/(language.(spec.(explore.adapter-async)))"
            (~tw :tokens "font-mono text-violet-600 hover:underline text-sm")
            "adapter-async")
          ", "
          (a
            :href "/sx/(language.(spec.(explore.adapter-sx)))"
            (~tw :tokens "font-mono text-violet-600 hover:underline text-sm")
            "adapter-sx")
          ", "
          (a
            :href "/sx/(language.(spec.(explore.adapter-dom)))"
            (~tw :tokens "font-mono text-violet-600 hover:underline text-sm")
            "adapter-dom")
          ".")))))
