@lettercamp/cli 0.1.7 → 0.1.9

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 (60) hide show
  1. package/dist/account.js +20 -8
  2. package/dist/build.js +22 -5
  3. package/dist/cli.js +15 -6
  4. package/dist/code-block.js +50 -0
  5. package/dist/code-detection.js +24 -0
  6. package/dist/code-highlight.js +54 -0
  7. package/dist/component-blocks.js +32 -0
  8. package/dist/custom-layout.js +32 -0
  9. package/dist/deploy.js +131 -0
  10. package/dist/dev.js +28 -10
  11. package/dist/domain.js +190 -0
  12. package/dist/hosting.js +132 -0
  13. package/dist/lettercamp.js +1 -4
  14. package/dist/media-dimensions.js +154 -0
  15. package/dist/new.js +1 -1
  16. package/dist/preview.js +1 -1
  17. package/dist/prompts.js +31 -0
  18. package/dist/publication-links.js +23 -0
  19. package/dist/react.js +15 -0
  20. package/dist/upload.js +40 -10
  21. package/dist/workspace.js +5 -3
  22. package/package.json +11 -2
  23. package/renderer/src/components/AuthorByline.astro +20 -11
  24. package/renderer/src/components/BlogHome.astro +14 -57
  25. package/renderer/src/components/Footer.astro +12 -9
  26. package/renderer/src/components/Header.astro +38 -0
  27. package/renderer/src/components/MobileMenuIcons.astro +7 -0
  28. package/renderer/src/components/PostCard.astro +12 -5
  29. package/renderer/src/components/SocialLink.astro +4 -4
  30. package/renderer/src/layouts/SiteLayout.astro +70 -51
  31. package/renderer/src/lib/analytics.ts +21 -0
  32. package/renderer/src/lib/author-dialog.ts +102 -0
  33. package/renderer/src/lib/authors.js +18 -0
  34. package/renderer/src/lib/block-widths.ts +35 -0
  35. package/renderer/src/lib/branding.js +15 -0
  36. package/renderer/src/lib/button.js +10 -0
  37. package/renderer/src/lib/category-routes.js +19 -0
  38. package/renderer/src/lib/code-block.ts +46 -0
  39. package/renderer/src/lib/font-fallbacks.js +196 -0
  40. package/renderer/src/lib/fonts.js +48 -0
  41. package/renderer/src/lib/home-layout.ts +5 -0
  42. package/renderer/src/lib/image-radius.ts +5 -0
  43. package/renderer/src/lib/image-rounding.ts +28 -0
  44. package/renderer/src/lib/image-widths.ts +13 -0
  45. package/renderer/src/lib/metadata.js +47 -0
  46. package/renderer/src/lib/mobile-navigation.ts +76 -0
  47. package/renderer/src/lib/post-layout.ts +6 -0
  48. package/renderer/src/lib/post-share.ts +264 -0
  49. package/renderer/src/lib/post-toc.ts +69 -0
  50. package/renderer/src/lib/posts.js +14 -6
  51. package/renderer/src/lib/publication-url.js +14 -0
  52. package/renderer/src/lib/table-styles.ts +12 -0
  53. package/renderer/src/lib/urls.js +3 -0
  54. package/renderer/src/pages/[...slug].astro +21 -11
  55. package/renderer/src/pages/category/[tag].astro +16 -0
  56. package/renderer/src/pages/category/index.astro +4 -0
  57. package/renderer/src/pages/index.astro +1 -1
  58. package/renderer/src/styles/post-share.css +57 -0
  59. package/renderer/src/styles/site.css +261 -91
  60. package/dist/public-assets.js +0 -29
