;; Blog editor components

(defcomp ~blog-editor-error (&key error)
  (div :class "max-w-[768px] mx-auto mt-[16px] rounded-[8px] border border-red-300 bg-red-50 px-[16px] py-[12px] text-[14px] text-red-700"
    (strong "Save failed:") " " error))

(defcomp ~blog-editor-form (&key csrf title-placeholder create-label)
  (form :id "post-new-form" :method "post" :class "max-w-[768px] mx-auto pb-[48px]"
    (input :type "hidden" :name "csrf_token" :value csrf)
    (input :type "hidden" :id "lexical-json-input" :name "lexical" :value "")
    (input :type "hidden" :id "sx-content-input" :name "sx_content" :value "")
    (input :type "hidden" :id "feature-image-input" :name "feature_image" :value "")
    (input :type "hidden" :id "feature-image-caption-input" :name "feature_image_caption" :value "")
    (div :id "feature-image-container" :class "relative mt-[16px] mb-[24px] group"
      (div :id "feature-image-empty"
        (button :type "button" :id "feature-image-add-btn"
          :class "text-[14px] text-stone-400 hover:text-stone-600 transition-colors cursor-pointer"
          "+ Add feature image"))
      (div :id "feature-image-filled" :class "relative hidden"
        (img :id "feature-image-preview" :src "" :alt ""
          :class "w-full max-h-[448px] object-cover rounded-[8px] cursor-pointer")
        (button :type "button" :id "feature-image-delete-btn"
          :class "absolute top-[8px] right-[8px] w-[32px] h-[32px] rounded-full bg-black/50 text-white flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/70 cursor-pointer text-[14px]"
          :title "Remove feature image"
          (i :class "fa-solid fa-trash-can"))
        (input :type "text" :id "feature-image-caption" :value ""
          :placeholder "Add a caption..."
          :class "mt-[8px] w-full text-[14px] text-stone-500 bg-transparent border-none outline-none placeholder:text-stone-300 focus:text-stone-700"))
      (div :id "feature-image-uploading"
        :class "hidden flex items-center gap-[8px] mt-[8px] text-[14px] text-stone-400"
        (i :class "fa-solid fa-spinner fa-spin") " Uploading...")
      (input :type "file" :id "feature-image-file"
        :accept "image/jpeg,image/png,image/gif,image/webp,image/svg+xml" :class "hidden"))
    (input :type "text" :name "title" :value "" :placeholder title-placeholder
      :class "w-full text-[36px] font-bold bg-transparent border-none outline-none placeholder:text-stone-300 mb-[8px] leading-tight")
    (textarea :name "custom_excerpt" :rows "1" :placeholder "Add an excerpt..."
      :class "w-full text-[18px] text-stone-500 bg-transparent border-none outline-none placeholder:text-stone-300 resize-none mb-[24px] leading-relaxed")
    ;; Editor tabs: SX (primary) and Koenig (legacy)
    (div :class "flex gap-[4px] mb-[8px] border-b border-stone-200"
      (button :type "button" :id "editor-tab-sx"
        :class "px-[12px] py-[6px] text-[13px] font-medium text-stone-700 border-b-2 border-stone-700 cursor-pointer bg-transparent"
        :onclick "document.getElementById('sx-editor').style.display='block';document.getElementById('lexical-editor').style.display='none';this.className='px-[12px] py-[6px] text-[13px] font-medium text-stone-700 border-b-2 border-stone-700 cursor-pointer bg-transparent';document.getElementById('editor-tab-koenig').className='px-[12px] py-[6px] text-[13px] font-medium text-stone-400 border-b-2 border-transparent cursor-pointer bg-transparent hover:text-stone-600'"
        "SX Editor")
      (button :type "button" :id "editor-tab-koenig"
        :class "px-[12px] py-[6px] text-[13px] font-medium text-stone-400 border-b-2 border-transparent cursor-pointer bg-transparent hover:text-stone-600"
        :onclick "document.getElementById('lexical-editor').style.display='block';document.getElementById('sx-editor').style.display='none';this.className='px-[12px] py-[6px] text-[13px] font-medium text-stone-700 border-b-2 border-stone-700 cursor-pointer bg-transparent';document.getElementById('editor-tab-sx').className='px-[12px] py-[6px] text-[13px] font-medium text-stone-400 border-b-2 border-transparent cursor-pointer bg-transparent hover:text-stone-600'"
        "Koenig (Legacy)"))
    (div :id "sx-editor" :class "relative w-full bg-transparent")
    (div :id "lexical-editor" :class "relative w-full bg-transparent" :style "display:none")
    (div :class "flex items-center gap-[16px] mt-[32px] pt-[16px] border-t border-stone-200"
      (select :name "status"
        :class "text-[14px] rounded-[4px] border border-stone-200 px-[8px] py-[6px] bg-white text-stone-600"
        (option :value "draft" :selected true "Draft")
        (option :value "published" "Published"))
      (button :type "submit"
        :class "px-[20px] py-[6px] bg-stone-700 text-white text-[14px] rounded-[8px] hover:bg-stone-800 transition-colors cursor-pointer" create-label))))

