@jant/core 0.6.13 → 0.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/bin/commands/import-site.js +81 -13
  2. package/bin/commands/migrate.js +55 -0
  3. package/bin/commands/posts/rebuild-html.js +184 -0
  4. package/bin/commands/site/snapshot/import.js +14 -10
  5. package/bin/lib/d1-query.js +23 -5
  6. package/bin/lib/migration-runner.js +29 -7
  7. package/bin/lib/node-database.js +32 -0
  8. package/bin/lib/site-snapshot.js +347 -46
  9. package/bin/lib/sql-export.js +1 -1
  10. package/bin/lib/thread-collection-migration.js +326 -0
  11. package/dist/app-BxCOR3Uc.js +6 -0
  12. package/dist/{app-HnzmsaqU.js → app-C-oi_t8W.js} +7419 -6350
  13. package/dist/client/.vite/manifest.json +3 -3
  14. package/dist/client/_assets/client-B_eGKN5p.css +2 -0
  15. package/dist/client/_assets/{client-3_OaxhTs.js → client-DxY5BFe8.js} +58 -37
  16. package/dist/client/_assets/client-auth-CakPESv9.js +5533 -0
  17. package/dist/{export-Dfbq9aot.js → export-CsFx6F_M.js} +1273 -131
  18. package/dist/{github-sync--PSoE-Ne.js → github-sync-Cq1pzzOI.js} +1 -1
  19. package/dist/{github-sync-BGTLx8Mf.js → github-sync-ppWXN3jb.js} +2 -2
  20. package/dist/index.js +3 -3
  21. package/dist/node.js +5 -5
  22. package/package.json +11 -10
  23. package/src/__tests__/bin/posts-rebuild-html.test.ts +137 -0
  24. package/src/__tests__/dev-navigation-defaults.test.ts +22 -0
  25. package/src/__tests__/export-hugo-build.test.ts +63 -4
  26. package/src/__tests__/export-import-roundtrip.test.ts +6 -4
  27. package/src/__tests__/export-service.test.ts +142 -9
  28. package/src/__tests__/helpers/app.ts +3 -0
  29. package/src/__tests__/helpers/export-fixtures.ts +2 -0
  30. package/src/__tests__/import-site-command.test.ts +65 -0
  31. package/src/__tests__/vite-config.test.ts +45 -0
  32. package/src/app.tsx +16 -3
  33. package/src/client/__tests__/collection-created-notice.test.ts +36 -0
  34. package/src/client/__tests__/collection-form-bridge.test.ts +10 -1
  35. package/src/client/__tests__/collection-page-actions.test.ts +71 -2
  36. package/src/client/__tests__/compose-bridge.test.ts +64 -0
  37. package/src/client/__tests__/feed-video-player.test.ts +16 -1
  38. package/src/client/__tests__/footnote-rail.test.ts +472 -0
  39. package/src/client/__tests__/hydrate-partial.test.ts +3 -0
  40. package/src/client/__tests__/toast.test.ts +18 -1
  41. package/src/client/collection-created-notice.ts +66 -0
  42. package/src/client/collection-form-bridge.ts +17 -0
  43. package/src/client/collection-navigation.ts +44 -0
  44. package/src/client/collection-page-actions.ts +57 -1
  45. package/src/client/components/__tests__/jant-collection-directory.test.ts +155 -6
  46. package/src/client/components/__tests__/jant-command-palette.test.ts +137 -10
  47. package/src/client/components/__tests__/jant-compose-dialog.test.ts +373 -5
  48. package/src/client/components/__tests__/jant-compose-editor.test.ts +97 -0
  49. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +104 -0
  50. package/src/client/components/__tests__/jant-config-editor.test.ts +405 -0
  51. package/src/client/components/__tests__/jant-media-lightbox.test.ts +121 -0
  52. package/src/client/components/__tests__/jant-nav-manager.test.ts +538 -2
  53. package/src/client/components/__tests__/jant-post-form.test.ts +2 -2
  54. package/src/client/components/__tests__/jant-post-menu.test.ts +47 -5
  55. package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
  56. package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
  57. package/src/client/components/collection-manager-types.ts +9 -3
  58. package/src/client/components/compose-types.ts +8 -0
  59. package/src/client/components/jant-collection-directory.ts +165 -30
  60. package/src/client/components/jant-command-palette.ts +102 -71
  61. package/src/client/components/jant-compose-dialog.ts +86 -22
  62. package/src/client/components/jant-compose-editor.ts +13 -7
  63. package/src/client/components/jant-compose-fullscreen.ts +24 -4
  64. package/src/client/components/jant-config-editor.ts +722 -0
  65. package/src/client/components/jant-media-lightbox.ts +100 -18
  66. package/src/client/components/jant-nav-manager.ts +1283 -54
  67. package/src/client/components/jant-post-form.ts +3 -3
  68. package/src/client/components/jant-post-menu.ts +86 -74
  69. package/src/client/components/nav-manager-types.ts +51 -2
  70. package/src/client/components/post-form-types.ts +1 -1
  71. package/src/client/compose-bridge.ts +4 -1
  72. package/src/client/feed-video-player.ts +62 -2
  73. package/src/client/footnote-rail.ts +478 -0
  74. package/src/client/hydrate-partial.ts +2 -0
  75. package/src/client/media-lightbox-events.ts +63 -0
  76. package/src/client/site-header-fragment.ts +38 -0
  77. package/src/client/tiptap/__tests__/blockquote-editing.test.ts +201 -0
  78. package/src/client/tiptap/__tests__/clear-formatting.test.ts +168 -0
  79. package/src/client/tiptap/__tests__/footnotes.test.ts +284 -0
  80. package/src/client/tiptap/__tests__/list-editing.test.ts +447 -0
  81. package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +872 -2
  82. package/src/client/tiptap/__tests__/table-controls.test.ts +263 -0
  83. package/src/client/tiptap/__tests__/table-size-picker.test.ts +179 -0
  84. package/src/client/tiptap/bubble-menu.ts +91 -13
  85. package/src/client/tiptap/continuous-lists.ts +102 -0
  86. package/src/client/tiptap/create-editor.ts +3 -0
  87. package/src/client/tiptap/extensions.ts +14 -5
  88. package/src/client/tiptap/footnotes.ts +156 -0
  89. package/src/client/tiptap/markdown-clipboard.ts +614 -12
  90. package/src/client/tiptap/slash-commands.ts +8 -1
  91. package/src/client/tiptap/structural-keymap.ts +211 -0
  92. package/src/client/tiptap/tab-indent.ts +8 -22
  93. package/src/client/tiptap/table-control-labels.ts +30 -0
  94. package/src/client/tiptap/table-controls.ts +537 -0
  95. package/src/client/tiptap/table-size-picker.ts +254 -0
  96. package/src/client/toast.ts +8 -6
  97. package/src/client-auth.ts +1 -0
  98. package/src/client.ts +1 -0
  99. package/src/db/__tests__/d1-query.test.ts +43 -1
  100. package/src/db/__tests__/migration-runner.test.ts +63 -0
  101. package/src/db/__tests__/migrations.test.ts +469 -0
  102. package/src/db/__tests__/thread-collection-migration.test.ts +364 -0
  103. package/src/db/migrations/0027_old_blue_blade.sql +39 -0
  104. package/src/db/migrations/0028_superb_post.sql +2 -0
  105. package/src/db/migrations/0029_boring_magma.sql +1 -0
  106. package/src/db/migrations/0030_typical_vivisector.sql +50 -0
  107. package/src/db/migrations/meta/0027_snapshot.json +2511 -0
  108. package/src/db/migrations/meta/0028_snapshot.json +2511 -0
  109. package/src/db/migrations/meta/0029_snapshot.json +2519 -0
  110. package/src/db/migrations/meta/0030_snapshot.json +2541 -0
  111. package/src/db/migrations/meta/_journal.json +28 -0
  112. package/src/db/migrations/pg/0025_moaning_epoch.sql +39 -0
  113. package/src/db/migrations/pg/0026_talented_killer_shrike.sql +2 -0
  114. package/src/db/migrations/pg/0027_short_cannonball.sql +1 -0
  115. package/src/db/migrations/pg/0028_perfect_sharon_carter.sql +27 -0
  116. package/src/db/migrations/pg/meta/0025_snapshot.json +3204 -0
  117. package/src/db/migrations/pg/meta/0026_snapshot.json +3204 -0
  118. package/src/db/migrations/pg/meta/0027_snapshot.json +3211 -0
  119. package/src/db/migrations/pg/meta/0028_snapshot.json +3248 -0
  120. package/src/db/migrations/pg/meta/_journal.json +28 -0
  121. package/src/db/pg/schema.ts +28 -20
  122. package/src/db/rehearsal-fixtures/demo-current.json +39 -4
  123. package/src/db/rehearsal-fixtures/demo-current.sql +121 -6
  124. package/src/db/schema.ts +28 -20
  125. package/src/i18n/__tests__/detect.test.ts +8 -2
  126. package/src/i18n/detect.ts +14 -5
  127. package/src/i18n/locales/public/en.po +133 -30
  128. package/src/i18n/locales/public/en.ts +1 -1
  129. package/src/i18n/locales/public/zh-Hans.po +128 -25
  130. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  131. package/src/i18n/locales/public/zh-Hant.po +128 -25
  132. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  133. package/src/i18n/locales/settings/en.po +513 -10
  134. package/src/i18n/locales/settings/en.ts +1 -1
  135. package/src/i18n/locales/settings/zh-Hans.po +515 -12
  136. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  137. package/src/i18n/locales/settings/zh-Hant.po +515 -12
  138. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  139. package/src/lib/__tests__/api-settings.test.ts +299 -0
  140. package/src/lib/__tests__/constants.test.ts +5 -0
  141. package/src/lib/__tests__/feed-policy.test.ts +25 -0
  142. package/src/lib/__tests__/footnotes-upgrade.test.ts +181 -0
  143. package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +61 -0
  144. package/src/lib/__tests__/markdown-to-tiptap.test.ts +36 -4
  145. package/src/lib/__tests__/markdown.test.ts +8 -6
  146. package/src/lib/__tests__/navigation.test.ts +59 -2
  147. package/src/lib/__tests__/post-body-html.test.ts +176 -0
  148. package/src/lib/__tests__/post-display.test.ts +64 -0
  149. package/src/lib/__tests__/resolve-config.test.ts +113 -1
  150. package/src/lib/__tests__/summary.test.ts +82 -0
  151. package/src/lib/__tests__/timeline.test.ts +109 -70
  152. package/src/lib/__tests__/timezones.test.ts +31 -3
  153. package/src/lib/__tests__/tiptap-render.test.ts +268 -9
  154. package/src/lib/__tests__/tiptap-to-markdown.test.ts +6 -0
  155. package/src/lib/__tests__/view.test.ts +53 -0
  156. package/src/lib/__tests__/worker-response-cache.test.ts +18 -1
  157. package/src/lib/api-settings.ts +239 -12
  158. package/src/lib/collection-sort.ts +7 -5
  159. package/src/lib/constants.ts +2 -0
  160. package/src/lib/feed-path.ts +25 -0
  161. package/src/lib/feed-policy.ts +64 -0
  162. package/src/lib/footnotes.ts +264 -0
  163. package/src/lib/github-sync-site-config.ts +2 -0
  164. package/src/lib/hugo-markdown.ts +10 -3
  165. package/src/lib/jant-branding.ts +1 -0
  166. package/src/lib/markdown-manager.ts +202 -10
  167. package/src/lib/markdown.ts +10 -3
  168. package/src/lib/navigation.ts +16 -3
  169. package/src/lib/post-body-html.ts +131 -0
  170. package/src/lib/post-display.ts +7 -1
  171. package/src/lib/render.tsx +9 -2
  172. package/src/lib/resolve-config.ts +100 -16
  173. package/src/lib/schemas.ts +133 -1
  174. package/src/lib/settings-paths.ts +5 -0
  175. package/src/lib/site-skill.ts +78 -0
  176. package/src/lib/summary.ts +16 -5
  177. package/src/lib/timeline.ts +98 -122
  178. package/src/lib/timezones.ts +54 -14
  179. package/src/lib/tiptap-render.ts +355 -81
  180. package/src/lib/view.ts +30 -20
  181. package/src/lib/worker-response-cache.ts +11 -18
  182. package/src/middleware/__tests__/public-content-access.test.ts +91 -0
  183. package/src/middleware/config.ts +2 -2
  184. package/src/middleware/public-content-access.ts +87 -0
  185. package/src/node/__tests__/cli-export.test.ts +14 -0
  186. package/src/node/__tests__/cli-migrate.test.ts +6 -0
  187. package/src/node/__tests__/cli-site-snapshot.test.ts +205 -2
  188. package/src/node/__tests__/cli-snapshot-meta.test.ts +100 -0
  189. package/src/node/index.ts +2 -1
  190. package/src/preset.css +16 -2
  191. package/src/routes/__tests__/compose.test.ts +51 -0
  192. package/src/routes/__tests__/site-skill.test.ts +112 -0
  193. package/src/routes/api/__tests__/collections.test.ts +91 -19
  194. package/src/routes/api/__tests__/mcp.test.ts +65 -1
  195. package/src/routes/api/__tests__/nav-items.test.ts +91 -0
  196. package/src/routes/api/__tests__/public-access.test.ts +81 -0
  197. package/src/routes/api/__tests__/settings.test.ts +217 -0
  198. package/src/routes/api/collections.ts +22 -26
  199. package/src/routes/api/export.ts +2 -0
  200. package/src/routes/api/internal/__tests__/post-body-html.test.ts +163 -0
  201. package/src/routes/api/internal/__tests__/sites.test.ts +35 -2
  202. package/src/routes/api/internal/post-body-html.ts +43 -0
  203. package/src/routes/api/internal/sites.ts +24 -0
  204. package/src/routes/api/nav-items.ts +24 -1
  205. package/src/routes/api/posts.ts +2 -2
  206. package/src/routes/api/public/__tests__/archive.test.ts +35 -0
  207. package/src/routes/api/public/__tests__/posts.test.ts +208 -0
  208. package/src/routes/api/public/archive.ts +4 -0
  209. package/src/routes/api/public/posts.ts +41 -24
  210. package/src/routes/api/search.ts +3 -0
  211. package/src/routes/api/settings.ts +33 -0
  212. package/src/routes/auth/__tests__/setup.test.ts +7 -4
  213. package/src/routes/compose.tsx +2 -1
  214. package/src/routes/dash/settings.tsx +45 -2
  215. package/src/routes/feed/__tests__/feed.test.ts +210 -6
  216. package/src/routes/feed/feed.ts +23 -14
  217. package/src/routes/pages/__tests__/archive-params.test.ts +92 -3
  218. package/src/routes/pages/__tests__/collection-routing.test.ts +63 -5
  219. package/src/routes/pages/__tests__/collections.test.ts +9 -9
  220. package/src/routes/pages/__tests__/preview.test.ts +174 -0
  221. package/src/routes/pages/__tests__/search.test.ts +69 -0
  222. package/src/routes/pages/archive.tsx +27 -7
  223. package/src/routes/pages/collection.tsx +32 -19
  224. package/src/routes/pages/collections.tsx +6 -1
  225. package/src/routes/pages/page.tsx +83 -11
  226. package/src/routes/site-skill.ts +18 -0
  227. package/src/services/__tests__/auth.test.ts +7 -5
  228. package/src/services/__tests__/collection.test.ts +159 -44
  229. package/src/services/__tests__/navigation.test.ts +229 -21
  230. package/src/services/__tests__/post-timeline.test.ts +109 -30
  231. package/src/services/__tests__/post.test.ts +567 -5
  232. package/src/services/__tests__/search.test.ts +48 -1
  233. package/src/services/__tests__/settings.test.ts +8 -0
  234. package/src/services/__tests__/site-admin.test.ts +121 -1
  235. package/src/services/auth.ts +2 -2
  236. package/src/services/bootstrap.ts +3 -2
  237. package/src/services/collection.ts +226 -167
  238. package/src/services/export-theme/assets/client-site.css +1 -1
  239. package/src/services/export-theme/assets/client-site.js +46 -25
  240. package/src/services/export-theme/layouts/_default/list.html +18 -4
  241. package/src/services/export-theme/layouts/_default/rss.xml +8 -12
  242. package/src/services/export-theme/layouts/featured/list.html +5 -4
  243. package/src/services/export-theme/layouts/partials/featured-thread.html +56 -0
  244. package/src/services/export-theme/layouts/partials/footer.html +1 -1
  245. package/src/services/export-theme/layouts/partials/media-gallery.html +1 -0
  246. package/src/services/export-theme/styles/main.css +30 -2
  247. package/src/services/export.ts +91 -80
  248. package/src/services/mcp.ts +37 -27
  249. package/src/services/media.ts +1 -1
  250. package/src/services/navigation.ts +185 -46
  251. package/src/services/post.ts +874 -271
  252. package/src/services/search.ts +8 -2
  253. package/src/services/settings.ts +2 -10
  254. package/src/services/site-admin.ts +63 -43
  255. package/src/style-cjk-jp.css +1 -1
  256. package/src/style-cjk-kr.css +1 -1
  257. package/src/style-cjk-tc.css +1 -1
  258. package/src/style-cjk.css +1 -1
  259. package/src/styles/components.css +540 -0
  260. package/src/styles/site-media.css +111 -19
  261. package/src/styles/tokens.css +14 -9
  262. package/src/styles/ui.css +952 -64
  263. package/src/types/bindings.ts +3 -0
  264. package/src/types/config.ts +243 -12
  265. package/src/types/constants.ts +22 -1
  266. package/src/types/entities.ts +9 -8
  267. package/src/types/operations.ts +18 -10
  268. package/src/types/props.ts +8 -2
  269. package/src/types/raw-assets.d.ts +5 -0
  270. package/src/types/views.ts +16 -4
  271. package/src/ui/__tests__/font-themes.test.ts +42 -30
  272. package/src/ui/compose/ComposeDialog.tsx +92 -2
  273. package/src/ui/dash/appearance/NavigationContent.tsx +332 -4
  274. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +23 -0
  275. package/src/ui/dash/settings/ConfigEditorContent.tsx +578 -0
  276. package/src/ui/dash/settings/GeneralContent.tsx +15 -5
  277. package/src/ui/dash/settings/SettingsRootContent.tsx +21 -1
  278. package/src/ui/dash/settings/__tests__/ConfigEditorContent.test.tsx +98 -0
  279. package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +15 -0
  280. package/src/ui/feed/CuratedThreadPreview.tsx +16 -4
  281. package/src/ui/feed/LinkCard.tsx +2 -12
  282. package/src/ui/feed/NoteCard.tsx +10 -20
  283. package/src/ui/feed/QuoteCard.tsx +2 -12
  284. package/src/ui/feed/ThreadPreview.tsx +26 -34
  285. package/src/ui/feed/TimelineFeed.tsx +2 -2
  286. package/src/ui/feed/__tests__/thread-preview.test.ts +197 -56
  287. package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -6
  288. package/src/ui/feed/thread-preview-state.ts +5 -7
  289. package/src/ui/font-themes.ts +74 -26
  290. package/src/ui/layouts/BaseLayout.tsx +23 -9
  291. package/src/ui/layouts/SiteLayout.tsx +19 -13
  292. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +64 -1
  293. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +48 -0
  294. package/src/ui/pages/CollectionPage.tsx +101 -22
  295. package/src/ui/pages/CollectionsPage.tsx +3 -1
  296. package/src/ui/pages/PostPage.tsx +39 -12
  297. package/src/ui/pages/SearchPage.tsx +6 -14
  298. package/src/ui/pages/__tests__/ArchivePage.test.tsx +7 -0
  299. package/src/ui/pages/__tests__/PostPage.test.tsx +88 -0
  300. package/src/ui/shared/CollectionDirectory.tsx +13 -9
  301. package/src/ui/shared/CollectionsManager.tsx +17 -7
  302. package/src/ui/shared/DraftPreviewBar.tsx +33 -0
  303. package/src/ui/shared/HomePageBranding.tsx +2 -2
  304. package/src/ui/shared/MediaGallery.tsx +1 -0
  305. package/src/ui/shared/PostFooter.tsx +3 -1
  306. package/src/ui/shared/__tests__/home-page-branding.test.tsx +1 -1
  307. package/src/ui/shared/__tests__/media-gallery.test.ts +1 -0
  308. package/src/ui/shared/__tests__/navigation-labels.test.ts +2 -2
  309. package/src/ui/shared/__tests__/post-footer.test.ts +15 -0
  310. package/src/ui/shared/collection-management-labels.ts +33 -2
  311. package/src/ui/shared/navigation-labels.ts +2 -4
  312. package/src/ui/shared/post-article-attributes.ts +46 -0
  313. package/dist/app-CWJFPjuR.js +0 -6
  314. package/dist/client/_assets/client-DCrcHVJl.css +0 -2
  315. package/dist/client/_assets/client-auth-TsYq90xm.js +0 -4733
  316. package/src/client/tiptap/wrapping-input-rules.ts +0 -102