@@ -0,0 +1,57 @@
1
+ .byline:has(.post-share-button) { display: grid; grid-template-columns: minmax(0, 1fr) auto; width: 100%; }
2
+ .byline:has(.post-share-button) .post-byline { grid-column: 1; grid-row: 1; min-width: 0; }
3
+ .byline > .post-share-button { grid-column: 2; grid-row: 1; justify-self: end; }
4
+ .post-share-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; flex-shrink: 0; margin-left: auto; padding: 5px 11px; border: 0; border-radius: 999px; background: #f5f5f5; color: #555; font-size: 13px; line-height: 1.4; }
5
+ .post-share-button:hover { background: #ededed; color: #111; }
6
+ .post-share-button svg { width: 15px; height: 15px; }
7
+ html:has(.post-share-dialog[open]) { overflow: hidden; }
8
+ .post-share-dialog { position: fixed; width: min(380px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); margin: auto; padding: 16px; overflow: auto; overscroll-behavior: contain; border: 0; border-radius: 14px; background: var(--site-background, #fff); color: #222; box-shadow: 0 20px 80px #0003; font: 13px/1.5 var(--font-family); }
9
+ .post-share-dialog::backdrop { background: #0006; }
10
+ .post-share-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
11
+ .post-share-heading h2 { margin: 0; font: 600 15px/1.4 var(--font-family); letter-spacing: -.3px; }
12
+ .post-share-close { display: grid; place-items: center; flex-shrink: 0; width: 28px; height: 28px; margin: -4px -4px -4px 0; padding: 0; border: 0; background: transparent; color: #777; }
13
+ .post-share-close:hover { color: #111; }
14
+ .post-share-close svg { width: 19px; height: 19px; }
15
+ .post-share-preview { overflow: hidden; border: 1px solid #e5e5e5; border-radius: 8px; background: #f7f7f7; color: #222; font-family: Arial, Helvetica, sans-serif; }
16
+ .post-share-image { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; }
17
+ .post-share-preview-copy { padding: 9px 10px; }
18
+ .post-share-domain { margin: 0 0 4px; color: #777; font-size: 10px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
19
+ .post-share-preview h3 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 0; font: 600 14px/1.3 Arial, Helvetica, sans-serif; letter-spacing: 0; text-wrap: auto; }
20
+ .post-share-description { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 4px 0 0; color: #666; font-size: 11px; line-height: 1.4; overflow-wrap: anywhere; }
21
+ .post-share-description:empty { display: none; }
22
+ .post-share-actions { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin-top: 22px; }
23
+ .post-share-action { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; padding: 0; border: 0; background: transparent; color: #555; font-size: 11px; line-height: 1.4; text-decoration: none; }
24
+ .post-share-action-icon { display: grid; place-items: center; width: 100%; height: 40px; border-radius: 10px; background: #00000005; }
25
+ .post-share-action:hover, .post-share-action[aria-expanded='true'] { color: #111; }
26
+ .post-share-action:hover .post-share-action-icon, .post-share-action[aria-expanded='true'] .post-share-action-icon { background: #0000000a; }
27
+ .post-share-action svg { width: 18px; height: 18px; }
28
+ .post-share-menu { position: fixed; inset: auto; width: 184px; max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px); margin: 0; padding: 6px; overflow: auto; overscroll-behavior: contain; border: 1px solid #eaeaea; border-radius: 10px; background: #fafafa; color: #444; box-shadow: 0 6px 24px #0002; font: 12px/1.4 var(--font-family); }
29
+ .post-share-menu::backdrop { background: transparent; }
30
+ .post-share-menu-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 7px 9px; border: 0; border-radius: 6px; background: transparent; color: inherit; text-align: left; font: inherit; text-decoration: none; }
31
+ .post-share-menu-item:is(:hover, :focus-visible) { background: #efefef; color: #111; outline: none; }
32
+ .post-share-menu-item svg { width: 15px; height: 15px; color: #888; }
33
+ .post-share-menu-item[data-share='native'] { margin-top: 4px; border-top: 1px solid #e8e8e8; border-radius: 0; }
34
+ .post-share-summaries { margin-top: 14px; padding-top: 12px; border-top: 1px solid #0000000d; }
35
+ .post-share-summaries h3 { margin: 0; font: 600 13px/1.5 var(--font-family); }
36
+ .post-share-summaries > p { margin: 3px 0 9px; color: #777; font-size: 12px; }
37
+ .post-share-providers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
38
+ .post-share-provider { display: flex; align-items: center; justify-content: space-between; gap: 5px; padding: 7px 9px; border-radius: 7px; background: #00000005; font-size: 12px; text-decoration: none; }
39
+ .post-share-provider:hover { background: #0000000a; }
40
+ .post-share-provider svg { width: 13px; height: 13px; flex-shrink: 0; color: #888; }
41
+ .post-share-status { margin: 12px 0 0; color: #666; font-size: 12px; text-align: center; }
42
+ .post-share-status:empty { display: none; }
43
+ .post-share-copy-fallback { margin-top: 12px; }
44
+ .post-share-copy-fallback label { display: block; margin-bottom: 4px; color: #666; font-size: 12px; }
45
+ .post-share-copy-fallback input { width: 100%; min-width: 0; padding: 8px 10px; border: 1px solid #ddd; border-radius: 8px; background: #fff; color: #333; font: inherit; }
46
+ @media (max-width: 640px) {
47
+ .byline:has(.post-share-button) .post-byline { flex-wrap: wrap; gap: 6px 12px; overflow: visible; white-space: normal; }
48
+ .byline:has(.post-share-button) .post-authors { max-width: 100%; white-space: normal; }
49
+ }
50
+ @media (max-width: 400px) {
51
+ .post-share-provider { padding: 7px; font-size: 11px; }
52
+ .byline:has(.post-share-button) { column-gap: 10px; }
53
+ }
54
+ @media (prefers-reduced-motion: no-preference) {
55
+ .post-share-dialog[open] { animation: post-share-enter 140ms ease-out; }
56
+ @keyframes post-share-enter { from { opacity: 0; transform: translateY(6px) scale(.98); } to { opacity: 1; transform: none; } }
57
+ }
@@ -1,12 +1,23 @@
1
1
  :root {
2
2
  color-scheme: light;
3
3
  --font-family: 'Inter', Arial, Helvetica, sans-serif;
4
- --heading-size: 3rem;
5
- --body-size: 1.125rem;
4
+ font-size: 100%;
5
+ --type-scale: 1;
6
+ --text-base: calc(1rem * var(--type-scale));
7
+ --text-title: calc(clamp(2rem, 3.5vw, 2.625rem) * var(--type-scale));
8
+ --text-card-title: calc(clamp(1.125rem, 1.4vw, 1.25rem) * var(--type-scale));
9
+ --text-h2: calc(var(--text-base) * 1.875);
10
+ --text-h3: calc(var(--text-base) * 1.5);
11
+ --text-h4: calc(var(--text-base) * 1.25);
12
+ --text-h5: calc(var(--text-base) * 1.125);
13
+ --text-summary: calc(var(--text-base) * .9375);
14
+ --text-small: calc(var(--text-base) * .875);
15
+ --text-label: calc(var(--text-base) * .8125);
16
+ --text-meta: calc(var(--text-base) * .75);
6
17
  font-family: var(--font-family);
7
18
  color: #111;
8
- background: #fff;
9
- font-synthesis: none;
19
+ background: var(--site-background, #ffffff);
20
+ font-synthesis: weight style;
10
21
  -webkit-font-smoothing: antialiased;
11
22
  --muted: #666;
12
23
  --text-secondary: #6f6e77;
@@ -16,145 +27,212 @@
16
27
  }
17
28
  * { box-sizing: border-box; }
18
29
  [hidden] { display: none !important; }
19
- body { margin: 0; font-size: var(--body-size); line-height: 1.65; }
30
+ body { margin: 0; font-size: var(--text-base); line-height: 1.65; }
20
31
  a { color: inherit; text-underline-offset: .22em; }
21
32
  button { font: inherit; color: inherit; cursor: pointer; }
22
33
  a:focus-visible, button:focus-visible, summary:focus-visible { outline: 2px solid #111; outline-offset: 5px; border-radius: 3px; }
23
34
  img { display: block; max-width: 100%; height: auto; }
24
- h1, h2, h3, h4 { text-wrap: balance; overflow-wrap: anywhere; }
35
+ h1, h2, h3, h4, h5, h6 { font-family: var(--heading-font, var(--font-family)); text-wrap: balance; overflow-wrap: anywhere; }
25
36
  .site-header-inner, .footer-inner, .blog-home { width: var(--site-width); margin-inline: auto; }
26
- .site-header { border-bottom: 1px solid var(--line); }
27
- .site-header-inner { position: relative; min-height: 72px; display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-block: 12px; }
37
+ .site-header { position: relative; z-index: 30; border: 0; background: var(--site-background, #ffffff); }
38
+ .site-header[data-header-position='fixed'] { position: sticky; top: 0; }
39
+ html:has(.site-header[data-header-position='fixed']) { scroll-padding-top: 80px; }
40
+ .site-header-inner { position: relative; min-height: 64px; display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-block: 12px; }
41
+ @keyframes header-scroll-away { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-6px); } }
42
+ @media (prefers-reduced-motion: no-preference) {
43
+ @supports (animation-timeline: scroll()) {
44
+ .site-header[data-header-position='scroll']:not(:focus-within):not(:has(details[open])) .site-header-inner {
45
+ animation: header-scroll-away linear both;
46
+ animation-timeline: scroll(root block);
47
+ animation-range: 0px 64px;
48
+ }
49
+ }
50
+ }
28
51
  .site-header .brand { min-width: 0; overflow-wrap: anywhere; }
29
52
  .site-header .brand img { flex-shrink: 0; }
30
- .brand { display: inline-flex; align-items: center; gap: 9px; font-size: 17px; font-weight: 600; letter-spacing: -.45px; text-decoration: none; }
53
+ .brand { display: inline-flex; align-items: center; gap: 9px; font-size: calc(var(--text-base) * 1.0625); font-weight: 600; letter-spacing: -.45px; text-decoration: none; }
31
54
  .brand img { width: 24px; height: 24px; }
32
- .site-header nav { display: flex; flex-wrap: wrap; gap: 28px; font-size: 14px; }
55
+ .site-header nav { display: flex; flex-wrap: wrap; gap: 28px; font-size: var(--text-small); }
33
56
  .site-header nav a { text-decoration: none; color: var(--muted); }
34
57
  .site-header nav a[aria-current], .site-header nav a:hover { color: #111; }
35
58
  .header-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 16px 28px; margin-left: auto; max-width: 100%; }
36
- .header-primary { display: inline-flex; justify-content: center; padding: 6px 12px; border-radius: 6px; background: #111; color: #fff; font-size: 13px; font-weight: 500; text-decoration: none; overflow-wrap: anywhere; max-width: 100%; }
37
- .header-primary:hover { background: #333; }
38
- .mobile-navigation { display: none; position: relative; }
39
- .mobile-navigation summary { cursor: pointer; padding: 9px 0; font-size: 13px; }
59
+ .header-primary { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 5px 20px; border: 0; border-radius: 12px; background: var(--button-color, #111111); color: var(--button-text, #ffffff); font-size: var(--text-label); font-weight: 500; line-height: 1.4; text-decoration: none; overflow-wrap: anywhere; max-width: 100%; }
60
+ .site-header a.header-primary, .site-header a.header-primary:hover { color: var(--button-text, #ffffff); }
61
+ .header-primary:hover { filter: brightness(.9); }
62
+ .mobile-navigation { display: none; }
63
+ .mobile-menu-toggle { position: relative; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; list-style: none; cursor: pointer; background: transparent; }
64
+ .mobile-menu-toggle::-webkit-details-marker { display: none; }
65
+ .mobile-menu-toggle svg { grid-area: 1 / 1; width: 24px; height: 24px; transition: opacity 180ms ease, transform 180ms ease; }
66
+ .mobile-close-icon { opacity: 0; transform: rotate(-90deg) scale(.75); }
67
+ .mobile-navigation[open] .mobile-menu-icon { opacity: 0; transform: rotate(90deg) scale(.75); }
68
+ .mobile-navigation[open] .mobile-close-icon { opacity: 1; transform: rotate(0) scale(1); }
69
+ @keyframes mobile-menu-enter { from { opacity: 0; translate: 0 -6px; } to { opacity: 1; translate: 0 0; } }
70
+ @media (prefers-reduced-motion: reduce) {
71
+ .mobile-menu-toggle svg { transition: none; }
72
+ }
40
73
  .blog-home { padding-block: 72px 100px; }
41
74
  .blog-heading { margin-bottom: 18px; }
75
+ .blog-home:not(:has(> .category-selector:not([hidden]))) > .blog-heading { margin-bottom: 64px; }
42
76
  .blog-heading h1 { margin-bottom: 0; }
43
- .eyebrow { text-transform: uppercase; letter-spacing: .12em; font-size: 11px; font-weight: 600; color: var(--muted); margin: 0 0 20px; }
44
- .eyebrow a { text-decoration: none; }
45
- h1 { font-size: clamp(calc(var(--heading-size) * .75), 4vw, var(--heading-size)); font-weight: 500; line-height: 1.15; letter-spacing: -.04em; margin: 0 0 22px; }
46
- .description { color: var(--text-secondary); font-size: var(--body-size); line-height: 1.65; max-width: 660px; margin: 0; }
77
+ .article-category { margin: 0 0 20px; }
78
+ .article-category a, .post-card-category { display: inline-flex; align-items: center; justify-content: center; padding: 4px 15px; border: 0; border-radius: 10px; background: #fafafa; color: var(--muted); font-size: var(--text-meta); font-weight: 500; line-height: 1.333333; text-decoration: none; }
79
+ h1 { font-size: var(--text-title); font-weight: 500; line-height: 1.15; letter-spacing: -.04em; margin: 0 0 22px; }
80
+ .description { color: var(--text-secondary); font-size: var(--text-base); line-height: 1.65; max-width: 660px; margin: 0; }
47
81
  .category-selector { display: flex; flex-wrap: wrap; gap: 8px 24px; margin-block: 0 40px; }
48
- .category-selector a { padding-block: 8px; color: var(--muted); font-size: 14px; line-height: 1.5; text-decoration: none; transition: color .15s; }
82
+ .category-selector a { padding-block: 8px; color: var(--muted); font-size: var(--text-small); line-height: 1.5; text-decoration: none; transition: color .15s; }
49
83
  .category-selector a:hover { color: #111; }
50
84
  .category-selector a[aria-current='page'] { color: #111; font-weight: 500; }
51
85
  .post-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 64px 48px; }
52
- .post-card { min-width: 0; }
53
- .post-card-link { display: flex; flex-direction: column; height: 100%; gap: 24px; text-decoration: none; }
86
+ .post-card { position: relative; isolation: isolate; min-width: 0; }
87
+ .post-card-link { color: inherit; text-decoration: none; }
88
+ .post-card-link::after { content: ''; position: absolute; inset: 0; z-index: 1; }
89
+ .post-card-link:focus-visible { outline: none; }
90
+ .post-card-link:focus-visible::after { outline: 2px solid currentColor; outline-offset: 6px; border-radius: 4px; }
91
+ .post-card-layout { display: flex; flex-direction: column; height: 100%; gap: 20px; text-decoration: none; }
54
92
  .post-card-content { display: flex; flex-direction: column; flex: 1; min-width: 0; }
55
- .post-card-cover { flex-shrink: 0; overflow: hidden; aspect-ratio: 1200 / 630; background: #f4f4f4; border: 1px solid var(--line); border-radius: 4px; }
93
+ .post-card-meta { margin-top: auto; min-width: 0; }
94
+ .archive-date { display: none; }
95
+ .post-card-details { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px; margin-bottom: 12px; color: #8c8b93; font-size: var(--text-label); line-height: 1.4; }
96
+ .post-card-category { min-width: 0; overflow-wrap: anywhere; }
97
+ .post-card-date { white-space: nowrap; }
98
+ .post-card-cover { flex-shrink: 0; overflow: hidden; aspect-ratio: 1200 / 630; background: #f4f4f4; border: 1px solid var(--line); border-radius: calc(var(--image-short-side, 200px) * var(--image-radius, 0.02)); }
56
99
  .post-card-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .25s ease; }
57
- .post-card-link:hover .post-card-cover img { transform: scale(1.025); }
58
- .image-placeholder { display: grid; place-items: center; height: 100%; font-size: 26px; letter-spacing: -.05em; color: #777; }
59
- .post-card h3 { font-size: clamp(calc(var(--heading-size) * .375), 1.4vw, calc(var(--heading-size) * .4167)); font-weight: 500; line-height: 1.35; letter-spacing: -.025em; margin: 0 0 8px; text-wrap: pretty; }
60
- .post-card p { font-size: calc(var(--body-size) * .8333); line-height: 1.55; margin: 0; color: var(--text-secondary); }
61
- .post-byline { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; margin-top: auto; padding-top: 24px; color: var(--text-tertiary); font-size: 12px; line-height: 1.4; }
62
- .post-author { display: inline-flex; align-items: center; gap: 8px; min-width: 0; overflow-wrap: anywhere; }
63
- .author-avatar { width: 24px; height: 24px; flex-shrink: 0; border-radius: 50%; object-fit: cover; }
64
- .post-card .post-byline { gap: 8px; }
100
+ .post-card:has(.post-card-link:hover) .post-card-cover img { transform: scale(1.025); }
101
+ .image-placeholder { display: grid; place-items: center; height: 100%; font-size: calc(var(--text-base) * 1.625); letter-spacing: -.05em; color: #777; }
102
+ .post-card h3 { font-size: var(--text-card-title); font-weight: 500; line-height: 1.35; letter-spacing: -.025em; margin: 0 0 8px; text-wrap: pretty; }
103
+ .post-card p { font-size: var(--text-summary); line-height: 1.55; margin: 0; color: var(--text-secondary); }
104
+ .post-byline { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin-top: auto; padding-top: 24px; color: #8c8b93; font-size: var(--text-label); line-height: 1.4; }
105
+ .post-authors { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; min-width: 0; }
106
+ .author-portraits { position: relative; z-index: 2; display: inline-flex; align-items: center; flex-shrink: 0; isolation: isolate; }
107
+ .author-portrait { position: relative; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 24px; height: 24px; border: 1px solid #fafafa; border-radius: 50%; background: #f1f1f2; }
108
+ .author-portraits .author-portrait + .author-portrait { margin-left: -8px; }
109
+ .author-portraits .author-portrait:hover { z-index: 1; }
110
+ .author-avatar { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
111
+ .author-initials { color: #666; font-size: 9px; font-weight: 500; line-height: 1; }
112
+ .author-names { min-width: 0; overflow-wrap: anywhere; }
113
+ .author-more { position: relative; z-index: 2; display: inline; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); text-underline-offset: 3px; cursor: pointer; white-space: nowrap; }
114
+ .author-more:hover { color: var(--text-primary, #111); }
115
+ .author-more:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; border-radius: 3px; }
116
+ .authors-popover { position: fixed; inset: auto; width: min(240px, calc(100vw - 16px)); max-height: 320px; margin: 0; padding: 10px; overflow: auto; border: 1px solid #ebebeb; border-radius: 9px; background: #fafafa; color: #55545c; box-shadow: 0 4px 18px #00000017; font: 13px/1.4 var(--font-family); outline: none; }
117
+ .authors-popover::backdrop { background: transparent; }
118
+ .authors-popover-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
119
+ .authors-popover-author { display: flex; align-items: center; gap: 9px; min-width: 0; overflow-wrap: anywhere; }
120
+ .authors-popover-author .author-portrait { width: 24px; height: 24px; }
121
+ .authors-popover-author .author-initials { font-size: 10px; }
122
+ @media (prefers-reduced-motion: no-preference) {
123
+ .authors-popover:popover-open { animation: authors-popover-in 100ms ease-out; }
124
+ @keyframes authors-popover-in { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } }
125
+ }
65
126
  .post-byline time { white-space: nowrap; }
66
127
  .reading-time { margin-left: auto; white-space: nowrap; }
67
- .post-list.post-list-rows { grid-template-columns: 1fr; }
68
- .post-list-rows .post-card-link { display: grid; grid-template-columns: minmax(0, .86fr) minmax(0, 1.14fr); align-items: center; gap: 32px; }
69
- .post-list-rows .post-card h3 { font-size: calc(var(--heading-size) * .5); }
70
- .archive { margin-top: 72px; }
71
- .archive > h2 { font-size: calc(var(--heading-size) * .5); font-weight: 500; letter-spacing: -.025em; margin: 0 0 20px; }
128
+ [data-home-layout='rows'] .post-list { grid-template-columns: 1fr; }
129
+ [data-home-layout='rows'] .post-list .post-card-layout { display: grid; grid-template-columns: minmax(0, .86fr) minmax(0, 1.14fr); align-items: center; gap: 32px; }
130
+ [data-home-layout='rows'] .post-list .post-card h3 { font-size: var(--text-h3); }
131
+ .archive { margin-top: 104px; }
132
+ .archive > h2 { font-size: var(--text-h3); font-weight: 500; letter-spacing: -.025em; margin: 0 0 20px; }
72
133
  .archive-list { display: grid; gap: 4px; }
73
- .archive-list .post-card-link { padding-block: 18px; }
74
- .archive-list .post-card-cover, .archive-list .post-card-description, .archive-list .author-avatar { display: none; }
75
- .archive-list .post-card-content { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px 32px; }
76
- .archive-list h3 { grid-area: 1 / 1; font-size: calc(var(--heading-size) * .375); margin: 0; }
77
- .archive-list .post-byline { grid-area: 1 / 2; justify-content: flex-end; margin: 0; padding: 0; }
78
- .archive-list .post-card-link:hover h3 { text-decoration: underline; text-underline-offset: .2em; }
79
- .empty-state { grid-column: 1 / -1; color: var(--muted); padding: 40px 0 70px; font-size: 16px; }
134
+ .archive-list .post-card { margin-inline: -12px; padding-inline: 12px; border-radius: 6px; }
135
+ .archive-list .post-card:is(:hover, :focus-within) { background: #f7f7f8; }
136
+ .archive-list .post-card-layout { padding-block: 18px; }
137
+ .archive-list .post-card-cover, .archive-list .post-card-description { display: none; }
138
+ .archive-list .post-card-content { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .85fr); align-items: center; gap: 16px 32px; }
139
+ .archive-list .post-card-details { display: none; }
140
+ .archive-list h3 { grid-area: 1 / 1; font-size: var(--text-base); margin: 0; }
141
+ .archive-list .post-card-meta { grid-area: 1 / 2; display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px 16px; margin: 0; color: #8c8b93; font-size: var(--text-label); line-height: 1.4; }
142
+ .archive-list .archive-date { display: block; white-space: nowrap; }
143
+ .archive-list .post-byline { align-items: flex-end; min-width: 0; margin: 0; padding: 0; text-align: right; }
144
+ .archive-list .post-authors { justify-content: flex-end; }
145
+ .archive-list .author-portraits { display: none; }
146
+ .archive-list :is(.post-byline, .post-card-date) { font-size: var(--text-label); }
147
+ .empty-state { grid-column: 1 / -1; color: var(--muted); padding: 40px 0 70px; font-size: var(--text-base); }
80
148
  .publication { width: min(760px, calc(100% - 48px)); margin: 60px auto 100px; }
81
149
  .article-header { margin-bottom: 40px; }
150
+ .article-header h1 { font-weight: 700; }
82
151
  .publication h1 { line-height: 1.2; }
83
- .back-link { display: inline-block; font-size: 13px; color: var(--muted); text-decoration: none; margin-bottom: 40px; }
84
- .byline { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 26px; font-size: 13px; color: var(--text-tertiary); }
152
+ .byline { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 26px; font-size: var(--text-label); color: var(--text-tertiary); }
85
153
  .byline { align-items: center; }
86
- .byline .post-byline { padding: 0; margin: 0; }
154
+ .byline .post-byline { flex-direction: row; align-items: center; flex-wrap: nowrap; gap: 16px; max-width: 100%; padding: 0; margin: 0; overflow-x: auto; scrollbar-width: thin; white-space: nowrap; }
155
+ .byline .post-authors { flex-wrap: nowrap; flex-shrink: 0; white-space: nowrap; }
156
+ .byline .post-byline > time, .byline .reading-time { flex-shrink: 0; font-size: inherit; }
87
157
  .byline .reading-time { margin-left: 0; }
88
158
  .cover { margin: 0 0 44px; }
89
- .cover img, .prose img { width: 100%; border-radius: 4px; }
159
+ .cover img, .prose img, .article-hero-image img { background-color: #fafafa; }
160
+ .cover img, .prose img:not([width]) { width: 100%; border-radius: calc(var(--image-short-side, 200px) * var(--image-radius, 0.02)); }
161
+ .prose img { display: block; max-width: 100%; height: auto; margin: 32px auto; border-radius: calc(var(--image-short-side, 200px) * var(--image-radius, 0.02)); }
162
+ .prose img[data-media-sized] { width: 100%; }
163
+ .prose :is(img, video):not([width][height]) { aspect-ratio: auto 16 / 9; }
164
+ .prose video { display: block; max-width: 100%; height: auto; background: #fafafa; }
165
+ .prose video:not([width]) { width: 100%; }
166
+ .prose audio { width: min(100%, 560px); height: 54px; }
90
167
  .publication-post { width: var(--site-width); }
91
168
  .article-hero { margin-bottom: 64px; padding-bottom: 64px; border-bottom: 1px solid var(--line); }
92
169
  .article-hero-with-image { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: 64px; }
93
170
  .article-intro { min-width: 0; }
94
171
  .article-hero .article-intro { max-width: 760px; }
95
172
  .article-hero-image { margin: 0; }
96
- .article-hero-image img { width: 100%; border-radius: 6px; }
97
- .publication-post { --post-content-width: 760px; }
98
- .publication-post[data-post-width='narrow'] { --post-content-width: 640px; }
99
- .publication-post[data-post-width='wide'] { --post-content-width: 920px; }
173
+ .article-hero-image img { width: 100%; border-radius: calc(var(--image-short-side, 200px) * var(--image-radius, 0.02)); }
174
+ .publication-post { --post-content-width: calc(var(--text-base) * 40); }
100
175
  .publication-post[data-post-cover='hidden'] .article-hero-image, .publication-post[data-post-toc='hidden'] .article-sidebar { display: none; }
101
176
  .publication-post[data-post-cover='hidden'] .article-hero, .publication-post[data-post-hero='stacked'] .article-hero { display: block; }
102
- .publication-post[data-post-hero='stacked'] .article-hero-image { margin-top: 32px; }
103
177
  .publication-post[data-post-toc='hidden'] .article-body { display: block; max-width: var(--post-content-width); }
104
- .article-body { max-width: var(--post-content-width, 760px); margin-inline: auto; }
105
- .article-body-with-toc { display: grid; grid-template-columns: 240px minmax(0, var(--post-content-width, 760px)); align-items: start; gap: 64px; max-width: calc(var(--post-content-width, 760px) + 304px); }
106
- .article-sidebar { position: sticky; top: 32px; max-height: calc(100dvh - 64px); overflow-y: auto; }
107
- .article-toc { padding: 4px 6px 6px; }
108
- .article-toc h2 { margin: 0 0 20px; font-size: 13px; font-weight: 600; letter-spacing: 0; }
178
+ .article-body { max-width: var(--post-content-width, 640px); margin-inline: auto; }
179
+ .article-body-with-toc { display: grid; grid-template-columns: 180px minmax(0, var(--post-content-width, 640px)); align-items: start; gap: 32px; max-width: calc(var(--post-content-width, 640px) + 212px); }
180
+ .article-sidebar { min-width: 0; position: sticky; top: 32px; max-height: calc(100dvh - 64px); overflow-y: auto; }
181
+ .article-toc { padding: 4px 0 6px; }
182
+ .article-toc h2 { font-family: var(--font-family); margin: 0 0 20px; font-size: var(--text-label); font-weight: 600; letter-spacing: 0; }
109
183
  .article-toc ol { list-style: none; margin: 0; padding: 0; }
110
184
  .article-toc li + li { margin-top: 14px; }
111
- .article-toc a { display: block; font-size: 13px; line-height: 1.6; color: var(--muted); text-decoration: none; overflow-wrap: anywhere; }
185
+ .article-toc a { display: block; font-size: var(--text-label); line-height: 1.6; color: var(--muted); text-decoration: none; overflow-wrap: anywhere; }
112
186
  .article-toc a:hover { color: #111; text-decoration: underline; }
113
187
  .article-toc .toc-subheading { padding-left: 16px; }
114
188
  .article-body .prose { min-width: 0; }
115
189
  .prose :is(h2, h3, h4, h5, h6) { scroll-margin-top: 32px; }
116
- .prose { font-size: var(--body-size); line-height: 1.8; overflow-wrap: anywhere; }
190
+ .prose { font-size: var(--text-base); line-height: var(--body-line-height, 1.8); overflow-wrap: anywhere; }
117
191
  .prose > :first-child { margin-top: 0; }
118
192
  .prose p { margin: 1.3em 0; }
119
- .prose h2 { font-size: calc(var(--heading-size) * .625); line-height: 1.3; font-weight: 600; letter-spacing: -.04em; margin: 2em 0 .7em; }
120
- .prose h3 { font-size: calc(var(--heading-size) * .5); line-height: 1.4; font-weight: 600; letter-spacing: -.03em; margin: 1.7em 0 .6em; }
121
- .prose h4 { font-size: calc(var(--heading-size) * .4167); line-height: 1.5; font-weight: 600; margin: 1.6em 0 .5em; }
193
+ .prose h2 { font-size: var(--text-h2); line-height: 1.3; font-weight: 600; letter-spacing: -.04em; margin: 2em 0 .7em; }
194
+ .prose h3 { font-size: var(--text-h3); line-height: 1.4; font-weight: 600; letter-spacing: -.03em; margin: 1.7em 0 .6em; }
195
+ .prose h4 { font-size: var(--text-h4); line-height: 1.5; font-weight: 600; margin: 1.6em 0 .5em; }
196
+ .prose h5 { font-size: var(--text-h5); line-height: 1.5; }
197
+ .prose h6 { font-size: var(--text-base); line-height: 1.5; }
122
198
  .prose h3 a { text-decoration: none; }
123
199
  .prose h3 a:hover { text-decoration: underline; }
124
200
  .prose ul, .prose ol { padding-left: 1.4em; }
125
201
  .prose li { padding-left: .2em; margin: .5em 0; }
126
- .prose blockquote { border-left: 2px solid var(--line); padding: 4px 0 4px 26px; margin: 36px 0; color: #555; font-size: calc(var(--body-size) * 1.15); line-height: 1.55; }
202
+ .prose .task-list-item:has(> input[type="checkbox"]:checked, > p > input[type="checkbox"]:checked) { text-decoration: line-through; }
203
+ .prose blockquote { border-left: 2px solid var(--line); padding: 4px 0 4px 26px; margin: 36px 0; color: #555; font-size: calc(var(--text-base) * 1.15); line-height: 1.55; }
127
204
  .prose blockquote p { margin: .3em 0; }
128
205
  .prose hr { border: 0; height: 1px; background: var(--line); margin: 42px 0; }
129
- .prose pre { padding: 24px; overflow: auto; border-radius: 5px; font-size: 14px; line-height: 1.8; }
206
+ .prose pre { padding: 24px; overflow: auto; border-radius: 5px; font-size: var(--text-small); line-height: 1.8; }
130
207
  .prose code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; }
131
208
  .prose :not(pre) > code { padding: .18em .35em; background: #f3f3f3; border-radius: 3px; }
209
+ .prose span[style*="background-color"] code { background-color: inherit; }
132
210
  .cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin: 32px 0; }
133
211
  .card { display: flex; flex-direction: column; align-items: start; padding: 26px; border-radius: 6px; background: #f6f6f6; text-decoration: none; }
134
212
  .card:hover { background: #eee; }
135
- .card-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
136
- .prose .card h3 { font-size: calc(var(--heading-size) * .4583); margin: 12px 0 6px; }
137
- .prose .card p { font-size: calc(var(--body-size) * .8333); line-height: 1.7; color: var(--muted); margin: 0 0 20px; }
138
- .card-link { font-size: 13px; font-weight: 600; margin-top: auto; }
213
+ .card-eyebrow { font-size: calc(var(--text-base) * .6875); text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
214
+ .prose .card h3 { font-size: calc(var(--text-base) * 1.375); margin: 12px 0 6px; }
215
+ .prose .card p { font-size: var(--text-summary); line-height: 1.7; color: var(--muted); margin: 0 0 20px; }
216
+ .card-link { font-size: var(--text-label); font-weight: 600; margin-top: auto; }
139
217
  .site-footer { border-top: 1px solid var(--line); }
140
- .footer-inner { padding-block: 64px 48px; }
218
+ .footer-inner { padding-block: 64px 80px; }
141
219
  .footer-main { display: grid; grid-template-columns: repeat(calc(var(--footer-columns) + 1), minmax(0, 1fr)); align-items: start; gap: 32px; }
142
220
  .footer-brand { min-width: 0; }
143
- .footer-brand .brand { font-size: 14px; }
221
+ .footer-brand .brand { font-size: var(--text-small); }
144
222
  .footer-brand .brand img { width: 24px; height: 24px; }
145
223
  .footer-columns { grid-column: 2 / -1; display: grid; grid-template-columns: repeat(var(--footer-columns), minmax(0, 1fr)); gap: 32px; }
146
- .footer-column h2 { font-size: 13px; line-height: 24px; font-weight: 500; margin: 0 0 24px; overflow-wrap: anywhere; }
147
- .footer-column ul { display: grid; gap: 8px; padding: 0; margin: 0; list-style: none; }
148
- .footer-column a { display: inline-block; color: var(--muted); font-size: 13px; line-height: 22px; text-decoration: none; overflow-wrap: anywhere; }
149
- .footer-column li { line-height: 22px; }
224
+ .footer-column h2 { font-family: var(--font-family); font-size: var(--text-small); line-height: 1.85; font-weight: 500; margin: 0 0 24px; overflow-wrap: anywhere; }
225
+ .footer-column ul { display: grid; gap: 8px; padding: 0; margin: 0; list-style: none; font-size: var(--text-small); }
226
+ .footer-column a { display: inline-block; color: var(--muted); font-size: var(--text-small); line-height: 1.7; text-decoration: none; overflow-wrap: anywhere; }
227
+ .footer-column li { line-height: 1.7; }
150
228
  .site-footer a:hover { color: #111; }
151
- .footer-bottom { display: flex; align-items: center; flex-wrap: wrap; gap: 16px 24px; margin-top: 64px; color: var(--muted); font-size: 12px; }
152
- .footer-has-columns .footer-bottom { padding-left: calc((100% + 32px) / (var(--footer-columns) + 1)); }
229
+ .footer-bottom { display: flex; align-items: center; flex-wrap: wrap; gap: 16px 24px; margin-top: 64px; color: var(--muted); font-size: var(--text-small); }
153
230
  .footer-bottom a { text-decoration: none; }
231
+ .footer-credit { margin-left: auto; opacity: .65; }
232
+ .footer-credit:is(:hover, :focus-visible) { opacity: 1; }
154
233
  .footer-socials { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
155
234
  .footer-socials a { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; min-height: 28px; }
156
235
  .footer-socials svg { width: 18px; height: 18px; }
157
- .footer-credit { margin-left: auto; }
158
236
  .skip-link { position: absolute; left: 20px; top: -100px; padding: 10px 15px; background: #111; color: white; z-index: 10; }
159
237
  .skip-link:focus { top: 15px; }
160
238
  .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
@@ -165,37 +243,129 @@ h1 { font-size: clamp(calc(var(--heading-size) * .75), 4vw, var(--heading-size))
165
243
  .article-body-with-toc { grid-template-columns: 180px minmax(0, 1fr); gap: 32px; }
166
244
  .footer-main { grid-template-columns: 1fr; gap: 32px; }
167
245
  .footer-columns { grid-column: 1; grid-template-columns: repeat(var(--footer-tablet-columns), minmax(0, 1fr)); gap: 40px 32px; }
168
- .footer-has-columns .footer-bottom { padding-left: 0; }
169
246
  }
170
247
  @media (max-width: 760px) {
171
248
  .article-hero-with-image, .article-body-with-toc { grid-template-columns: minmax(0, 1fr); }
172
249
  .article-hero { margin-bottom: 36px; padding-bottom: 36px; }
173
- .article-sidebar { position: static; max-height: none; overflow: visible; padding-bottom: 32px; border-bottom: 1px solid var(--line); }
250
+ .publication-post .article-sidebar { display: none; }
174
251
  }
175
252
  @media (max-width: 600px) {
176
- .site-header-inner { min-height: 64px; padding-block: 8px; }
177
- .header-actions { gap: 16px; }
253
+ .site-header-inner { width: auto; margin-inline: 24px 10px; min-height: 64px; padding-block: 10px; gap: 12px; }
254
+ .site-header .brand { flex: 1; overflow: hidden; }
255
+ .site-header .brand span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
256
+ .header-actions { flex: none; flex-wrap: nowrap; gap: 8px; }
178
257
  .site-header .desktop-navigation { display: none; }
179
- .header-actions > .header-primary { display: none; }
258
+ .header-actions > .header-primary { display: block; max-width: min(42vw, 200px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
180
259
  .mobile-navigation { display: block; position: static; }
181
- .mobile-navigation nav { position: absolute; top: calc(100% + 8px); right: 0; z-index: 5; display: flex; flex-direction: column; gap: 0; width: min(240px, calc(100vw - 48px)); padding: 8px; border-radius: 8px; background: #fff; box-shadow: 0 6px 24px #0002; }
182
- .mobile-navigation nav a { padding: 10px 12px; overflow-wrap: anywhere; }
260
+ .site-header .mobile-menu-panel { position: fixed; inset: var(--mobile-menu-top, 64px) 0 0; z-index: 5; display: flex; flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 4px; width: 100%; margin: 0; padding: 24px 24px max(32px, env(safe-area-inset-bottom)); overflow-y: auto; overscroll-behavior: contain; background: var(--site-background, #ffffff); }
261
+ .mobile-menu-panel a { flex: none; padding: 12px 0; font-size: var(--text-base); line-height: 1.4; overflow-wrap: anywhere; }
262
+ @media (prefers-reduced-motion: no-preference) { .mobile-navigation[open] .mobile-menu-panel { animation: mobile-menu-enter 180ms ease-out; } }
183
263
  .blog-home { padding-block: 46px 64px; }
264
+ .blog-home:not(:has(> .category-selector:not([hidden]))) > .blog-heading { margin-bottom: 40px; }
184
265
  .category-selector { margin-bottom: 36px; gap: 4px 20px; }
185
266
  .post-list { grid-template-columns: 1fr; gap: 48px; }
186
- .post-list-rows .post-card-link { display: flex; gap: 24px; }
187
- .post-list-rows .post-card h3 { font-size: calc(var(--heading-size) * .4167); }
188
- .archive { margin-top: 48px; }
189
- .archive-list .post-card-content { display: flex; align-items: stretch; gap: 8px; }
190
- .archive-list .post-byline { align-self: start; justify-content: flex-start; }
267
+ [data-home-layout='rows'] .post-list .post-card-layout { display: flex; align-items: stretch; gap: 20px; }
268
+ [data-home-layout='rows'] .post-list .post-card h3 { font-size: var(--text-h4); }
269
+ .archive { margin-top: 72px; }
191
270
  .publication { margin-top: 36px; }
192
271
  .cards { grid-template-columns: 1fr; }
193
272
  .footer-columns { grid-template-columns: repeat(var(--footer-mobile-columns), minmax(0, 1fr)); gap: 32px 24px; }
194
- .footer-inner { padding-block: 40px 32px; }
273
+ .footer-inner { padding-block: 40px 56px; }
195
274
  .footer-bottom { margin-top: 48px; }
196
- .footer-bottom { align-items: start; gap: 18px; }
197
- .footer-credit { flex-basis: 100%; margin: 0; }
275
+ .footer-bottom { gap: 18px; }
276
+ }
277
+ .article-toc a { display: block; container-type: inline-size; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; overflow-wrap: normal; }
278
+ .article-toc .toc-label { display: inline; }
279
+ .article-toc a:is(:hover, :focus-visible) { text-overflow: clip; }
280
+ .article-toc a:is(:hover, :focus-visible) .toc-label { display: inline-block; min-width: 100%; animation: toc-scroll 6s linear infinite alternate; }
281
+ @keyframes toc-scroll {
282
+ 0%, 15% { transform: translateX(0); }
283
+ 85%, 100% { transform: translateX(min(0px, calc(100cqi - 100%))); }
284
+ }
285
+ @media (prefers-reduced-motion: reduce) {
286
+ .article-toc a:is(:hover, :focus-visible) { white-space: normal; overflow-wrap: anywhere; }
287
+ .article-toc a:is(:hover, :focus-visible) .toc-label { display: inline; animation: none; }
288
+ }
289
+ /* Vertical posts share one reading column from the headline through the body. */
290
+ .publication-post[data-post-hero='stacked'] { margin-top: 48px; }
291
+ .publication-post[data-post-hero='stacked'] .article-hero { margin-bottom: 40px; padding-bottom: 0; border-bottom: 0; }
292
+ .publication-post[data-post-hero='stacked'] .article-intro { max-width: var(--post-content-width); margin-inline: auto; text-align: left; }
293
+ .publication-post[data-post-hero='stacked'] h1 { margin-bottom: 0; line-height: 1.12; }
294
+ .publication-post[data-post-hero='stacked'] .article-intro .description { margin: 16px 0 0; }
295
+ .publication-post[data-post-hero='stacked'] .byline { justify-content: flex-start; margin-top: 24px; }
296
+ .publication-post[data-post-hero='stacked'] .byline :is(.post-byline, .post-authors) { justify-content: flex-start; }
297
+ .publication-post[data-post-hero='stacked'] .article-hero-image { margin: 32px auto 0; }
298
+ .publication-post[data-post-hero='stacked'] .article-hero-image img { border-radius: calc(var(--image-short-side, 200px) * var(--image-radius, 0.02)); }
299
+ .publication-post[data-post-hero='stacked'] .article-body { display: block; max-width: var(--post-content-width); }
300
+ .publication-post[data-post-hero='stacked'] .article-sidebar { position: static; max-height: none; overflow: visible; margin: 0 0 32px; padding: 0; border: 0; }
301
+ .toc-trigger { display: none; }
302
+ .publication-post[data-post-hero='stacked'] .article-sidebar[data-floating-toc] { position: fixed; z-index: 20; left: 8px; top: 50%; transform: translateY(-50%); margin: 0; }
303
+ .publication-post[data-post-hero='stacked'] .toc-trigger { display: flex; flex-direction: column; align-items: center; gap: 0; max-height: min(60dvh, 320px); overflow: hidden; width: 28px; padding: 8px 6px; border: 0; border-radius: 6px; background: transparent; color: #414146; cursor: pointer; }
304
+ .toc-trigger span { position: relative; display: block; flex: none; width: 16px; height: 12px; }
305
+ .toc-trigger span::after { content: ''; position: absolute; left: 0; top: 5px; width: 100%; height: 2px; border-radius: 2px; background: currentColor; opacity: .3; transform: scaleX(.5); transform-origin: left center; transition: transform 140ms ease-out, opacity 140ms ease-out; }
306
+ .toc-trigger:is([aria-expanded='true'], :hover, :focus-visible) span::after { opacity: .38; transform: scaleX(.5625); }
307
+ .toc-trigger span[data-active]::after, .toc-trigger:is([aria-expanded='true'], :hover, :focus-visible) span[data-active]::after { opacity: .5; transform: scaleX(.625); }
308
+ .toc-trigger:is([aria-expanded='true'], :hover, :focus-visible) span:hover::after { opacity: 1; transform: scaleX(1); }
309
+ .toc-trigger:focus-visible { outline: 2px solid #b8b8c0; outline-offset: 1px; }
310
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc { visibility: hidden; opacity: 0; position: absolute; left: 100%; top: 50%; transform: translate(-4px, -50%) scale(.98); transform-origin: left center; width: min(240px, calc(100vw - 60px)); max-height: calc(100dvh - 48px); overflow-y: auto; overscroll-behavior: contain; padding: 5px; margin-left: 6px; border: 0; border-radius: 9px; background: var(--site-background, #fff); box-shadow: 0 6px 18px rgb(0 0 0 / 2%), 0 3px 9px rgb(0 0 0 / 4%), 0 1px 1px rgb(0 0 0 / 4%); transition: opacity 120ms ease-out, transform 140ms ease-out; }
311
+ .publication-post[data-post-hero='stacked'] [data-floating-toc]::after { content: ''; position: absolute; left: 100%; top: 0; width: 6px; height: 100%; }
312
+ .publication-post[data-post-hero='stacked'] [data-toc-open] .article-toc { visibility: visible; opacity: 1; transform: translate(0, -50%) scale(1); }
313
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc h2 { margin: 0 0 3px; padding: 6px 8px 4px; color: #87878e; font-size: 11px; line-height: 16px; font-weight: 500; letter-spacing: 0; text-transform: none; }
314
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc li + li { margin-top: 4px; }
315
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc a { padding: 5px 8px; border-radius: 5px; color: #414146; font-size: var(--text-label); line-height: 1.4; white-space: normal; overflow-wrap: anywhere; text-decoration: none; transition: background-color 120ms ease; }
316
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc a[aria-current='location'] { color: #303034; font-weight: 500; background: color-mix(in srgb, var(--site-background, #fff), #000 5%); }
317
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc a:is(:hover, :focus-visible) { background: #fafafa; outline: none; }
318
+ .publication-post[data-post-hero='stacked'] [data-floating-toc] .toc-label { display: inline; animation: none; }
319
+ @media (prefers-reduced-motion: reduce) {
320
+ .toc-trigger span::after, .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc, .publication-post[data-post-hero='stacked'] [data-floating-toc] .article-toc a { transition: none; }
321
+ }
322
+ .article-toc a[aria-current='location'] { color: #111; font-weight: 500; }
323
+ @media (min-width: 1200px) {
324
+ .publication-post[data-post-hero='split'][data-post-toc='visible'] .article-body-with-toc { grid-template-columns: minmax(180px, 1fr) minmax(0, var(--post-content-width)) minmax(180px, 1fr); max-width: none; }
325
+ .publication-post[data-post-hero='split'] .article-sidebar { width: 180px; }
326
+ }
327
+ @media (max-width: 900px) {
328
+ /* Keep the floating control clear of the reading column on small screens. */
329
+ .publication-post[data-post-hero='stacked'] .article-sidebar[data-floating-toc] { left: 0; }
330
+ .publication-post[data-post-hero='stacked'] .toc-trigger { width: 20px; padding-inline: 2px; }
331
+ }
332
+ @media (max-width: 600px) {
333
+ .publication-post[data-post-hero='stacked'] { margin-top: 32px; }
334
+ .publication-post[data-post-hero='stacked'] .article-hero { margin-bottom: 32px; }
335
+ .publication-post[data-post-hero='stacked'] .byline { gap: 8px 16px; margin-top: 20px; }
198
336
  }
199
337
  @media (prefers-reduced-motion: reduce) {
200
338
  *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; }
201
339
  }
340
+
341
+ .prose figure { margin: 1.3em 0; }
342
+ .prose figure:has(img) { margin-block: 32px; }
343
+ .prose figure img, .prose figure > p:has(img) { margin-block: 0; }
344
+ .prose figure :is(img, video, audio) { display: block; max-width: 100%; }
345
+ .prose figure > a { display: block; }
346
+ .prose figcaption { margin-top: 4px; color: var(--muted, #777); font-size: 13px; line-height: 1.5; text-align: center; }
347
+ .prose figcaption p { margin: 0; }
348
+
349
+ /* Homepage width and container-responsive layouts. */
350
+
351
+ .blog-home { container: home-layout / inline-size; width: min(var(--home-content-width, 1280px), calc(100% - 64px)); }
352
+ @media (max-width: 900px) {
353
+ .blog-home { width: min(var(--home-content-width, 1280px), calc(100% - 48px)); }
354
+ }
355
+ @container home-layout (max-width: 852px) {
356
+ .archive-list .post-card-content { grid-template-columns: minmax(0, 1fr); align-items: start; gap: 10px; }
357
+ .archive-list h3 { grid-area: 1 / 1; }
358
+ .archive-list .post-card-meta { grid-area: 2 / 1; }
359
+ [data-home-layout='grid'] .post-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 48px 32px; }
360
+ }
361
+ @container home-layout (max-width: 552px) {
362
+ [data-home-layout='grid'] .post-list { grid-template-columns: 1fr; gap: 48px; }
363
+ [data-home-layout='grid'] .archive-list .post-card-layout { padding-block: 12px; }
364
+ [data-home-layout='grid'] .archive-list .post-card-content { grid-template-columns: minmax(0, 1fr) 14px; align-items: center; gap: 12px; }
365
+ [data-home-layout='grid'] .archive-list .post-card-meta { display: none; }
366
+ [data-home-layout='grid'] .archive-list h3 { min-width: 0; overflow: hidden; white-space: nowrap; text-wrap: nowrap; text-overflow: ellipsis; font-family: var(--font-family); font-size: var(--text-summary); font-weight: 400; letter-spacing: normal; line-height: 1.55; }
367
+ [data-home-layout='grid'] .archive-list .post-card-content::after { content: ''; grid-area: 1 / 2; justify-self: center; width: 6px; height: 6px; border-top: 1.5px solid #c5c5ca; border-right: 1.5px solid #c5c5ca; transform: rotate(45deg); pointer-events: none; }
368
+ [data-home-layout='rows'] .post-list .post-card-layout { display: flex; align-items: stretch; gap: 20px; }
369
+ [data-home-layout='rows'] .post-list .post-card h3 { font-size: var(--text-h4); }
370
+
371
+ }
@@ -1,29 +0,0 @@
1
- import { cp, mkdir, rm } from 'node:fs/promises';
2
- import path from 'node:path';
3
- // Publication images live at remote URLs. Keep other public files (robots.txt,
4
- // downloads, etc.) available without copying images into each deployment.
5
- const imageFile = /\.(?:avif|bmp|gif|heic|heif|ico|jpe?g|png|svg|tiff?|webp)$/i;
6
- export async function copyPublicAssets(contentRoot, workspace) {
7
- await mkdir(path.join(workspace, 'public'), { recursive: true });
8
- try {
9
- await cp(path.join(contentRoot, 'public'), path.join(workspace, 'public'), {
10
- recursive: true, filter: source => !imageFile.test(source),
11
- });
12
- }
13
- catch (error) {
14
- if (error.code !== 'ENOENT')
15
- throw error;
16
- }
17
- }
18
- export async function updatePublicAsset(contentRoot, workspace, file, event) {
19
- const relative = path.relative(path.join(contentRoot, 'public'), file);
20
- if (!relative || relative.startsWith('..') || path.isAbsolute(relative) || imageFile.test(relative))
21
- return;
22
- const destination = path.join(workspace, 'public', relative);
23
- if (event === 'unlink' || event === 'unlinkDir')
24
- await rm(destination, { recursive: true, force: true });
25
- else if (event === 'add' || event === 'change') {
26
- await mkdir(path.dirname(destination), { recursive: true });
27
- await cp(file, destination);
28
- }
29
- }