;; Edit form — pre-populated version for /<slug>/admin/edit/
(defcomp ~blog-editor-edit-form (&key csrf updated-at title-val excerpt-val
                                      feature-image feature-image-caption
                                      sx-content-val lexical-json
                                      has-sx title-placeholder
                                      status already-emailed
                                      newsletter-options footer-extra)
  (let* ((sel-cls "text-[14px] rounded-[4px] border border-stone-200 px-[8px] py-[6px] bg-white text-stone-600")
         (active "px-[12px] py-[6px] text-[13px] font-medium text-stone-700 border-b-2 border-stone-700 cursor-pointer bg-transparent")
         (inactive "px-[12px] py-[6px] text-[13px] font-medium text-stone-400 border-b-2 border-transparent cursor-pointer bg-transparent hover:text-stone-600"))
    (form :id "post-edit-form" :method "post" :class "max-w-[768px] mx-auto pb-[48px]"
      (input :type "hidden" :name "csrf_token" :value csrf)
      (input :type "hidden" :name "updated_at" :value updated-at)
      (input :type "hidden" :id "lexical-json-input" :name "lexical" :value "")
      (input :type "hidden" :id "sx-content-input" :name "sx_content" :value (or sx-content-val ""))
      (input :type "hidden" :id "feature-image-input" :name "feature_image" :value (or feature-image ""))
      (input :type "hidden" :id "feature-image-caption-input" :name "feature_image_caption" :value (or feature-image-caption ""))
      (div :id "feature-image-container" :class "relative mt-[16px] mb-[24px] group"
        (div :id "feature-image-empty" :class (if feature-image "hidden" "")
          (button :type "button" :id "feature-image-add-btn"
            :class "text-[14px] text-stone-400 hover:text-stone-600 transition-colors cursor-pointer"
            "+ Add feature image"))
        (div :id "feature-image-filled" :class (str "relative " (if feature-image "" "hidden"))
          (img :id "feature-image-preview" :src (or feature-image "") :alt ""
            :class "w-full max-h-[448px] object-cover rounded-[8px] cursor-pointer")
          (button :type "button" :id "feature-image-delete-btn"
            :class "absolute top-[8px] right-[8px] w-[32px] h-[32px] rounded-full bg-black/50 text-white flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/70 cursor-pointer text-[14px]"
            :title "Remove feature image"
            (i :class "fa-solid fa-trash-can"))
          (input :type "text" :id "feature-image-caption" :value (or feature-image-caption "")
            :placeholder "Add a caption..."
            :class "mt-[8px] w-full text-[14px] text-stone-500 bg-transparent border-none outline-none placeholder:text-stone-300 focus:text-stone-700"))
        (div :id "feature-image-uploading"
          :class "hidden flex items-center gap-[8px] mt-[8px] text-[14px] text-stone-400"
          (i :class "fa-solid fa-spinner fa-spin") " Uploading...")
        (input :type "file" :id "feature-image-file"
          :accept "image/jpeg,image/png,image/gif,image/webp,image/svg+xml" :class "hidden"))
      (input :type "text" :name "title" :value (or title-val "") :placeholder title-placeholder
        :class "w-full text-[36px] font-bold bg-transparent border-none outline-none placeholder:text-stone-300 mb-[8px] leading-tight")
      (textarea :name "custom_excerpt" :rows "1" :placeholder "Add an excerpt..."
        :class "w-full text-[18px] text-stone-500 bg-transparent border-none outline-none placeholder:text-stone-300 resize-none mb-[24px] leading-relaxed"
        (or excerpt-val ""))
      ;; Editor tabs
      (div :class "flex gap-[4px] mb-[8px] border-b border-stone-200"
        (button :type "button" :id "editor-tab-sx"
          :class (if has-sx active inactive)
          :onclick "document.getElementById('sx-editor').style.display='block';document.getElementById('lexical-editor').style.display='none';this.className='px-[12px] py-[6px] text-[13px] font-medium text-stone-700 border-b-2 border-stone-700 cursor-pointer bg-transparent';document.getElementById('editor-tab-koenig').className='px-[12px] py-[6px] text-[13px] font-medium text-stone-400 border-b-2 border-transparent cursor-pointer bg-transparent hover:text-stone-600'"
          "SX Editor")
        (button :type "button" :id "editor-tab-koenig"
          :class (if has-sx inactive active)
          :onclick "document.getElementById('lexical-editor').style.display='block';document.getElementById('sx-editor').style.display='none';this.className='px-[12px] py-[6px] text-[13px] font-medium text-stone-700 border-b-2 border-stone-700 cursor-pointer bg-transparent';document.getElementById('editor-tab-sx').className='px-[12px] py-[6px] text-[13px] font-medium text-stone-400 border-b-2 border-transparent cursor-pointer bg-transparent hover:text-stone-600'"
          "Koenig (Legacy)"))
      (div :id "sx-editor" :class "relative w-full bg-transparent"
        :style (if has-sx "" "display:none"))
      (div :id "lexical-editor" :class "relative w-full bg-transparent"
        :style (if has-sx "display:none" ""))
      ;; Initial lexical JSON
      (script :id "lexical-initial-data" :type "application/json" lexical-json)
      ;; Footer: status + publish mode + newsletter + save + badges
      (div :class "flex flex-wrap items-center gap-[16px] mt-[32px] pt-[16px] border-t border-stone-200"
        (select :id "status-select" :name "status" :class sel-cls
          (option :value "draft" :selected (= status "draft") "Draft")
          (option :value "published" :selected (= status "published") "Published"))
        (select :id "publish-mode-select" :name "publish_mode"
          :class (str sel-cls (if (= status "published") "" " hidden")
                              (if already-emailed " opacity-50 pointer-events-none" ""))
          :disabled (if already-emailed true nil)
          (option :value "web" :selected true "Web only")
          (option :value "email" "Email only")
          (option :value "both" "Web + Email"))
        (select :id "newsletter-select" :name "newsletter_slug"
          :class (str sel-cls " hidden")
          :disabled (if already-emailed true nil)
          newsletter-options)
        (button :type "submit"
          :class "px-[20px] py-[6px] bg-stone-700 text-white text-[14px] rounded-[8px] hover:bg-stone-800 transition-colors cursor-pointer"
          "Save")
        (when footer-extra footer-extra)))))