package/src/styles/ui.css CHANGED
@@ -30,6 +30,73 @@
30
30
  counter-reset: sidenote-counter;
31
31
  }
32
32
 
33
+ /* Authenticated utility chrome lives outside .site-page so the previewed
34
+ theme layout remains the same subtree visitors receive after publishing. */
35
+ .draft-preview-bar {
36
+ position: sticky;
37
+ top: 0;
38
+ z-index: 40;
39
+ width: 100%;
40
+ border-bottom: 1px solid
41
+ color-mix(in srgb, var(--site-divider) 78%, transparent);
42
+ background: color-mix(
43
+ in srgb,
44
+ var(--site-elevated-bg) 94%,
45
+ var(--site-nav-hover-bg)
46
+ );
47
+ color: var(--site-text-secondary);
48
+ font-family: var(--font-ui);
49
+ box-shadow: 0 1px 0
50
+ color-mix(in srgb, var(--site-elevated-bg) 70%, transparent);
51
+ backdrop-filter: blur(14px);
52
+ -webkit-backdrop-filter: blur(14px);
53
+ }
54
+
55
+ .draft-preview-bar-inner {
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ gap: 0.7rem;
60
+ min-height: 2.5rem;
61
+ max-width: var(--layout-body-max-width);
62
+ margin: 0 auto;
63
+ padding: 0.45rem var(--site-padding);
64
+ font-size: var(--type-ui-caption);
65
+ line-height: 1.35;
66
+ text-align: center;
67
+ }
68
+
69
+ .draft-preview-bar-label {
70
+ display: inline-flex;
71
+ align-items: center;
72
+ gap: 0.42rem;
73
+ flex: none;
74
+ color: var(--site-text-primary);
75
+ font-weight: var(--fw-semibold);
76
+ letter-spacing: var(--type-label-tracking);
77
+ text-transform: uppercase;
78
+ }
79
+
80
+ .draft-preview-bar-dot {
81
+ width: 0.42rem;
82
+ height: 0.42rem;
83
+ border-radius: 999px;
84
+ background: var(--site-accent);
85
+ box-shadow: 0 0 0 3px
86
+ color-mix(in srgb, var(--site-accent) 12%, transparent);
87
+ }
88
+
89
+ .draft-preview-bar-description {
90
+ color: var(--site-text-secondary);
91
+ }
92
+
93
+ @media (max-width: 480px) {
94
+ .draft-preview-bar-inner {
95
+ flex-direction: column;
96
+ gap: 0.18rem;
97
+ }
98
+ }
99
+
33
100
  /*
34
101
  * Tufte horizontal frame — single source of truth for left/right positioning.
35
102
  * Every page-level section gets the same asymmetric padding.
@@ -848,7 +915,7 @@
848
915
  display: flex;
849
916
  }
850
917
 
851
- .site-header-more-divider-responsive {
918
+ .site-header-more-divider-show-lg {
852
919
  display: block;
853
920
  }
854
921
  }
@@ -865,6 +932,10 @@
865
932
  .site-header-more-link-show-md {
866
933
  display: flex;
867
934
  }
935
+
936
+ .site-header-more-divider-show-md {
937
+ display: block;
938
+ }
868
939
  }
869
940
 
870
941
  @media (max-width: 580px) {
@@ -879,6 +950,10 @@
879
950
  .site-header-more-link-show-sm {
880
951
  display: flex;
881
952
  }
953
+
954
+ .site-header-more-divider-show-sm {
955
+ display: block;
956
+ }
882
957
  }
883
958
 
884
959
  @media (max-width: 480px) {
@@ -1119,6 +1194,148 @@
1119
1194
  align-items: center;
1120
1195
  }
1121
1196
 
1197
+ .collection-created-notice {
1198
+ display: flex;
1199
+ align-items: center;
1200
+ flex-wrap: wrap;
1201
+ gap: 0.4rem 0.7rem;
1202
+ width: fit-content;
1203
+ max-width: 100%;
1204
+ min-height: 2rem;
1205
+ margin-bottom: 0.7rem;
1206
+ padding: 0.35rem 0.45rem 0.35rem 0.65rem;
1207
+ border-radius: 0.55rem;
1208
+ background: color-mix(in srgb, var(--site-accent) 5%, var(--site-page-bg));
1209
+ box-shadow: inset 2px 0 0
1210
+ color-mix(in srgb, var(--site-accent) 30%, transparent);
1211
+ }
1212
+
1213
+ .collection-created-notice-message {
1214
+ margin: 0;
1215
+ color: color-mix(
1216
+ in srgb,
1217
+ var(--site-text-secondary) 82%,
1218
+ var(--site-text-primary)
1219
+ );
1220
+ font-family: var(--font-ui);
1221
+ font-size: var(--type-sm);
1222
+ line-height: 1.35;
1223
+ }
1224
+
1225
+ @keyframes collection-created-notice-enter {
1226
+ from {
1227
+ opacity: 0;
1228
+ transform: translateY(0.25rem);
1229
+ }
1230
+
1231
+ to {
1232
+ opacity: 1;
1233
+ transform: translateY(0);
1234
+ }
1235
+ }
1236
+
1237
+ @keyframes collection-created-notice-attention {
1238
+ from {
1239
+ background: color-mix(
1240
+ in srgb,
1241
+ var(--site-accent) 11%,
1242
+ var(--site-page-bg)
1243
+ );
1244
+ box-shadow: inset 2px 0 0
1245
+ color-mix(in srgb, var(--site-accent) 52%, transparent);
1246
+ }
1247
+
1248
+ to {
1249
+ background: color-mix(
1250
+ in srgb,
1251
+ var(--site-accent) 5%,
1252
+ var(--site-page-bg)
1253
+ );
1254
+ box-shadow: inset 2px 0 0
1255
+ color-mix(in srgb, var(--site-accent) 30%, transparent);
1256
+ }
1257
+ }
1258
+
1259
+ @media (prefers-reduced-motion: no-preference) {
1260
+ .collection-created-notice {
1261
+ animation:
1262
+ collection-created-notice-enter 360ms cubic-bezier(0.22, 1, 0.36, 1)
1263
+ both,
1264
+ collection-created-notice-attention 1.8s 120ms ease-out both;
1265
+ }
1266
+ }
1267
+
1268
+ .collection-created-notice-actions {
1269
+ display: flex;
1270
+ align-items: center;
1271
+ flex: 0 0 auto;
1272
+ gap: 0.35rem;
1273
+ }
1274
+
1275
+ .collection-created-notice-action {
1276
+ appearance: none;
1277
+ display: inline-flex;
1278
+ align-items: center;
1279
+ min-height: 2rem;
1280
+ padding: 0.2rem 0;
1281
+ border: 0;
1282
+ background: transparent;
1283
+ font-family: var(--font-ui);
1284
+ font-size: var(--type-sm);
1285
+ line-height: 1.35;
1286
+ text-decoration: none;
1287
+ text-underline-offset: 0.22em;
1288
+ cursor: pointer;
1289
+ }
1290
+
1291
+ .collection-created-notice-action-primary {
1292
+ color: var(--site-text-primary);
1293
+ font-weight: var(--fw-medium);
1294
+ }
1295
+
1296
+ .collection-created-notice-action-primary:hover {
1297
+ color: var(--site-text-primary);
1298
+ text-decoration: underline;
1299
+ }
1300
+
1301
+ .collection-created-notice-action:focus-visible {
1302
+ outline: 2px solid color-mix(in srgb, var(--site-accent) 28%, transparent);
1303
+ outline-offset: 3px;
1304
+ border-radius: var(--radius-sm, 0.25rem);
1305
+ }
1306
+
1307
+ .collection-created-notice-action:disabled {
1308
+ opacity: 0.5;
1309
+ cursor: wait;
1310
+ }
1311
+
1312
+ .collection-created-notice-dismiss {
1313
+ appearance: none;
1314
+ display: inline-flex;
1315
+ align-items: center;
1316
+ justify-content: center;
1317
+ width: 1.75rem;
1318
+ height: 1.75rem;
1319
+ padding: 0;
1320
+ border: 0;
1321
+ border-radius: 999px;
1322
+ background: transparent;
1323
+ color: var(--site-text-secondary);
1324
+ opacity: 0.65;
1325
+ cursor: pointer;
1326
+ }
1327
+
1328
+ .collection-created-notice-dismiss:hover {
1329
+ background: color-mix(in srgb, var(--site-accent) 6%, transparent);
1330
+ color: var(--site-text-primary);
1331
+ opacity: 1;
1332
+ }
1333
+
1334
+ .collection-created-notice-dismiss:focus-visible {
1335
+ outline: 2px solid color-mix(in srgb, var(--site-accent) 28%, transparent);
1336
+ outline-offset: 2px;
1337
+ }
1338
+
1122
1339
  .collections-page-actions {
1123
1340
  display: flex;
1124
1341
  align-items: center;
@@ -1432,6 +1649,15 @@
1432
1649
  position: absolute;
1433
1650
  top: 0.7rem;
1434
1651
  right: 0;
1652
+ opacity: 0;
1653
+ pointer-events: none;
1654
+ }
1655
+
1656
+ .collection-directory-managed-row:hover .collection-directory-item-menu,
1657
+ .collection-directory-managed-row:focus-within
1658
+ .collection-directory-item-menu {
1659
+ opacity: 1;
1660
+ pointer-events: auto;
1435
1661
  }
1436
1662
 
1437
1663
  .collections-link-create,
@@ -2072,6 +2298,13 @@
2072
2298
  }
2073
2299
  }
2074
2300
 
2301
+ @media (hover: none) {
2302
+ .collection-directory-item-menu {
2303
+ opacity: 1;
2304
+ pointer-events: auto;
2305
+ }
2306
+ }
2307
+
2075
2308
  /* =========================================================================
2076
2309
  * Timeline & Post Components
2077
2310
  * ========================================================================= */
