@lettercamp/cli 0.1.10 → 0.1.11

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.
@@ -193,4 +193,40 @@ export const fontFallbacks = {
193
193
  { "weight": 600, "local": ["Arial Bold", "Arial-BoldMT"], "sizeAdjust": "97.2956%", "ascentOverride": "101.1351%", "descentOverride": "32.4783%", "lineGapOverride": "0.0000%" },
194
194
  { "weight": 700, "local": ["Arial Bold", "Arial-BoldMT"], "sizeAdjust": "99.5874%", "ascentOverride": "98.8077%", "descentOverride": "31.7309%", "lineGapOverride": "0.0000%" },
195
195
  ] },
196
+ "Geist Mono": { generic: "monospace", faces: [
197
+ { "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
198
+ { "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
199
+ { "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
200
+ { "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "100.5164%", "descentOverride": "29.5048%", "lineGapOverride": "0.0000%" },
201
+ ] },
202
+ "JetBrains Mono": { generic: "monospace", faces: [
203
+ { "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
204
+ { "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
205
+ { "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
206
+ { "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.0166%", "descentOverride": "30.0049%", "lineGapOverride": "0.0000%" },
207
+ ] },
208
+ "Fira Code": { generic: "monospace", faces: [
209
+ { "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
210
+ { "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
211
+ { "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
212
+ { "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "99.0161%", "descentOverride": "32.2052%", "lineGapOverride": "0.0000%" },
213
+ ] },
214
+ "Roboto Mono": { generic: "monospace", faces: [
215
+ { "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
216
+ { "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
217
+ { "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
218
+ { "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "100.0000%", "ascentOverride": "104.7852%", "descentOverride": "27.0996%", "lineGapOverride": "0.0000%" },
219
+ ] },
220
+ "IBM Plex Mono": { generic: "monospace", faces: [
221
+ { "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
222
+ { "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
223
+ { "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
224
+ { "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "102.5167%", "descentOverride": "27.5045%", "lineGapOverride": "0.0000%" },
225
+ ] },
226
+ "Source Code Pro": { generic: "monospace", faces: [
227
+ { "weight": 400, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
228
+ { "weight": 500, "local": ["Courier New", "CourierNewPSMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
229
+ { "weight": 600, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
230
+ { "weight": 700, "local": ["Courier New Bold", "CourierNewPS-BoldMT"], "sizeAdjust": "99.9837%", "ascentOverride": "98.4160%", "descentOverride": "27.3044%", "lineGapOverride": "0.0000%" },
231
+ ] },
196
232
  };
@@ -1,11 +1,14 @@
1
1
  // Generated from lettercamp/lib/font-resources.ts.
2
2
  import { fontFallbacks } from "./font-fallbacks.js";
3
- export function fontResources(typography = {}) {
3
+ import { fontCatalog } from "./font-catalog.js";
4
+ export function fontResources(typography = {}, headingWeight = 500) {
4
5
  const quote = (value) => '"' + Array.from(String(value), char => /[a-zA-Z0-9 -]/.test(char) ? char : '\\' + char.codePointAt(0).toString(16) + ' ').join('') + '"';
5
6
  const google = new Set();
7
+ const resolved = new Set();
8
+ const preloads = new Set();
6
9
  const custom = new Map();
7
10
  const faces = [];
8
- const resolve = (input) => {
11
+ const resolve = (input, criticalWeight) => {
9
12
  const value = input && typeof input === "object" ? input : undefined;
10
13
  if (value && typeof value.url === 'string' && !(typeof value.google === 'string' && value.google.trim())) {
11
14
  try {
@@ -24,25 +27,45 @@ export function fontResources(typography = {}) {
24
27
  catch { /* Invalid custom URLs use the default family. */ }
25
28
  }
26
29
  const family = value && typeof value.google === 'string' && value.google.trim() ? value.google.trim() : 'Inter';
30
+ const catalog = Object.hasOwn(fontCatalog, family) ? fontCatalog[family] : undefined;
31
+ if (catalog) {
32
+ if (!resolved.has(family))
33
+ for (const face of catalog.faces) {
34
+ faces.push('@font-face{font-family:' + quote(family) + ';src:url(' + quote(face.url) + ') format("woff2");font-weight:' + face.weight
35
+ + ';font-style:normal;font-display:swap;unicode-range:' + face.unicodeRange + ';}');
36
+ }
37
+ // Prioritize only the title and body Latin files. Other subsets load on demand.
38
+ const distance = (weight) => {
39
+ const [min, max = min] = weight.split(' ').map(Number);
40
+ return Math.max(min - criticalWeight, criticalWeight - max, 0);
41
+ };
42
+ const critical = catalog.faces.filter(face => face.subset === 'latin').sort((a, b) => distance(a.weight) - distance(b.weight))[0];
43
+ if (critical)
44
+ preloads.add(critical.url);
45
+ }
46
+ else {
47
+ // Preserve support for existing CLI settings naming fonts outside our picker.
48
+ google.add(family);
49
+ }
27
50
  const fallback = Object.hasOwn(fontFallbacks, family) ? fontFallbacks[family] : undefined;
28
51
  if (fallback) {
29
52
  const alias = family + ' Fallback';
30
- if (!google.has(family))
53
+ if (!resolved.has(family))
31
54
  for (const face of fallback.faces) {
32
55
  faces.push('@font-face{font-family:' + quote(alias) + ';src:' + face.local.map(name => 'local(' + quote(name) + ')').join(',')
33
56
  + ';font-weight:' + face.weight + ';font-style:normal;size-adjust:' + face.sizeAdjust
34
57
  + ';ascent-override:' + face.ascentOverride + ';descent-override:' + face.descentOverride + ';line-gap-override:' + face.lineGapOverride + ';}');
35
58
  }
36
- google.add(family);
59
+ resolved.add(family);
37
60
  return quote(family) + ', ' + quote(alias) + ', ' + fallback.generic;
38
61
  }
39
- google.add(family);
62
+ resolved.add(family);
40
63
  return quote(family) + ', Arial, Helvetica, sans-serif';
41
64
  };
42
- const headingFamily = resolve(typography.headingFont);
43
- const bodyFamily = resolve(typography.bodyFont);
65
+ const headingFamily = resolve(typography.headingFont, headingWeight);
66
+ const bodyFamily = resolve(typography.bodyFont, 400);
44
67
  return {
45
- headingFamily, bodyFamily, css: faces.join('\n'),
68
+ headingFamily, bodyFamily, css: faces.join('\n'), preloads: [...preloads],
46
69
  googleUrl: google.size ? 'https://fonts.googleapis.com/css2?' + [...google].map(family => 'family=' + encodeURIComponent(family) + ':wght@' + (Object.hasOwn(fontFallbacks, family) ? fontFallbacks[family].faces.map(face => face.weight).join(';') : '400;500;600;700')).join('&') + '&display=swap' : null,
47
70
  };
48
71
  }
@@ -7,7 +7,4 @@ export const imageWidthStyles = `
7
7
  .publication-post .article-hero-image:not([data-image-width="regular"]):not([data-image-width="full"]) { width: min(100%, calc(var(--post-content-width) + 128px)); max-width: none; }
8
8
  .publication-post .article-hero-image[data-image-width="full"] { width: 100%; max-width: none; }
9
9
  .publication-post .article-hero-image[data-image-width="regular"] { width: min(100%, var(--cover-image-width, var(--post-content-width)), var(--post-content-width)); max-width: none; }
10
- .publication-post .article-hero:has(.article-hero-image:not([data-image-width="regular"])) { display: block; }
11
- .publication-post .article-hero:has(.article-hero-image:not([data-image-width="regular"])) .article-intro { max-width: var(--post-content-width); margin-inline: auto; }
12
- .publication-post .article-hero:has(.article-hero-image:not([data-image-width="regular"])) .article-hero-image { margin-top: 32px; }
13
10
  `;
@@ -0,0 +1,25 @@
1
+ // Generated from lettercamp/lib/key-takeaways.ts.
2
+ // Shared by the page editor, draft preview, and published renderer.
3
+ export const keyTakeawaysStyles = `
4
+ [data-lettercamp-key-takeaways] {
5
+ box-sizing: border-box; margin-block: 24px; padding: 20px 24px;
6
+ border-radius: 8px; background: var(--site-surface, #fafafa); color: inherit;
7
+ font: inherit; overflow-wrap: anywhere;
8
+ }
9
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > :is(h1, h2, h3, h4, h5, h6) {
10
+ margin: 0 0 10px; padding: 0;
11
+ }
12
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] :is(ul, ol) {
13
+ margin: 0; padding: 0 0 0 1.4em;
14
+ }
15
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] li {
16
+ margin: 6px 0; padding: 0 0 0 .2em;
17
+ }
18
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] p { margin: 0; padding: 0; min-height: 0; }
19
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > p:first-child { margin-bottom: 10px; }
20
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > :last-child,
21
+ [data-lettercamp-key-takeaways][data-lettercamp-key-takeaways] > :last-child > li:last-child { margin-bottom: 0; }
22
+ @media (max-width: 600px) {
23
+ [data-lettercamp-key-takeaways] { padding: 16px 18px; }
24
+ }
25
+ `;
@@ -31,6 +31,16 @@ export const postShareScript = `
31
31
  const ensureTrigger = () => {
32
32
  const byline = document.querySelector('.publication-post .byline');
33
33
  if (byline && !byline.querySelector('.post-share-button')) byline.insertAdjacentHTML('beforeend', triggerMarkup);
34
+ const intro = document.querySelector('.publication-post .article-intro');
35
+ if (!intro) return;
36
+ let row = intro.querySelector('.article-meta-row');
37
+ if (!row) {
38
+ row = document.createElement('div'); row.className = 'article-meta-row';
39
+ const category = intro.querySelector('.article-category');
40
+ if (category) row.append(category);
41
+ intro.prepend(row);
42
+ }
43
+ if (!row.querySelector('.post-share-button')) row.insertAdjacentHTML('beforeend', triggerMarkup);
34
44
  };
35
45
  const action = (tag, name, label, graphic) => '<' + tag + ' class="post-share-action" data-share="' + name + '"' + (tag === 'button' ? ' type="button"' : '') + '><span class="post-share-action-icon">' + graphic + '</span><span data-label>' + label + '</span></' + tag + '>';
36
46
  const external = (link, href) => {
@@ -0,0 +1,42 @@
1
+ // Generated from lettercamp/lib/site-colors.ts.
2
+ // Shared by published sites and live previews. Keep this function self-contained:
3
+ // the preview bridge embeds it in the publication iframe.
4
+ export function siteColors(design?: { backgroundColor?: unknown; accentColor?: unknown }): Record<string, string> {
5
+ const [background, accent] = [design?.backgroundColor, design?.accentColor].map((value, index) =>
6
+ typeof value === "string" && /^#[0-9a-f]{6}$/i.test(value) ? value.toLowerCase() : index === 0 ? "#ffffff" : "");
7
+ const [text, accentText] = [background, accent || background].map(color => {
8
+ const linear = [1, 3, 5].map(offset => {
9
+ const channel = parseInt(color.slice(offset, offset + 2), 16) / 255;
10
+ return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
11
+ });
12
+ const luminance = linear[0] * 0.2126 + linear[1] * 0.7152 + linear[2] * 0.0722;
13
+ return (luminance + 0.05) / 0.05 >= 1.05 / (luminance + 0.05) ? "#000000" : "#ffffff";
14
+ });
15
+ const base = [1, 3, 5].map(offset => parseInt(background.slice(offset, offset + 2), 16));
16
+ const target = text === "#000000" ? 0 : 255;
17
+ // Reference grays on white. Retain each shade's distance along the
18
+ // background-to-foreground scale; e.g. 250 preserves #fafafa on white.
19
+ const shades: Record<string, number> = {
20
+ "surface": 250,
21
+ "surface-hover": 245,
22
+ "surface-active": 238,
23
+ "border": 240,
24
+ "border-strong": 221,
25
+ "text-strong": 65,
26
+ "text-secondary": 111,
27
+ "text-muted": 134,
28
+ "text-faint": 180,
29
+ };
30
+ const colors: Record<string, string> = {
31
+ "color-scheme": target === 255 ? "dark" : "light",
32
+ "--site-background": background,
33
+ "--site-foreground": text,
34
+ "--site-accent": accent || "inherit",
35
+ "--site-accent-text": accent ? accentText : "inherit",
36
+ "--site-code-tint": target === 255 ? "45%" : "0%",
37
+ };
38
+ for (const [name, gray] of Object.entries(shades)) {
39
+ colors[`--site-${name}`] = "#" + base.map(channel => Math.round(channel + (target - channel) * (1 - gray / 255)).toString(16).padStart(2, "0")).join("");
40
+ }
41
+ return colors;
42
+ }
@@ -1,12 +1,12 @@
1
1
  // Generated from lettercamp/lib/table-styles.ts.
2
2
  // Shared by the editable table, safe preview, and published renderer.
3
3
  export const tableStyles = `
4
- [data-lettercamp-table] { position: relative; margin-block: 24px 32px; color: #303034; font: inherit; }
4
+ [data-lettercamp-table] { position: relative; margin-block: 24px 32px; color: var(--site-text-strong, #303034); font: inherit; }
5
5
  [data-lettercamp-table] > table { width: 100%; border-collapse: collapse; table-layout: fixed; border-spacing: 0; font: inherit; color: inherit; }
6
- [data-lettercamp-table] > table > tbody > tr > :is(td, th) { position: relative; border: 1px solid #f0f0f0; padding: 0; min-width: 0; vertical-align: top; font: inherit; text-align: left; }
6
+ [data-lettercamp-table] > table > tbody > tr > :is(td, th) { position: relative; border: 1px solid var(--site-border, #f0f0f0); padding: 0; min-width: 0; vertical-align: top; font: inherit; text-align: left; }
7
7
  [data-lettercamp-table] [data-table-content] { display: block; box-sizing: border-box; min-height: calc(1.8em + 16px); padding: 8px 12px; line-height: inherit; overflow-wrap: anywhere; white-space: pre-wrap; outline: none; }
8
8
  [data-lettercamp-table] [data-table-cell-height] [data-table-content] { height: var(--table-cell-height); min-height: 0; overflow: auto; }
9
9
  [data-lettercamp-table] :is(td, th) > p, [data-lettercamp-table] [data-table-content] > p { margin: 0; padding: 0; min-height: 0; }
10
- [data-lettercamp-table] [data-table-content] code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; padding: .18em .35em; background: #f3f3f3; border-radius: 3px; }
10
+ [data-lettercamp-table] [data-table-content] code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; padding: .18em .35em; background: var(--site-surface-hover, #f3f3f3); border-radius: 3px; }
11
11
  [data-lettercamp-table] [data-table-content] a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
12
12
  `;
@@ -35,9 +35,12 @@ const coverWidth = /^[0-9]+$/.test(String(frontmatter.coverWidth)) && Number(fro
35
35
  data-post-cover={postLayout.showCover === false ? 'hidden' : 'visible'} data-post-toc={postLayout.showTableOfContents === false ? 'hidden' : 'visible'}>
36
36
  <header class:list={['article-header', 'article-hero', { 'article-hero-with-image': coverImage }]}>
37
37
  <div class="article-intro">
38
+ <div class="article-meta-row">
38
39
  {category && <p class="article-category">
39
40
  <a href={categoryHref}>{category}</a>
40
41
  </p>}
42
+ <Fragment set:html={postShareButton} />
43
+ </div>
41
44
  <h1>{frontmatter.title ?? site.name}</h1>
42
45
  {frontmatter.description && <p class="description">{frontmatter.description}</p>}
43
46
  <div class="byline">
@@ -1,56 +1,73 @@
1
1
  .byline:has(.post-share-button) { display: grid; grid-template-columns: minmax(0, 1fr) auto; width: 100%; }
2
2
  .byline:has(.post-share-button) .post-byline { grid-column: 1; grid-row: 1; min-width: 0; }
3
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; }
4
+ .article-meta-row { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
5
+ .article-meta-row:not(:has(.article-category)) { display: none; }
6
+ .article-meta-row .article-category { min-width: 0; margin: 0; }
7
+ .article-meta-row > .post-share-button { display: none; }
8
+ .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: var(--site-surface-hover); color: var(--site-text-strong); font-size: 13px; line-height: 1.4; }
9
+ .post-share-button:hover { background: var(--site-surface-active); color: var(--site-foreground); }
6
10
  .post-share-button svg { width: 15px; height: 15px; }
7
11
  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); }
12
+ .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: var(--site-foreground); box-shadow: 0 20px 80px #0003; font: 13px/1.5 var(--font-family); }
9
13
  .post-share-dialog::backdrop { background: #0006; }
10
14
  .post-share-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
11
15
  .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; }
16
+ .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: var(--site-text-secondary); }
17
+ .post-share-close:hover { color: var(--site-foreground); }
14
18
  .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; }
19
+ .post-share-preview { overflow: hidden; border: 1px solid var(--site-border); border-radius: 8px; background: var(--site-surface-hover); color: var(--site-foreground); font-family: Arial, Helvetica, sans-serif; }
16
20
  .post-share-image { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; }
17
21
  .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; }
22
+ .post-share-domain { margin: 0 0 4px; color: var(--site-text-secondary); font-size: 10px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
19
23
  .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; }
24
+ .post-share-description { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 4px 0 0; color: var(--site-text-secondary); font-size: 11px; line-height: 1.4; overflow-wrap: anywhere; }
21
25
  .post-share-description:empty { display: none; }
22
26
  .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 { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; padding: 0; border: 0; background: transparent; color: var(--site-text-strong); font-size: 11px; line-height: 1.4; text-decoration: none; }
28
+ .post-share-action-icon { display: grid; place-items: center; width: 100%; height: 40px; border-radius: 10px; background: var(--site-surface); }
29
+ .post-share-action:hover, .post-share-action[aria-expanded='true'] { color: var(--site-foreground); }
30
+ .post-share-action:hover .post-share-action-icon, .post-share-action[aria-expanded='true'] .post-share-action-icon { background: var(--site-surface-hover); }
27
31
  .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); }
32
+ .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 var(--site-border); border-radius: 10px; background: var(--site-surface); color: var(--site-text-strong); box-shadow: 0 6px 24px #0002; font: 12px/1.4 var(--font-family); }
29
33
  .post-share-menu::backdrop { background: transparent; }
30
34
  .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-menu-item:is(:hover, :focus-visible) { background: var(--site-surface-active); color: var(--site-foreground); outline: none; }
36
+ .post-share-menu-item svg { width: 15px; height: 15px; color: var(--site-text-muted); }
37
+ .post-share-menu-item[data-share='native'] { margin-top: 4px; border-top: 1px solid var(--site-border); border-radius: 0; }
38
+ .post-share-summaries { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--site-border); }
35
39
  .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; }
40
+ .post-share-summaries > p { margin: 3px 0 9px; color: var(--site-text-secondary); font-size: 12px; }
37
41
  .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-provider { display: flex; align-items: center; justify-content: space-between; gap: 5px; padding: 7px 9px; border-radius: 7px; background: var(--site-surface); font-size: 12px; text-decoration: none; }
43
+ .post-share-provider:hover { background: var(--site-surface-hover); }
44
+ .post-share-provider svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--site-text-muted); }
45
+ .post-share-status { margin: 12px 0 0; color: var(--site-text-secondary); font-size: 12px; text-align: center; }
42
46
  .post-share-status:empty { display: none; }
43
47
  .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; }
48
+ .post-share-copy-fallback label { display: block; margin-bottom: 4px; color: var(--site-text-secondary); font-size: 12px; }
49
+ .post-share-copy-fallback input { width: 100%; min-width: 0; padding: 8px 10px; border: 1px solid var(--site-border-strong); border-radius: 8px; background: var(--site-background); color: var(--site-text-strong); font: inherit; }
50
+ @media (max-width: 600px) {
51
+ .article-meta-row, .article-meta-row:not(:has(.article-category)) { display: flex; }
52
+ .article-meta-row > .post-share-button { display: inline-flex; }
53
+ .byline:has(.post-share-button) { grid-template-columns: minmax(0, 1fr); }
54
+ .byline > .post-share-button { display: none; }
55
+ .byline .post-byline { flex-wrap: wrap; gap: 6px 16px; overflow: visible; white-space: normal; }
56
+ .byline .post-authors { order: -1; flex: 0 0 100%; min-width: 0; white-space: nowrap; }
49
57
  }
50
58
  @media (max-width: 400px) {
51
59
  .post-share-provider { padding: 7px; font-size: 11px; }
52
60
  .byline:has(.post-share-button) { column-gap: 10px; }
53
61
  }
62
+ /* Split headers keep metadata above authors, with sharing centered beside both rows. */
63
+ .publication-post[data-post-hero='split'] .byline { display: grid; grid-template-columns: max-content minmax(0, 1fr) auto; gap: 6px 16px; }
64
+ .publication-post[data-post-hero='split'] .byline .post-byline { display: contents; }
65
+ .publication-post[data-post-hero='split'] .byline .post-byline > :is(time, .reading-time) { grid-row: 1; }
66
+ .publication-post[data-post-hero='split'] .byline > .post-share-button { display: inline-flex; grid-column: 3; grid-row: 1 / 3; align-self: center; }
67
+ .publication-post[data-post-hero='split'] .byline .post-authors { grid-column: 1 / 3; grid-row: 2; min-width: 0; order: 0; }
68
+ .publication-post[data-post-hero='split'] .byline .author-names { flex: 1; min-width: 0; white-space: normal; }
69
+ .publication-post[data-post-hero='split'] .article-meta-row > .post-share-button { display: none; }
70
+ .publication-post[data-post-hero='split'] .article-meta-row:not(:has(.article-category)) { display: none; }
54
71
  @media (prefers-reduced-motion: no-preference) {
55
72
  .post-share-dialog[open] { animation: post-share-enter 140ms ease-out; }
56
73
  @keyframes post-share-enter { from { opacity: 0; transform: translateY(6px) scale(.98); } to { opacity: 1; transform: none; } }