;; Publish-mode show/hide script for edit form
(defcomp ~blog-editor-publish-js (&key already-emailed)
  (script
    "(function() {"
    "  var statusSel = document.getElementById('status-select');"
    "  var modeSel = document.getElementById('publish-mode-select');"
    "  var nlSel = document.getElementById('newsletter-select');"
    (str "  var alreadyEmailed = " (if already-emailed "true" "false") ";")
    "  function sync() {"
    "    var isPublished = statusSel.value === 'published';"
    "    if (isPublished && !alreadyEmailed) { modeSel.classList.remove('hidden'); } else { modeSel.classList.add('hidden'); }"
    "    var needsEmail = isPublished && !alreadyEmailed && (modeSel.value === 'email' || modeSel.value === 'both');"
    "    if (needsEmail) { nlSel.classList.remove('hidden'); } else { nlSel.classList.add('hidden'); }"
    "  }"
    "  statusSel.addEventListener('change', sync);"
    "  modeSel.addEventListener('change', sync);"
    "  sync();"
    "})();"))

(defcomp ~blog-editor-styles (&key css-href)
  (<> (link :rel "stylesheet" :href css-href)
    (style
      "#lexical-editor { display: flow-root; }"
      "#lexical-editor [data-kg-card=\"html\"] * { float: none !important; }"
      "#lexical-editor [data-kg-card=\"html\"] table { width: 100% !important; }")))