@@ -2277,7 +2510,7 @@
2277
2510
  .post-header-meta-link {
2278
2511
  color: var(--site-text-secondary);
2279
2512
  font-family: var(--font-ui);
2280
- font-size: var(--type-ui-meta);
2513
+ font-size: var(--type-ui-hint);
2281
2514
  text-decoration: none;
2282
2515
  white-space: nowrap;
2283
2516
  }
@@ -2324,19 +2557,13 @@
2324
2557
  [data-page="post"] .feed-quote-attribution,
2325
2558
  [data-page="post"] .post-header-meta-row,
2326
2559
  [data-page="post"] .post-header-meta-link,
2327
- [data-page="post"] .post-header-meta-link time,
2328
- [data-page="post"] [data-post-meta],
2329
- [data-page="post"] [data-post-meta] .post-footer-meta,
2330
- [data-page="post"] [data-post-meta] a,
2331
- [data-page="post"] [data-post-meta] time {
2560
+ [data-page="post"] .post-header-meta-link time {
2332
2561
  color: var(--site-reading-meta);
2333
2562
  }
2334
2563
 
2335
2564
  [data-page="post"] .feed-link-domain:hover,
2336
2565
  [data-page="post"] .feed-quote-source:hover,
2337
- [data-page="post"] .post-header-meta-link:hover,
2338
- [data-page="post"] [data-post-meta] a:hover,
2339
- [data-page="post"] [data-post-meta] .post-footer-external-link:hover {
2566
+ [data-page="post"] .post-header-meta-link:hover {
2340
2567
  color: var(--site-reading-body);
2341
2568
  }
2342
2569
 
@@ -2348,9 +2575,128 @@
2348
2575
  );
2349
2576
  }
2350
2577
 
2351
- /* --- Sidenotes (Tufte-style margin notes) ------------------------------ */
2578
+ /* --- Semantic footnotes with Tufte presentation ------------------------ */
2579
+
2580
+ /* The published DOM stays semantic and readable without CSS: body blocks
2581
+ come first, followed by doc-endnotes containing one native ordered list.
2582
+ Detail and timeline posts use their real container width to opt into the
2583
+ Tufte rail; narrow containers, print, feeds, and readers retain endnote
2584
+ order. */
2585
+ [data-post-view],
2586
+ [data-timeline-item] {
2587
+ container: post-view / inline-size;
2588
+ }
2589
+
2590
+ .footnote-endnotes {
2591
+ margin-block: 1.5rem 0;
2592
+ padding-block-start: 0.85rem;
2593
+ border-block-start: 1px solid var(--site-divider);
2594
+ }
2595
+
2596
+ .footnote-list {
2597
+ margin-block: 0;
2598
+ padding-inline-start: 1.5rem;
2599
+ }
2600
+
2601
+ .footnote {
2602
+ scroll-margin-block-start: 2rem;
2603
+ padding-inline-start: 0.15rem;
2604
+ overflow-wrap: anywhere;
2605
+ font-family: var(--font-body);
2606
+ font-size: var(--type-sm);
2607
+ font-style: normal;
2608
+ font-weight: 400;
2609
+ font-variant: normal;
2610
+ line-height: 1.3;
2611
+ letter-spacing: var(--type-body-tracking, normal);
2612
+ text-align: left;
2613
+ color: var(--site-footnote-text);
2614
+ }
2615
+
2616
+ .footnote::marker {
2617
+ color: inherit;
2618
+ font-size: var(--type-xs);
2619
+ font-variant-numeric: tabular-nums;
2620
+ }
2621
+
2622
+ .footnote:target {
2623
+ border-radius: var(--radius-sm, 0.25rem);
2624
+ background: color-mix(in srgb, var(--site-reading-meta) 8%, transparent);
2625
+ }
2626
+
2627
+ .footnote > :first-child {
2628
+ margin-block-start: 0;
2629
+ }
2630
+
2631
+ .footnote > :last-child {
2632
+ margin-block-end: 0;
2633
+ }
2634
+
2635
+ .footnote code {
2636
+ font-size: var(--type-code);
2637
+ }
2638
+
2639
+ .footnote-backlinks {
2640
+ display: inline-flex;
2641
+ flex-wrap: wrap;
2642
+ align-items: baseline;
2643
+ gap: 0.35rem;
2644
+ font-size: var(--type-xs);
2645
+ }
2646
+
2647
+ span.footnote-backlinks {
2648
+ margin-inline-start: 0.35rem;
2649
+ vertical-align: baseline;
2650
+ }
2651
+
2652
+ p.footnote-backlinks {
2653
+ display: flex;
2654
+ margin-block: 0.45rem 0;
2655
+ }
2656
+
2657
+ .footnote + .footnote {
2658
+ margin-block-start: 0.75rem;
2659
+ }
2660
+
2661
+ .footnote-backref,
2662
+ .footnote-ref > a {
2663
+ color: inherit;
2664
+ text-decoration: none;
2665
+ }
2666
+
2667
+ .footnote-backref:hover,
2668
+ .footnote-ref > a:hover {
2669
+ text-decoration: underline;
2670
+ }
2671
+
2672
+ .footnote-backref:focus-visible,
2673
+ .footnote-ref > a:focus-visible {
2674
+ border-radius: var(--radius-sm, 0.25rem);
2675
+ outline: 1px solid currentColor;
2676
+ outline-offset: 2px;
2677
+ }
2352
2678
 