(defcomp ~blog-editor-scripts (&key js-src sx-editor-js-src init-js)
  (<> (script :src js-src)
      (when sx-editor-js-src (script :src sx-editor-js-src))
      (script init-js)))

;; SX editor styles — comprehensive CSS for the Koenig-style block editor
(defcomp ~sx-editor-styles ()
  (style
    ;; Editor container
    ".sx-editor { position: relative; font-size: 18px; line-height: 1.6; font-family: Georgia, 'Times New Roman', serif; color: #1c1917; }"
    ".sx-blocks-container { display: flex; flex-direction: column; min-height: 300px; cursor: text; padding-left: 48px; }"

    ;; Block base
    ".sx-block { position: relative; margin: 1px 0; }"
    ".sx-block-content { padding: 2px 0; }"
    ".sx-editable { outline: none; min-height: 1.6em; }"
    ".sx-editable:empty:before { content: attr(data-placeholder); color: #d6d3d1; pointer-events: none; }"

    ;; Text block styles
    ".sx-heading { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-weight: 700; }"
    ".sx-block[data-sx-tag='h2'] .sx-heading { font-size: 2em; line-height: 1.25; margin: 0.5em 0 0.25em; }"
    ".sx-block[data-sx-tag='h3'] .sx-heading { font-size: 1.5em; line-height: 1.3; margin: 0.4em 0 0.2em; }"
    ".sx-quote { border-left: 3px solid #d6d3d1; padding-left: 16px; font-style: italic; color: #57534e; }"

    ;; HR
    ".sx-block-hr { padding: 12px 0; }"
    ".sx-hr { border: none; border-top: 1px solid #e7e5e4; }"

    ;; Code blocks
    ".sx-block-code { background: #1c1917; border-radius: 6px; padding: 16px; margin: 8px 0; }"
    ".sx-code-header { margin-bottom: 8px; }"
    ".sx-code-lang { background: transparent; border: none; color: #a8a29e; font-size: 12px; outline: none; width: 120px; font-family: monospace; }"
    ".sx-code-textarea { width: 100%; background: transparent; border: none; color: #fafaf9; font-family: 'SF Mono', 'Fira Code', 'Fira Mono', monospace; font-size: 14px; line-height: 1.5; resize: none; outline: none; min-height: 60px; tab-size: 2; }"

    ;; List blocks
    ".sx-list-content { padding-left: 0; }"
    ".sx-list-item { padding: 2px 0 2px 24px; position: relative; outline: none; min-height: 1.6em; }"
    ".sx-list-item:empty:before { content: attr(data-placeholder); color: #d6d3d1; pointer-events: none; }"
    ".sx-list-item:before { content: '\\2022'; position: absolute; left: 6px; color: #78716c; }"
    ".sx-block[data-sx-tag='ol'] { counter-reset: sx-list; }"
    ".sx-block[data-sx-tag='ol'] .sx-list-item { counter-increment: sx-list; }"
    ".sx-block[data-sx-tag='ol'] .sx-list-item:before { content: counter(sx-list) '.'; font-size: 14px; }"

    ;; Card blocks
    ".sx-block-card { margin: 12px 0; border-radius: 4px; position: relative; transition: box-shadow 0.15s; }"
    ".sx-block-card:hover { box-shadow: 0 0 0 1px #d6d3d1; }"
    ".sx-block-card.sx-card-editing { box-shadow: 0 0 0 2px #3b82f6; }"
    ".sx-card-preview { cursor: pointer; }"
    ".sx-card-preview img { max-width: 100%; }"
    ".sx-card-fallback { padding: 24px; text-align: center; color: #a8a29e; font-style: italic; background: #fafaf9; border-radius: 4px; }"
    ".sx-card-caption { padding: 8px 0; font-size: 14px; color: #78716c; text-align: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-card-caption:empty:before { content: attr(data-placeholder); color: #d6d3d1; }"

    ;; Card toolbar
    ".sx-card-toolbar { position: absolute; top: -36px; right: 0; z-index: 10; display: none; gap: 4px; }"
    ".sx-block-card:hover .sx-card-toolbar { display: flex; }"
    ".sx-card-editing .sx-card-toolbar { display: flex; }"
    ".sx-card-tool-btn { width: 28px; height: 28px; border-radius: 4px; border: 1px solid #d6d3d1; background: white; color: #78716c; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 12px; transition: all 0.1s; }"
    ".sx-card-tool-btn:hover { background: #fafaf9; color: #dc2626; border-color: #dc2626; }"

    ;; Card edit panel
    ".sx-card-edit { padding: 16px; background: #fafaf9; border-radius: 4px; }"

    ;; Plus button (Koenig-style floating)
    ".sx-plus-container { position: absolute; z-index: 40; display: flex; align-items: center; }"
    ".sx-plus-btn { width: 28px; height: 28px; border-radius: 50%; border: 1px solid #d6d3d1; background: white; color: #a8a29e; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.15s; padding: 0; }"
    ".sx-plus-btn:hover { border-color: #1c1917; color: #1c1917; }"
    ".sx-plus-btn-open { transform: rotate(45deg); border-color: #1c1917; color: #1c1917; }"
    ".sx-plus-btn svg { width: 14px; height: 14px; }"

    ;; Plus menu
    ".sx-plus-menu { position: absolute; top: 36px; left: -8px; z-index: 50; background: white; border: 1px solid #e7e5e4; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); padding: 8px; width: 320px; max-height: 420px; overflow-y: auto; }"
    ".sx-plus-menu-section { margin-bottom: 4px; }"
    ".sx-plus-menu-heading { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #a8a29e; padding: 6px 8px 2px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-plus-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 6px 8px; border: none; background: none; cursor: pointer; font-size: 14px; color: #1c1917; border-radius: 4px; text-align: left; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-plus-menu-item:hover { background: #f5f5f4; }"
    ".sx-plus-menu-icon { width: 24px; text-align: center; font-size: 14px; color: #78716c; flex-shrink: 0; }"
    ".sx-plus-menu-label { font-weight: 500; white-space: nowrap; }"
    ".sx-plus-menu-desc { color: #a8a29e; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }"

    ;; Slash command menu
    ".sx-slash-menu { position: absolute; z-index: 50; background: white; border: 1px solid #e7e5e4; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); padding: 4px; width: 320px; max-height: 300px; overflow-y: auto; }"
    ".sx-slash-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border: none; background: none; cursor: pointer; font-size: 14px; color: #1c1917; border-radius: 4px; text-align: left; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-slash-menu-item:hover { background: #f5f5f4; }"
    ".sx-slash-menu-icon { width: 24px; text-align: center; font-size: 14px; color: #78716c; flex-shrink: 0; }"
    ".sx-slash-menu-label { font-weight: 500; white-space: nowrap; }"
    ".sx-slash-menu-desc { color: #a8a29e; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }"

    ;; Format toolbar
    ".sx-format-bar { position: absolute; z-index: 100; display: flex; gap: 1px; background: #1c1917; border-radius: 6px; padding: 2px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-format-btn { border: none; background: none; color: #e7e5e4; cursor: pointer; padding: 6px 10px; border-radius: 4px; font-size: 14px; line-height: 1; }"
    ".sx-format-btn:hover { background: #44403c; color: white; }"
    ".sx-fmt-bold { font-weight: 700; }"
    ".sx-fmt-italic { font-style: italic; }"

    ;; Card edit UI elements
    ".sx-edit-controls { display: flex; flex-direction: column; gap: 8px; }"
    ".sx-edit-row { display: flex; align-items: center; gap: 8px; }"
    ".sx-edit-label { font-size: 12px; font-weight: 600; color: #78716c; min-width: 80px; text-transform: uppercase; letter-spacing: 0.03em; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-edit-input { flex: 1; padding: 6px 10px; border: 1px solid #d6d3d1; border-radius: 4px; font-size: 14px; outline: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-edit-input:focus { border-color: #3b82f6; box-shadow: 0 0 0 2px rgba(59,130,246,0.15); }"
    ".sx-edit-select { padding: 6px 10px; border: 1px solid #d6d3d1; border-radius: 4px; font-size: 14px; outline: none; background: white; }"
    ".sx-edit-btn { padding: 8px 16px; border: 1px solid #d6d3d1; border-radius: 6px; font-size: 14px; cursor: pointer; background: white; color: #1c1917; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; transition: all 0.1s; }"
    ".sx-edit-btn:hover { background: #f5f5f4; border-color: #a8a29e; }"
    ".sx-edit-btn-sm { padding: 4px 12px; font-size: 12px; }"
    ".sx-edit-info { font-size: 12px; color: #a8a29e; }"
    ".sx-edit-status { font-size: 13px; color: #78716c; margin-top: 4px; }"
    ".sx-edit-url-input { font-family: monospace; }"
    ".sx-edit-url-display { font-size: 12px; color: #78716c; font-family: monospace; margin: 4px 0; word-break: break-all; }"
    ".sx-edit-checkbox-label { display: flex; align-items: center; gap: 6px; font-size: 14px; color: #44403c; cursor: pointer; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-edit-img-preview { max-width: 100%; max-height: 300px; object-fit: contain; border-radius: 4px; margin-bottom: 12px; }"
    ".sx-edit-embed-preview { margin-bottom: 8px; }"
    ".sx-edit-embed-preview iframe { max-width: 100%; }"
    ".sx-edit-audio-player { width: 100%; margin-bottom: 12px; }"
    ".sx-edit-video-player { width: 100%; max-height: 300px; margin-bottom: 12px; border-radius: 4px; }"
    ".sx-edit-html-textarea { width: 100%; min-height: 120px; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 13px; padding: 12px; border: 1px solid #d6d3d1; border-radius: 4px; resize: vertical; outline: none; background: #fafaf9; line-height: 1.5; tab-size: 2; }"
    ".sx-edit-html-textarea:focus { border-color: #3b82f6; }"
    ".sx-edit-callout-content { padding: 8px 12px; background: white; border: 1px solid #d6d3d1; border-radius: 4px; min-height: 40px; }"
    ".sx-edit-toggle-content { padding: 8px 12px; background: white; border: 1px solid #d6d3d1; border-radius: 4px; min-height: 40px; margin-top: 4px; }"
    ".sx-edit-emoji-input { width: 60px; text-align: center; font-size: 20px; padding: 4px; margin-bottom: 8px; }"

    ;; Color picker
    ".sx-edit-color-row { display: flex; gap: 6px; margin-bottom: 8px; }"
    ".sx-edit-color-swatch { width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; transition: all 0.1s; }"
    ".sx-edit-color-swatch:hover { transform: scale(1.15); }"
    ".sx-edit-color-swatch.active { border-color: #3b82f6; box-shadow: 0 0 0 2px rgba(59,130,246,0.3); }"

    ;; Gallery grid
    ".sx-edit-gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 12px; }"
    ".sx-edit-gallery-thumb { position: relative; aspect-ratio: 1; overflow: hidden; border-radius: 4px; }"
    ".sx-edit-gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }"
    ".sx-edit-gallery-remove { position: absolute; top: 4px; right: 4px; width: 20px; height: 20px; border-radius: 50%; background: rgba(0,0,0,0.6); color: white; border: none; cursor: pointer; font-size: 14px; line-height: 1; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.1s; }"
    ".sx-edit-gallery-thumb:hover .sx-edit-gallery-remove { opacity: 1; }"

    ;; Upload area
    ".sx-upload-area { padding: 40px 24px; border: 2px dashed #d6d3d1; border-radius: 8px; text-align: center; cursor: pointer; transition: all 0.15s; }"
    ".sx-upload-area:hover, .sx-upload-dragover { border-color: #3b82f6; background: rgba(59,130,246,0.03); }"
    ".sx-upload-icon { font-size: 32px; color: #a8a29e; margin-bottom: 8px; }"
    ".sx-upload-msg { font-size: 14px; color: #78716c; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }"
    ".sx-upload-progress { font-size: 13px; color: #3b82f6; margin-top: 8px; }"

    ;; Drag over editor
    ".sx-drag-over { outline: 2px dashed #3b82f6; outline-offset: -2px; border-radius: 4px; }"))