2353
- .sidenote,
2679
+ .footnote-ref {
2680
+ --site-prose-link-color: var(--site-footnote-marker);
2681
+ --site-prose-link-hover: var(--site-footnote-marker);
2682
+ --site-prose-link-underline: transparent;
2683
+
2684
+ margin-inline: 0.25rem;
2685
+ vertical-align: super;
2686
+ line-height: 0;
2687
+ font-size: var(--type-footnote-ref);
2688
+ font-variant-numeric: tabular-nums;
2689
+ white-space: nowrap;
2690
+ color: var(--site-footnote-marker);
2691
+ }
2692
+
2693
+ /* --- Legacy v1 sidenotes ----------------------------------------------- */
2694
+
2695
+ /* Kept for one compatibility window so imported snapshots or cached HTML
2696
+ from an older runtime remain readable during rollout. Product reads render
2697
+ stale post projections as v3 before the database rebuild runs. */
2698
+
2699
+ .sidenote:not([role="doc-footnote"]),
2354
2700
  .marginnote {
2355
2701
  /* Layout: float into the right margin. A floated note anchors to its containing
2356
2702
  block's right *content* edge, so if a theme gives that block inline-end padding
@@ -2393,7 +2739,7 @@
2393
2739
  }
2394
2740
 
2395
2741
  .sidenote-number::after,
2396
- .sidenote::before {
2742
+ .sidenote:not([role="doc-footnote"])::before {
2397
2743
  position: relative;
2398
2744
  vertical-align: baseline;
2399
2745
  }
@@ -2409,13 +2755,13 @@
2409
2755
  margin-right: 0.25rem;
2410
2756
  }
2411
2757
 
2412
- .sidenote::before {
2758
+ .sidenote:not([role="doc-footnote"])::before {
2413
2759
  content: counter(sidenote-counter) " ";
2414
2760
  font-size: var(--type-xs);
2415
2761
  top: -0.5rem;
2416
2762
  }
2417
2763
 
2418
- .sidenote > code,
2764
+ .sidenote:not([role="doc-footnote"]) > code,
2419
2765
  .marginnote > code {
2420
2766
  font-size: var(--type-code);
2421
2767
  }
@@ -2444,12 +2790,12 @@
2444
2790
  cursor: pointer;
2445
2791
  }
2446
2792
 
2447
- .sidenote,
2793
+ .sidenote:not([role="doc-footnote"]),
2448
2794
  .marginnote {
2449
2795
  display: none;
2450
2796
  }
2451
2797
 
2452
- .margin-toggle:checked + .sidenote,
2798
+ .margin-toggle:checked + .sidenote:not([role="doc-footnote"]),
2453
2799
  .margin-toggle:checked + .marginnote {
2454
2800
  display: block;
2455
2801
  float: left;
@@ -2997,6 +3343,10 @@
2997
3343
  line-height: 1.3;
2998
3344
  }
2999
3345
 
3346
+ .feed-quote-attribution:has(+ .post-menu-footer) {
3347
+ margin-bottom: 0.45rem;
3348
+ }
3349
+
3000
3350
  .feed-quote-attribution::before {
3001
3351
  content: "";
3002
3352
  width: 0.9rem;
@@ -3175,10 +3525,17 @@
3175
3525
 
3176
3526
  .post-footer-detail {
3177
3527
  margin-top: 24px;
3178
- font-size: var(--type-ui-meta);
3528
+ font-size: var(--type-ui-hint);
3179
3529
  color: var(--site-text-secondary);
3180
3530
  }
3181
3531
 
3532
+ /* A standalone detail footer needs extra breathing room to close the
3533
+ reading surface. Thread entries keep a smaller content gap so their
3534
+ metadata stays connected without crowding the post body. */
3535
+ .thread-group-detail .post-footer-detail {
3536
+ margin-top: var(--content-gap);
3537
+ }
3538
+
3182
3539
  .post-footer-meta {
3183
3540
  display: flex;
3184
3541
  flex: 1 1 auto;
@@ -5425,15 +5782,43 @@
5425
5782
  color: var(--site-text-primary);
5426
5783
  }
5427
5784
 
5428
- .compose-attached-tiptap {
5429
- flex: 1;
5430
- min-height: 0;
5431
- overflow-y: auto;
5432
- cursor: text;
5785
+ .compose-attached-done {
5786
+ margin-left: auto;
5787
+ padding: 6px 14px;
5788
+ border: none;
5789
+ border-radius: 8px;
5790
+ background-color: var(--site-text-primary);
5791
+ color: var(--site-elevated-bg);
5792
+ font-size: var(--type-ui-control);
5793
+ font-weight: var(--fw-medium);
5794
+ line-height: 1.25;
5795
+ cursor: pointer;
5796
+ transition: opacity 0.15s ease;
5433
5797
  }
5434
5798
 
5435
- .compose-attached-tiptap .tiptap {
5436
- min-height: 100%;
5799
+ .compose-attached-done:hover {
5800
+ opacity: 0.82;
5801
+ }
5802
+
5803
+ .compose-attached-done:focus-visible {
5804
+ outline: 2px solid
5805
+ color-mix(in srgb, var(--site-text-primary) 24%, transparent);
5806
+ outline-offset: 2px;
5807
+ }
5808
+
5809
+ .compose-attached-tiptap {
5810
+ flex: 1;
5811
+ min-height: 0;
5812
+ overflow-y: auto;
5813
+ cursor: text;
5814
+ }
5815
+
5816
+ .compose-attached-tiptap .tiptap {
5817
+ min-height: 100%;
5818
+ }
5819
+
5820
+ .compose-attached-tiptap .tiptap > :first-child {
5821
+ margin-top: 0;
5437
5822
  }
5438
5823
 
5439
5824
  .compose-attached-tiptap::-webkit-scrollbar {
@@ -5585,6 +5970,10 @@
5585
5970
  z-index: 6;
5586
5971
  }
5587
5972
 
5973
+ .compose-action-row-without-collection {
5974
+ justify-content: flex-end;
5975
+ }
5976
+
5588
5977
  @media (min-width: 700px) {
5589
5978
  .compose-action-row {
5590
5979
  padding: 12px 18px 16px;
@@ -6676,6 +7065,7 @@
6676
7065
  text-align: left;
6677
7066
  font-size: var(--compose-panel-control-size);
6678
7067
  color: var(--site-text-primary);
7068
+ text-decoration: none;
6679
7069
  transition: background-color 0.12s ease;
6680
7070
  }
6681
7071
 
@@ -6683,6 +7073,12 @@
6683
7073
  background-color: var(--compose-control-bg);
6684
7074
  }
6685
7075
 
7076
+ .compose-dropdown-separator {
7077
+ height: 1px;
7078
+ margin: 4px 8px;
7079
+ background-color: var(--compose-control-border);
7080
+ }
7081
+
6686
7082
  .compose-slug-suggestion {
6687
7083
  display: grid;
6688
7084
  grid-template-columns: minmax(0, 1fr) auto;
@@ -7735,28 +8131,9 @@
7735
8131
 
7736
8132
  /*
7737
8133
  * Reply context clones feed/detail card internals into a much narrower shell.
7738
- * Reset the reading widths and heading scale so the preview uses the thread
7739
- * context ladder instead of the original page/card layout.
8134
+ * Scale headings to the thread context ladder instead of the original
8135
+ * page/card layout.
7740
8136
  */
7741
- .compose-reply-context-body
7742
- :is(
7743
- .post-detail-title,
7744
- .post-detail-body,
7745
- .post-body-summary,
7746
- .feed-note-title,
7747
- .feed-link-title,
7748
- .feed-quote,
7749
- .feed-quote-attribution,
7750
- .feed-quote-commentary,
7751
- .feed-link-domain,
7752
- .feed-continue-link,
7753
- [data-post-body].prose,
7754
- [data-post-meta]
7755
- ) {
7756
- width: 100%;
7757
- max-width: none;
7758
- }
7759
-
7760
8137
  .compose-reply-context-body
7761
8138
  :is(.post-detail-title, .feed-note-title, .feed-link-title),
7762
8139
  .compose-reply-context-body .prose :is(h1, h2, h3, h4) {
@@ -7976,11 +8353,11 @@
7976
8353
  }
7977
8354
 
7978
8355
  .compose-thread-layout {
7979
- padding-top: 0.45rem;
8356
+ padding-top: 0.75rem;
7980
8357
  }
7981
8358
 
7982
8359
  .compose-thread-layout::before {
7983
- top: 0.45rem;
8360
+ top: 0.75rem;
7984
8361
  }
7985
8362
 
7986
8363
  .compose-reply-context {
@@ -8630,9 +9007,13 @@
8630
9007
  }
8631
9008
  }
8632
9009
 
9010
+ .command-palette-results-container {
9011
+ display: flex;
9012
+ flex-direction: column;
9013
+ min-height: 0;
9014
+ }
9015
+
8633
9016
  .command-palette-results {
8634
- list-style: none;
8635
- margin: 0;
8636
9017
  padding: 0.25rem 0;
8637
9018
  max-height: 20rem;
8638
9019
  overflow-y: auto;
@@ -8674,6 +9055,7 @@
8674
9055
  .command-palette-result-body {
8675
9056
  display: flex;
8676
9057
  flex-direction: column;
9058
+ flex: 1;
8677
9059
  gap: 0.125rem;
8678
9060
  min-width: 0;
8679
9061
  }
@@ -8694,6 +9076,37 @@
8694
9076
  text-overflow: ellipsis;
8695
9077
  }
8696
9078
 
9079
+ .command-palette-search-action {
9080
+ flex-shrink: 0;
9081
+ border-top: 1px solid var(--color-border);
9082
+ padding-block: 0.625rem;
9083
+ background-color: color-mix(
9084
+ in srgb,
9085
+ var(--color-muted) 32%,
9086
+ var(--compose-paper-bg)
9087
+ );
9088
+ }
9089
+
9090
+ .command-palette-search-action:hover,
9091
+ .command-palette-search-action.command-palette-result-selected {
9092
+ background-color: var(--color-accent);
9093
+ }
9094
+
9095
+ .command-palette-result-shortcut {
9096
+ flex-shrink: 0;
9097
+ margin-left: auto;
9098
+ border: 1px solid var(--color-border);
9099
+ border-radius: 0.25rem;
9100
+ padding: 0.125rem 0.375rem;
9101
+ background-color: var(--compose-paper-bg);
9102
+ color: var(--color-muted-foreground);
9103
+ font-family: inherit;
9104
+ font-size: 0.6875rem;
9105
+ font-weight: 500;
9106
+ line-height: 1.25rem;
9107
+ white-space: nowrap;
9108
+ }
9109
+
8697
9110
  .command-palette-empty {
8698
9111
  padding: 1.5rem 1rem;
8699
9112
  text-align: center;
@@ -8883,6 +9296,9 @@
8883
9296
  .compose-tiptap-body .tiptap {
8884
9297
  outline: none;
8885
9298
  min-height: 120px;
9299
+ min-width: 0;
9300
+ max-width: 100%;
9301
+ overflow-x: auto;
8886
9302
  font-size: var(--type-content-body);
8887
9303
  line-height: var(--type-body-leading);
8888
9304
  letter-spacing: var(--type-body-tracking);
@@ -8974,26 +9390,66 @@
8974
9390
 
8975
9391
  .compose-tiptap-body .tiptap ul {
8976
9392
  list-style-type: disc;
8977
- padding-left: 1.5em;
8978
- margin: 0.25em 0;
9393
+ padding-left: 1.4em;
9394
+ margin: 1.25em 0;
8979
9395
  }
8980
9396
 
8981
9397
  .compose-tiptap-body .tiptap ol {
8982
9398
  list-style-type: decimal;
9399
+ /* Keep multi-digit markers inside the editor's scrollable bounds. */
9400
+ padding-left: 2.25em;
9401
+ margin: 1.25em 0;
9402
+ }
9403
+
9404
+ .compose-tiptap-body .tiptap :is(ul, ol) :is(ul, ol) {
8983
9405
  padding-left: 1.5em;
8984
- margin: 0.25em 0;
9406
+ margin: 0.4em 0;
9407
+ }
9408
+
9409
+ .compose-tiptap-body .tiptap ul ul {
9410
+ list-style-type: circle;
9411
+ }
9412
+
9413
+ .compose-tiptap-body .tiptap ul ul ul {
9414
+ list-style-type: square;
9415
+ }
9416
+
9417
+ .compose-tiptap-body .tiptap ol ol {
9418
+ list-style-type: lower-alpha;
9419
+ }
9420
+
9421
+ .compose-tiptap-body .tiptap ol ol ol {
9422
+ list-style-type: lower-roman;
8985
9423
  }
8986
9424
 
8987
9425
  .compose-tiptap-body .tiptap li {
8988
- margin: 0.15em 0;
9426
+ margin: 0.5em 0;
9427
+ }
9428
+
9429
+ .compose-tiptap-body .tiptap li > p:first-child {
9430
+ margin-top: 0;
9431
+ }
9432
+
9433
+ .compose-tiptap-body .tiptap li > p:last-child {
9434
+ margin-bottom: 0;
9435
+ }
9436
+
9437
+ /* A nested list makes its leading paragraph no longer the last child, so
9438
+ clear the body-paragraph margin that would otherwise separate the levels. */
9439
+ .compose-tiptap-body .tiptap li > p:has(+ :is(ul, ol)),
9440
+ .compose-tiptap-body
9441
+ .tiptap
9442
+ li
9443
+ > p:has(+ .ProseMirror-gapcursor + :is(ul, ol)) {
9444
+ margin-bottom: 0;
8989
9445
  }
8990
9446
 
8991
9447
  .compose-tiptap-body .tiptap blockquote {
8992
9448
  --_blockquote-bg: var(--compose-blockquote-bg);
8993
9449
  --_blockquote-text: var(--compose-blockquote-text);
8994
9450
  position: relative;
8995
- margin: 0.72em 0;
8996
- padding: 1.3rem 0.9em 0.76em 1em;
9451
+ margin: 1.4rem 0;
9452
+ padding: 1.4rem 1rem 0.75rem;
8997
9453
  border: none;
8998
9454
  border-radius: 6px;
8999
9455
  background: var(--_blockquote-bg);
@@ -9008,6 +9464,35 @@
9008
9464
  color 0.18s ease;
9009
9465
  }
9010
9466
 
9467
+ /* Reply compose deliberately stays denser than ordinary authored prose. */
9468
+ .compose-reply-compose-layout .compose-tiptap-body .tiptap ul {
9469
+ padding-left: 1.5em;
9470
+ margin: 0.25em 0;
9471
+ }
9472
+
9473
+ .compose-reply-compose-layout .compose-tiptap-body .tiptap ol {
9474
+ padding-left: 2.25em;
9475
+ margin: 0.25em 0;
9476
+ }
9477
+
9478
+ .compose-reply-compose-layout
9479
+ .compose-tiptap-body
9480
+ .tiptap
9481
+ :is(ul, ol)
9482
+ :is(ul, ol) {
9483
+ padding-left: 1.5em;
9484
+ margin: 0.2em 0;
9485
+ }
9486
+
9487
+ .compose-reply-compose-layout .compose-tiptap-body .tiptap li {
9488
+ margin: 0.15em 0;
9489
+ }
9490
+
9491
+ .compose-reply-compose-layout .compose-tiptap-body .tiptap blockquote {
9492
+ margin: 0.72em 0;
9493
+ padding: 1.3rem 0.9em 0.76em 1em;
9494
+ }
9495
+
9011
9496
  /* CJK: no italic for blockquotes (no true italic glyph) */
9012
9497
  :lang(zh) .compose-tiptap-body .tiptap blockquote,
9013
9498
  :lang(ja) .compose-tiptap-body .tiptap blockquote,
@@ -9088,9 +9573,9 @@
9088
9573
 
9089
9574
  .compose-tiptap-body .tiptap table {
9090
9575
  border-collapse: collapse;
9091
- width: 100%;
9576
+ width: max-content;
9577
+ min-width: 100%;
9092
9578
  margin: 0.75em 0;
9093
- overflow: hidden;
9094
9579
  }
9095
9580
 
9096
9581
  .compose-tiptap-body .tiptap td,
@@ -9098,7 +9583,7 @@
9098
9583
  border: 1px solid var(--site-divider);
9099
9584
  padding: 0.4em 0.6em;
9100
9585
  vertical-align: top;
9101
- min-width: 1em;
9586
+ min-width: 6rem;
9102
9587
  position: relative;
9103
9588
  }
9104
9589
 
@@ -9111,6 +9596,211 @@
9111
9596
  background: oklch(from var(--site-accent) l c h / 0.12);
9112
9597
  }
9113
9598
 
9599
+ /* Contextual table toolbar — deliberately quieter than the text bubble menu. */
9600
+ .tiptap-table-controls {
9601
+ position: fixed;
9602
+ z-index: 10001;
9603
+ align-items: center;
9604
+ gap: 2px;
9605
+ padding: 3px;
9606
+ border: 1px solid color-mix(in srgb, var(--site-divider) 58%, transparent);
9607
+ border-radius: 11px;
9608
+ background: color-mix(
9609
+ in srgb,
9610
+ var(--site-elevated-bg) 96%,
9611
+ var(--site-nav-hover-bg) 4%
9612
+ );
9613
+ box-shadow:
9614
+ 0 10px 24px oklch(from var(--site-text-primary) l c h / 0.07),
9615
+ 0 2px 5px oklch(from var(--site-text-primary) l c h / 0.035);
9616
+ }
9617
+
9618
+ .tiptap-table-control-btn {
9619
+ display: inline-flex;
9620
+ align-items: center;
9621
+ justify-content: center;
9622
+ width: 2rem;
9623
+ height: 2rem;
9624
+ border-radius: 8px;
9625
+ color: var(--site-text-secondary);
9626
+ cursor: pointer;
9627
+ transition:
9628
+ color 0.15s ease,
9629
+ background-color 0.15s ease;
9630
+ }
9631
+
9632
+ .tiptap-table-control-btn:hover,
9633
+ .tiptap-table-control-btn:focus-visible,
9634
+ .tiptap-table-control-btn[aria-expanded="true"] {
9635
+ color: var(--site-text-primary);
9636
+ background: color-mix(
9637
+ in srgb,
9638
+ var(--site-nav-hover-bg) 78%,
9639
+ var(--site-elevated-bg) 22%
9640
+ );
9641
+ }
9642
+
9643
+ .tiptap-table-control-btn:focus-visible,
9644
+ .tiptap-table-menu-item:focus-visible,
9645
+ .tiptap-table-size-cell:focus-visible {
9646
+ outline: 2px solid color-mix(in srgb, var(--site-accent) 72%, transparent);
9647
+ outline-offset: 1px;
9648
+ }
9649
+
9650
+ .tiptap-table-control-btn:disabled,
9651
+ .tiptap-table-menu-item:disabled {
9652
+ opacity: 0.35;
9653
+ cursor: not-allowed;
9654
+ }
9655
+
9656
+ .tiptap-table-options {
9657
+ position: absolute;
9658
+ top: calc(100% + 6px);
9659
+ left: 50%;
9660
+ width: min(13.5rem, calc(100vw - 20px));
9661
+ padding: 5px;
9662
+ border: 1px solid color-mix(in srgb, var(--site-divider) 58%, transparent);
9663
+ border-radius: 12px;
9664
+ background: var(--site-elevated-bg);
9665
+ box-shadow:
9666
+ 0 14px 28px oklch(from var(--site-text-primary) l c h / 0.08),
9667
+ 0 2px 6px oklch(from var(--site-text-primary) l c h / 0.04);
9668
+ transform: translateX(-50%);
9669
+ }
9670
+
9671
+ .tiptap-table-options[hidden] {
9672
+ display: none;
9673
+ }
9674
+
9675
+ .tiptap-table-menu-item {
9676
+ display: flex;
9677
+ align-items: center;
9678
+ width: 100%;
9679
+ min-height: 2.25rem;
9680
+ padding: 0.45rem 0.65rem;
9681
+ border-radius: 8px;
9682
+ color: var(--site-text-primary);
9683
+ font-size: var(--type-sm);
9684
+ line-height: 1.25;
9685
+ text-align: left;
9686
+ cursor: pointer;
9687
+ }
9688
+
9689
+ .tiptap-table-menu-item:hover,
9690
+ .tiptap-table-menu-item:focus-visible {
9691
+ background: var(--site-nav-hover-bg);
9692
+ }
9693
+
9694
+ .tiptap-table-menu-item.is-destructive {
9695
+ color: var(--destructive);
9696
+ }
9697
+
9698
+ .tiptap-table-options-separator {
9699
+ height: 1px;
9700
+ margin: 4px 5px;
9701
+ background: color-mix(in srgb, var(--site-divider) 58%, transparent);
9702
+ }
9703
+
9704
+ /* Slash-command table size picker. */
9705
+ .tiptap-table-size-picker {
9706
+ position: fixed;
9707
+ z-index: 10002;
9708
+ width: min(17rem, calc(100vw - 20px));
9709
+ padding: 10px;
9710
+ border: 1px solid color-mix(in srgb, var(--site-divider) 58%, transparent);
9711
+ border-radius: 13px;
9712
+ background: linear-gradient(
9713
+ 180deg,
9714
+ color-mix(
9715
+ in srgb,
9716
+ var(--site-elevated-bg) 96%,
9717
+ var(--site-nav-hover-bg) 4%
9718
+ )
9719
+ 0%,
9720
+ var(--site-elevated-bg) 100%
9721
+ );
9722
+ box-shadow:
9723
+ 0 14px 30px oklch(from var(--site-text-primary) l c h / 0.08),
9724
+ 0 2px 6px oklch(from var(--site-text-primary) l c h / 0.035);
9725
+ }
9726
+
9727
+ .tiptap-table-size-picker-header {
9728
+ display: flex;
9729
+ align-items: baseline;
9730
+ justify-content: space-between;
9731
+ gap: 1rem;
9732
+ padding: 0 2px 8px;
9733
+ color: var(--site-text-secondary);
9734
+ font-size: var(--type-xs);
9735
+ font-weight: var(--fw-medium);
9736
+ }
9737
+
9738
+ .tiptap-table-size-picker-dimensions {
9739
+ color: var(--site-text-primary);
9740
+ font-variant-numeric: tabular-nums;
9741
+ }
9742
+
9743
+ .tiptap-table-size-grid {
9744
+ display: grid;
9745
+ gap: 3px;
9746
+ }
9747
+
9748
+ .tiptap-table-size-grid-row {
9749
+ display: grid;
9750
+ grid-template-columns: repeat(8, minmax(0, 1fr));
9751
+ gap: 3px;
9752
+ }
9753
+
9754
+ .tiptap-table-size-cell {
9755
+ aspect-ratio: 1;
9756
+ min-width: 0;
9757
+ border: 1px solid color-mix(in srgb, var(--site-divider) 68%, transparent);
9758
+ border-radius: 4px;
9759
+ background: color-mix(
9760
+ in srgb,
9761
+ var(--site-elevated-bg) 90%,
9762
+ var(--site-nav-hover-bg) 10%
9763
+ );
9764
+ cursor: pointer;
9765
+ transition:
9766
+ border-color 0.1s ease,
9767
+ background-color 0.1s ease,
9768
+ transform 0.1s ease;
9769
+ }
9770
+
9771
+ .tiptap-table-size-cell.is-selected {
9772
+ border-color: color-mix(
9773
+ in srgb,
9774
+ var(--site-accent) 38%,
9775
+ var(--site-divider)
9776
+ );
9777
+ background: color-mix(
9778
+ in srgb,
9779
+ var(--site-accent) 14%,
9780
+ var(--site-elevated-bg)
9781
+ );
9782
+ }
9783
+
9784
+ .tiptap-table-size-cell.is-current {
9785
+ border-color: color-mix(
9786
+ in srgb,
9787
+ var(--site-accent) 72%,
9788
+ var(--site-divider)
9789
+ );
9790
+ transform: scale(0.9);
9791
+ }
9792
+
9793
+ @media (pointer: coarse) {
9794
+ .tiptap-table-control-btn {
9795
+ width: 2.75rem;
9796
+ height: 2.75rem;
9797
+ }
9798
+
9799
+ .tiptap-table-menu-item {
9800
+ min-height: 2.75rem;
9801
+ }
9802
+ }
9803
+
9114
9804
  /* --- Image figure in editor ------------------------------------------- */
9115
9805
 
9116
9806
  .tiptap-image-figure {
@@ -9794,16 +10484,18 @@
9794
10484
  }
9795
10485
 
9796
10486
  .compose-tiptap-body .tiptap .tiptap-footnote-reference {
9797
- font-size: 0.75em;
10487
+ font-size: var(--type-footnote-ref);
9798
10488
  line-height: 1;
9799
10489
  vertical-align: super;
9800
- color: var(--site-accent);
10490
+ color: var(--site-footnote-marker);
9801
10491
  white-space: nowrap;
10492
+ cursor: pointer;
9802
10493
  }
9803
10494
 
9804
10495
  .compose-tiptap-body
9805
10496
  .tiptap
9806
10497
  .tiptap-footnote-reference.ProseMirror-selectednode {
10498
+ color: var(--site-accent);
9807
10499
  outline: 1px solid var(--site-accent);
9808
10500
  outline-offset: 2px;
9809
10501
  border-radius: 4px;
@@ -9818,7 +10510,7 @@
9818
10510
  }
9819
10511
 
9820
10512
  .compose-tiptap-body .tiptap .tiptap-footnote-definition::before {
9821
- content: attr(data-footnote-label);
10513
+ content: "[^" attr(data-footnote-label) "]:";
9822
10514
  grid-column: 1;
9823
10515
  align-self: start;
9824
10516
  color: var(--site-text-secondary);
@@ -9834,13 +10526,22 @@
9834
10526
 
9835
10527
  .compose-tiptap-body
9836
10528
  .tiptap
9837
- > :not(.tiptap-footnote-definition)
9838
- + .tiptap-footnote-definition {
10529
+ > :not(.tiptap-footnote-definition, .ProseMirror-gapcursor)
10530
+ ~ .tiptap-footnote-definition {
9839
10531
  margin-top: 2rem;
9840
10532
  padding-top: 1rem;
9841
10533
  border-top: 1px solid var(--site-divider);
9842
10534
  }
9843
10535
 
10536
+ .compose-tiptap-body
10537
+ .tiptap
10538
+ > .tiptap-footnote-definition
10539
+ ~ .tiptap-footnote-definition {
10540
+ margin-top: 1rem;
10541
+ padding-top: 0;
10542
+ border-top: 0;
10543
+ }
10544
+
9844
10545
  .compose-tiptap-body .tiptap .tiptap-footnote-definition > :first-child {
9845
10546
  margin-top: 0;
9846
10547
  }
@@ -12094,6 +12795,168 @@
12094
12795
  }
12095
12796
  }
12096
12797
 
12798
+ /* ---------------------------------------------------------------------------
12799
+ * Wide public-post footnote rail.
12800
+ *
12801
+ * Kept OUTSIDE @layer components so the runtime layout can override the
12802
+ * unlayered `.prose` list rules. The source HTML remains one natural endnote
12803
+ * list; these selectors only apply after the client has measured a wide detail
12804
+ * or timeline container and confirmed that the rail fits safely.
12805
+ * ------------------------------------------------------------------------- */
12806
+
12807
+ :is(
12808
+ [data-post-view] [data-page="post"] [data-post-body].post-detail-body,
12809
+ [data-timeline-item] article[data-post] > [data-post-body].prose
12810
+ ) {
12811
+ --jant-footnote-rail-enabled: 0;
12812
+ }
12813
+
12814
+ @media screen {
12815
+ @container post-view (min-width: 56rem) {
12816
+ :is(
12817
+ [data-post-view] [data-page="post"] [data-post-body].post-detail-body,
12818
+ [data-timeline-item] article[data-post] > [data-post-body].prose
12819
+ ) {
12820
+ --jant-footnote-rail-enabled: 1;
12821
+ }
12822
+
12823
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready) {
12824
+ position: relative;
12825
+ }
12826
+
12827
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready)
12828
+ > .footnote-endnotes {
12829
+ position: absolute;
12830
+ inset-block-start: 0;
12831
+ inset-inline-start: calc(100% + var(--layout-sidenote-gap));
12832
+ width: var(--layout-sidenote-width);
12833
+ margin: 0;
12834
+ padding: 0;
12835
+ border: 0;
12836
+ }
12837
+
12838
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready)
12839
+ > .footnote-endnotes
12840
+ > .footnote-list {
12841
+ margin: 0;
12842
+ padding: 0;
12843
+ list-style: none;
12844
+ row-gap: 0.75rem;
12845
+ }
12846
+
12847
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready)
12848
+ > .footnote-endnotes
12849
+ > .footnote-list
12850
+ > .footnote {
12851
+ display: grid;
12852
+ grid-template-columns: max-content minmax(0, 1fr);
12853
+ column-gap: 1ch;
12854
+ align-items: baseline;
12855
+ min-width: 0;
12856
+ margin: 0;
12857
+ padding: 0;
12858
+ list-style: none;
12859
+ }
12860
+
12861
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready)
12862
+ > .footnote-endnotes
12863
+ > .footnote-list
12864
+ > .footnote::before {
12865
+ content: var(--footnote-rail-number);
12866
+ grid-column: 1;
12867
+ grid-row: 1;
12868
+ font-size: inherit;
12869
+ line-height: inherit;
12870
+ font-variant-numeric: tabular-nums;
12871
+ color: var(--site-footnote-marker);
12872
+ }
12873
+
12874
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready)
12875
+ > .footnote-endnotes
12876
+ > .footnote-list
12877
+ > .footnote
12878
+ > * {
12879
+ grid-column: 2;
12880
+ min-width: 0;
12881
+ }
12882
+
12883
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready)
12884
+ > .footnote-endnotes
12885
+ > .footnote-list
12886
+ > .footnote
12887
+ > :first-child {
12888
+ grid-row: 1;
12889
+ }
12890
+
12891
+ [data-post-body].prose.footnote-rail-measuring > .footnote-endnotes {
12892
+ visibility: hidden;
12893
+ }
12894
+
12895
+ [data-post-body].prose.footnote-rail-measuring
12896
+ > .footnote-endnotes
12897
+ > .footnote-list {
12898
+ display: grid;
12899
+ }
12900
+
12901
+ [data-post-body].prose.footnote-rail-ready
12902
+ > .footnote-endnotes
12903
+ > .footnote-list {
12904
+ position: relative;
12905
+ display: block;
12906
+ block-size: var(--footnote-rail-block-size);
12907
+ }
12908
+
12909
+ [data-post-body].prose.footnote-rail-ready
12910
+ > .footnote-endnotes
12911
+ > .footnote-list
12912
+ > .footnote {
12913
+ position: absolute;
12914
+ inset-block-start: var(--footnote-rail-y);
12915
+ inset-inline: 0;
12916
+ width: 100%;
12917
+ }
12918
+
12919
+ /* The note is already beside its first reference. Keep reciprocal links in
12920
+ the DOM for keyboard and assistive-technology users, revealing them when
12921
+ focus enters rather than adding a persistent arrow to every rail item. */
12922
+ [data-post-body].prose.footnote-rail-ready .footnote-backlinks {
12923
+ position: absolute;
12924
+ width: 1px;
12925
+ height: 1px;
12926
+ padding: 0;
12927
+ margin: -1px;
12928
+ overflow: hidden;
12929
+ clip: rect(0, 0, 0, 0);
12930
+ clip-path: inset(50%);
12931
+ white-space: nowrap;
12932
+ border: 0;
12933
+ }
12934
+
12935
+ [data-post-body].prose.footnote-rail-ready
12936
+ .footnote-backlinks:focus-within {
12937
+ position: static;
12938
+ grid-column: 2;
12939
+ width: auto;
12940
+ height: auto;
12941
+ margin: 0;
12942
+ overflow: visible;
12943
+ clip: auto;
12944
+ clip-path: none;
12945
+ white-space: normal;
12946
+ }
12947
+
12948
+ [data-post-body].prose.footnote-rail-ready .footnote:target {
12949
+ background: transparent;
12950
+ }
12951
+ }
12952
+ }
12953
+
12954
+ @media print {
12955
+ [data-post-body].prose:is(.footnote-rail-measuring, .footnote-rail-ready) {
12956
+ min-block-size: auto !important;
12957
+ }
12958
+ }
12959
+
12097
12960
  /* ---------------------------------------------------------------------------
12098
12961
  * Home description — editorial "lede" paragraph.
12099
12962
  *
@@ -12158,3 +13021,28 @@
12158
13021
  );
12159
13022
  color: color-mix(in srgb, var(--site-text-secondary) 80%, transparent);
12160
13023
  }
13024
+
13025
+ /*
13026
+ * Compose reply context renders cloned feed/detail post HTML. Keep this
13027
+ * outside @layer components so it can override the unlayered global post-body
13028
+ * width rules that intentionally make normal desktop post text use the 55%
13029
+ * reading column.
13030
+ */
13031
+ .compose-reply-context-body
13032
+ :is(
13033
+ .post-detail-title,
13034
+ .post-detail-body,
13035
+ .post-body-summary,
13036
+ .feed-note-title,
13037
+ .feed-link-title,
13038
+ .feed-quote,
13039
+ .feed-quote-attribution,
13040
+ .feed-quote-commentary,
13041
+ .feed-link-domain,
13042
+ .feed-continue-link,
13043
+ [data-post-body].prose,
13044
+ [data-post-meta]
13045
+ ) {
13046
+ width: 100%;
13047
+ max-width: none;
13048
+ }