;; ---------------------------------------------------------------------------
;; Editor panel composition — replaces render_editor_panel (new post/page)
;; ---------------------------------------------------------------------------

(defcomp ~blog-editor-content (&key csrf title-placeholder create-label
                                     css-href js-src sx-editor-js-src init-js
                                     save-error)
  (~blog-editor-panel :parts
    (<>
      (when save-error (~blog-editor-error :error save-error))
      (~blog-editor-form :csrf csrf :title-placeholder title-placeholder
        :create-label create-label)
      (~blog-editor-styles :css-href css-href)
      (~sx-editor-styles)
      (~blog-editor-scripts :js-src js-src :sx-editor-js-src sx-editor-js-src
        :init-js init-js))))

;; ---------------------------------------------------------------------------
;; Edit content composition — replaces _h_post_edit_content (existing post)
;; ---------------------------------------------------------------------------

(defcomp ~blog-edit-content (&key csrf updated-at title-val excerpt-val
                                   feature-image feature-image-caption
                                   sx-content-val lexical-json has-sx
                                   title-placeholder status already-emailed
                                   newsletter-options footer-extra
                                   css-href js-src sx-editor-js-src init-js
                                   save-error)
  (~blog-editor-panel :parts
    (<>
      (when save-error (~blog-editor-error :error save-error))
      (~blog-editor-edit-form
        :csrf csrf :updated-at updated-at
        :title-val title-val :excerpt-val excerpt-val
        :feature-image feature-image :feature-image-caption feature-image-caption
        :sx-content-val sx-content-val :lexical-json lexical-json
        :has-sx has-sx :title-placeholder title-placeholder
        :status status :already-emailed already-emailed
        :newsletter-options newsletter-options :footer-extra footer-extra)
      (~blog-editor-publish-js :already-emailed already-emailed)
      (~blog-editor-styles :css-href css-href)
      (~sx-editor-styles)
      (~blog-editor-scripts :js-src js-src :sx-editor-js-src sx-editor-js-src
        :init-js init-js))))
