History

Add marketing blog with first post on MCP

Add an Astro content-collection blog to the marketing site: - /blog index listing posts and /blog/[slug] prerendered post pages - BlogPostLayout with prose styling and a top-of-post author card (round photo avatar, name, role + date) linking to the author's X - authorHandle/authorRole/authorAvatar fields on the blog schema with sensible defaults - Blog nav links in the homepage header and footer First post: "Why MCP didn't work, what's next".

Rhys Sullivan committed 4 months ago

Commit 66134b81 parent 78aa8718 files · +563 −-1Browse files

Unified · Split

8 files
  1. apps/marketing/public/authors/rhys-sullivan.png +0 −0
  2. apps/marketing/src/content.config.ts +24 −0
  3. apps/marketing/src/content/blog/why-mcp-didnt-work-whats-next.md +46 −0
  4. apps/marketing/src/layouts/BlogPostLayout.astro +182 −0
  5. apps/marketing/src/pages/blog/[...slug].astro +28 −0
  6. apps/marketing/src/pages/blog/index.astro +144 −0
  7. apps/marketing/src/pages/index.astro +6 −0
  8. apps/marketing/src/styles/global.css +134 −0
apps/marketing/public/authors/rhys-sullivan.png+0−0

Binary file not shown.

apps/marketing/src/content.config.ts+24−0
@@ -0,0 +1,24 @@
1import { defineCollection, z } from "astro:content";
2import { glob } from "astro/loaders";
3
4// Blog posts authored as Markdown under src/content/blog. The glob loader
5// turns each file into a collection entry whose `id` is the slug used in the
6// /blog/[...slug] route (filename without extension).
7const blog = defineCollection({
8 loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
9 schema: z.object({
10 title: z.string(),
11 description: z.string(),
12 date: z.coerce.date(),
13 author: z.string().default("Rhys Sullivan"),
14 // X/Twitter handle (without the leading @), a one-line role, and a round
15 // avatar image, used by the author card at the top of each post. The
16 // avatar path is served from public/ (e.g. /authors/rhys-sullivan.png).
17 authorHandle: z.string().default("RhysSullivan"),
18 authorRole: z.string().default("Founder of Executor"),
19 authorAvatar: z.string().default("/authors/rhys-sullivan.png"),
20 draft: z.boolean().default(false),
21 }),
22});
23
24export const collections = { blog };
apps/marketing/src/content/blog/why-mcp-didnt-work-whats-next.md+46−0
@@ -0,0 +1,46 @@
1---
2title: "Why MCP didn't work, what's next"
3description: "MCP shipped before anyone knew how to wire tools to agents, and it caught the blame for it. What actually went wrong, why CLIs aren't the answer, and where MCP goes from here."
4date: 2026-06-25
5author: "Rhys Sullivan"
6---
7
8MCP launched when the best models we had were Claude 3.5 Sonnet and GPT-4o. At the time, we did not know how to build with these tools. We were paranoid about security, context bloat, and granting access to models.
9
10Consequently, early MCP servers were useless. Vercel's launch MCP server had only 7 tools and limited utility. Paranoid about security, companies restricted access to the point of making the servers unviable.
11
12When Claude Code launched in February, developers realized agents just needed a bash tool. Giving agents bash access let them chain commands, filter output, and dynamically install CLIs. This shift led to a common belief: the fewer tools a model has, the better it performs. That belief is false. The GitHub CLI contains thousands of tools, especially since a model can call the API directly using `gh api`.
13
14Compare using MCP to using a CLI. With a CLI, you tell the agent to install the package and use it immediately. Most MCP harnesses require a full client restart to register a new tool, creating significant friction. Bash benefits from 37 years of development on stable primitives, while developers rushed out most MCP implementations in a month.
15
16MCP is in an awkward state. Vercel added `vercel api` to its CLI, giving agents access to the entire API. Yet Vercel's MCP server remains restricted to 20 tools, most of which are read-only. Additionally, only 16 clients are authorized to integrate with it. This friction makes no sense when developers routinely run the CLI in environments with skipped permissions.
17
18Should we abandon MCP for CLIs? I disagree. Most CLIs merely wrap APIs. Focusing on CLI authentication while neglecting API authentication introduces unnecessary statefulness. An agent reading emails should not spin up a container to run the Google Workspace CLI when lazy tool loading or code mode works.
19
20CLIs also fail to map the action space. You can convert an MCP tool into a CLI, but you cannot easily reverse the process. CLIs also lack the explicit annotations for destructive actions found in MCP or OpenAPI specifications, though improving LLMs make this less critical.
21
22## Myths surrounding MCP today
23
24**"Lots of tools bloat the context."** This is only true for poorly implemented clients. Most modern clients use lazy loading, letting the model discover tools on demand.
25
26**"Poor support of the spec."** While the spec is not fully supported everywhere, developers can work around missing features. We must solve this adoption chicken-and-egg problem.
27
28**"Auth sucks."** Some clients have poor authorization support. However, in Claude Code, I never get signed out.
29
30**"We should just be calling the APIs directly."** I agree. I founded [executor.sh](https://executor.sh) on this premise. Harnesses should support any integration method, whether it is MCP, CLI, API, or GraphQL. Executor functions by accepting any input format and converting it into a standard tool catalog.
31
32## Where do we go from here?
33
34Harness builders like Codex, Claude Code, and OpenCode will decide whether to commit to MCP. If these teams collaborate on an alternative specification with matching adoption, we should welcome it. From my perspective, MCP's broad adoption has solved the hardest challenge. Now we must focus on refining the implementation.
35
36I am optimistic about the future of MCP. Stateless elicitation is powerful, and client adoption of code mode is rising. MCP applications will unlock new interaction patterns over the next six months. Skill loading through MCP will also provide clear context on how to call remote sources.
37
38Ideally, companies will support CIMD OAuth for both their MCP servers and OpenAPI specifications. Developers can use the OpenAPI spec for raw data access and bulk processing, while the MCP server provides a curated experience using an `api` tool modeled after the Vercel and GitHub CLIs.
39
40## Some closing thoughts
41
42Avoid plugins. They create vendor lock-in and portability issues, resulting in closed ecosystems.
43
44This technology is new, and criticizing it is easier than building it. MCP has succeeded in driving widespread adoption.
45
46Ultimately, most MCP servers and CLIs are just wrappers for direct API calls.
apps/marketing/src/layouts/BlogPostLayout.astro+182−0
@@ -0,0 +1,182 @@
1---
2import Layout from "./Layout.astro";
3
4interface Props {
5 title: string;
6 description: string;
7 date: Date;
8 author: string;
9 authorHandle: string;
10 authorRole: string;
11 authorAvatar: string;
12}
13
14const {
15 title,
16 description,
17 date,
18 author,
19 authorHandle,
20 authorRole,
21 authorAvatar,
22} = Astro.props;
23
24const GH = "https://github.com/RhysSullivan/executor";
25const authorX = `https://x.com/${authorHandle}`;
26
27const formattedDate = date.toLocaleDateString("en-US", {
28 year: "numeric",
29 month: "long",
30 day: "numeric",
31 // Frontmatter dates are bare YYYY-MM-DD (parsed as UTC midnight); format in
32 // UTC so the displayed day matches the date the author wrote, regardless of
33 // the build/runtime timezone.
34 timeZone: "UTC",
35});
36const isoDate = date.toISOString();
37---
38
39<Layout title={`${title} — Executor`} description={description}>
40 <main class="bg-soft relative min-h-dvh antialiased">
41 {/* Graph-paper pattern + fade behind the header, matching the homepage */}
42 <div
43 class="pattern-bg pattern-graph"
44 style="position:absolute;top:0;left:0;right:0;height:60vh;z-index:1;pointer-events:none;"
45 >
46 </div>
47 <div
48 style="position:absolute;top:0;left:0;right:0;height:60vh;z-index:2;pointer-events:none;background:linear-gradient(180deg,transparent 0%,transparent 55%,var(--color-surface) 100%);"
49 >
50 </div>
51
52 <div class="relative z-10">
53 {/* ─── NAV ─── */}
54 <nav>
55 <div
56 class="mx-auto flex h-14 max-w-[1200px] items-center justify-between px-6 sm:px-10"
57 >
58 <a href="/" class="flex items-center gap-2.5">
59 <img src="/favicon-192.png" alt="" class="h-5 w-5" />
60 <span class="text-[15px] font-semibold tracking-[-0.01em] text-ink"
61 >executor</span
62 >
63 </a>
64 <div class="flex items-center gap-5">
65 <a
66 href="/blog"
67 class="text-[14px] font-medium text-ink-2 transition-colors hover:text-ink"
68 >Blog</a
69 >
70 <a
71 href="https://executor.sh/docs"
72 class="hidden text-[14px] font-medium text-ink-2 transition-colors hover:text-ink sm:inline"
73 >Docs</a
74 >
75 <a
76 href={GH}
77 class="text-[14px] font-medium text-ink-2 transition-colors hover:text-ink"
78 >GitHub</a
79 >
80 <a href="/#install" class="btn-primary">Get started →</a>
81 </div>
82 </div>
83 </nav>
84
85 {/* ─── HEADER ─── */}
86 <section class="px-6 pt-16 pb-10 sm:px-10 sm:pt-24 sm:pb-14">
87 <div class="mx-auto max-w-[720px]">
88 <a
89 href="/blog"
90 class="eyebrow inline-flex items-center gap-1.5 transition-colors hover:text-ink-2"
91 >← Blog</a
92 >
93 <h1
94 class="mt-5 text-[clamp(2.2rem,5vw,3.6rem)] font-semibold leading-[1.05] tracking-[-0.025em] text-ink text-balance"
95 >
96 {title}
97 </h1>
98 <p class="mt-5 text-[17px] leading-[1.6] text-ink-2 text-pretty">
99 {description}
100 </p>
101 <div class="mt-8 flex items-center gap-3">
102 <a
103 href={authorX}
104 target="_blank"
105 rel="noopener noreferrer"
106 class="shrink-0"
107 aria-label={`${author} on X`}
108 >
109 <img
110 src={authorAvatar}
111 alt={author}
112 width="44"
113 height="44"
114 loading="lazy"
115 class="h-11 w-11 rounded-full object-cover ring-1 ring-rule"
116 />
117 </a>
118 <div class="min-w-0">
119 <a
120 href={authorX}
121 target="_blank"
122 rel="noopener noreferrer"
123 class="text-[15px] font-semibold text-ink transition-colors hover:text-accent"
124 >{author}</a
125 >
126 <div class="mt-0.5 text-[13px] leading-snug text-ink-3">
127 {authorRole} · <time datetime={isoDate}>{formattedDate}</time>
128 </div>
129 </div>
130 </div>
131 </div>
132 </section>
133
134 {/* ─── ARTICLE ─── */}
135 <section class="px-6 pt-2 pb-20 sm:px-10 sm:pb-28">
136 <article class="blog-prose mx-auto max-w-[720px]">
137 <slot />
138 </article>
139 </section>
140
141 {/* ─── BOTTOM CTA ─── */}
142 <section class="border-t border-rule py-16 sm:py-20">
143 <div class="mx-auto max-w-[720px] px-6 text-center sm:px-10">
144 <h2 class="section-title mx-auto mb-7 max-w-[20ch]">
145 Connect any agent to <span class="serif-italic">everything</span>.
146 </h2>
147 <div class="flex flex-wrap items-center justify-center gap-3">
148 <a href="/#install" class="btn-primary btn-primary--hero"
149 >Get started →</a
150 >
151 <a href={GH} class="btn-secondary">Star on GitHub</a>
152 </div>
153 </div>
154 </section>
155
156 {/* ─── FOOTER ─── */}
157 <footer class="border-t border-rule py-10">
158 <div
159 class="mx-auto flex max-w-[1200px] flex-col items-start justify-between gap-6 px-6 sm:flex-row sm:items-center sm:px-10"
160 >
161 <div class="flex items-center gap-2.5">
162 <img src="/favicon-192.png" alt="" class="h-4 w-4 opacity-80" />
163 <span class="text-[13px] font-medium text-ink-2">executor</span>
164 <span class="ml-2 text-[12px] text-ink-3"
165 >© {new Date().getFullYear()}</span
166 >
167 </div>
168 <div class="flex items-center gap-6 text-[13px] text-ink-2">
169 <a href="/blog" class="transition-colors hover:text-ink">Blog</a>
170 <a
171 href="https://executor.sh/docs"
172 class="transition-colors hover:text-ink">Docs</a
173 >
174 <a href={GH} class="transition-colors hover:text-ink">GitHub</a>
175 <a href="/privacy" class="transition-colors hover:text-ink">Privacy</a>
176 <a href="/terms" class="transition-colors hover:text-ink">Terms</a>
177 </div>
178 </div>
179 </footer>
180 </div>
181 </main>
182</Layout>
apps/marketing/src/pages/blog/[...slug].astro+28−0
@@ -0,0 +1,28 @@
1---
2export const prerender = true;
3import { getCollection, render } from "astro:content";
4import BlogPostLayout from "../../layouts/BlogPostLayout.astro";
5
6export async function getStaticPaths() {
7 const posts = await getCollection("blog", ({ data }) => !data.draft);
8 return posts.map((post) => ({
9 params: { slug: post.id },
10 props: { post },
11 }));
12}
13
14const { post } = Astro.props;
15const { Content } = await render(post);
16---
17
18<BlogPostLayout
19 title={post.data.title}
20 description={post.data.description}
21 date={post.data.date}
22 author={post.data.author}
23 authorHandle={post.data.authorHandle}
24 authorRole={post.data.authorRole}
25 authorAvatar={post.data.authorAvatar}
26>
27 <Content />
28</BlogPostLayout>
apps/marketing/src/pages/blog/index.astro+144−0
@@ -0,0 +1,144 @@
1---
2export const prerender = true;
3import Layout from "../../layouts/Layout.astro";
4import { getCollection } from "astro:content";
5
6const GH = "https://github.com/RhysSullivan/executor";
7
8const posts = (await getCollection("blog", ({ data }) => !data.draft)).sort(
9 (a, b) => b.data.date.getTime() - a.data.date.getTime(),
10);
11
12const fmt = (date: Date) =>
13 date.toLocaleDateString("en-US", {
14 year: "numeric",
15 month: "long",
16 day: "numeric",
17 });
18---
19
20<Layout
21 title="Blog — Executor"
22 description="Notes on agents, tools, and the integration layer underneath them, from the team building Executor."
23>
24 <main class="bg-soft relative min-h-dvh antialiased">
25 <div
26 class="pattern-bg pattern-graph"
27 style="position:absolute;top:0;left:0;right:0;height:60vh;z-index:1;pointer-events:none;"
28 >
29 </div>
30 <div
31 style="position:absolute;top:0;left:0;right:0;height:60vh;z-index:2;pointer-events:none;background:linear-gradient(180deg,transparent 0%,transparent 55%,var(--color-surface) 100%);"
32 >
33 </div>
34
35 <div class="relative z-10">
36 {/* ─── NAV ─── */}
37 <nav>
38 <div
39 class="mx-auto flex h-14 max-w-[1200px] items-center justify-between px-6 sm:px-10"
40 >
41 <a href="/" class="flex items-center gap-2.5">
42 <img src="/favicon-192.png" alt="" class="h-5 w-5" />
43 <span class="text-[15px] font-semibold tracking-[-0.01em] text-ink"
44 >executor</span
45 >
46 </a>
47 <div class="flex items-center gap-5">
48 <a
49 href="/blog"
50 class="text-[14px] font-medium text-ink transition-colors">Blog</a
51 >
52 <a
53 href="https://executor.sh/docs"
54 class="hidden text-[14px] font-medium text-ink-2 transition-colors hover:text-ink sm:inline"
55 >Docs</a
56 >
57 <a
58 href={GH}
59 class="text-[14px] font-medium text-ink-2 transition-colors hover:text-ink"
60 >GitHub</a
61 >
62 <a href="/#install" class="btn-primary">Get started →</a>
63 </div>
64 </div>
65 </nav>
66
67 {/* ─── HEADER ─── */}
68 <section class="px-6 pt-16 pb-10 sm:px-10 sm:pt-24 sm:pb-12">
69 <div class="mx-auto max-w-[760px]">
70 <div class="eyebrow mb-4">Blog</div>
71 <h1
72 class="text-[clamp(2.4rem,5.6vw,4rem)] font-semibold leading-[1.02] tracking-[-0.025em] text-ink"
73 >
74 Field notes on <span class="serif-italic">agents</span> and tools.
75 </h1>
76 <p class="mt-5 max-w-[54ch] text-[17px] leading-[1.6] text-ink-2 text-pretty">
77 Thinking from the team building Executor, the integration layer that
78 connects any agent to everything.
79 </p>
80 </div>
81 </section>
82
83 {/* ─── POST LIST ─── */}
84 <section class="px-6 pb-24 sm:px-10 sm:pb-32">
85 <div class="mx-auto max-w-[760px]">
86 {
87 posts.length === 0 ? (
88 <p class="text-[15px] text-ink-3">No posts yet. Check back soon.</p>
89 ) : (
90 <ul class="flex flex-col gap-4">
91 {posts.map((post) => (
92 <li>
93 <a
94 href={`/blog/${post.id}`}
95 class="surface-card group block p-6 transition-colors sm:p-7"
96 >
97 <div class="mb-2 font-mono text-[12px] uppercase tracking-[0.14em] text-ink-3">
98 {fmt(post.data.date)}
99 </div>
100 <h2 class="text-[20px] font-semibold tracking-[-0.01em] text-ink sm:text-[22px]">
101 {post.data.title}
102 </h2>
103 <p class="mt-2 text-[14.5px] leading-[1.55] text-ink-2 text-pretty">
104 {post.data.description}
105 </p>
106 <span class="mt-4 inline-flex items-center gap-1 text-[13.5px] font-medium text-ink-2 transition-colors group-hover:text-ink">
107 Read post →
108 </span>
109 </a>
110 </li>
111 ))}
112 </ul>
113 )
114 }
115 </div>
116 </section>
117
118 {/* ─── FOOTER ─── */}
119 <footer class="border-t border-rule py-10">
120 <div
121 class="mx-auto flex max-w-[1200px] flex-col items-start justify-between gap-6 px-6 sm:flex-row sm:items-center sm:px-10"
122 >
123 <div class="flex items-center gap-2.5">
124 <img src="/favicon-192.png" alt="" class="h-4 w-4 opacity-80" />
125 <span class="text-[13px] font-medium text-ink-2">executor</span>
126 <span class="ml-2 text-[12px] text-ink-3"
127 >© {new Date().getFullYear()}</span
128 >
129 </div>
130 <div class="flex items-center gap-6 text-[13px] text-ink-2">
131 <a href="/blog" class="transition-colors hover:text-ink">Blog</a>
132 <a
133 href="https://executor.sh/docs"
134 class="transition-colors hover:text-ink">Docs</a
135 >
136 <a href={GH} class="transition-colors hover:text-ink">GitHub</a>
137 <a href="/privacy" class="transition-colors hover:text-ink">Privacy</a>
138 <a href="/terms" class="transition-colors hover:text-ink">Terms</a>
139 </div>
140 </div>
141 </footer>
142 </div>
143 </main>
144</Layout>
apps/marketing/src/pages/index.astro+6−0
@@ -83,6 +83,11 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 82 unmodified lines
83 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors hidden sm:inline"83 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors hidden sm:inline"
84 >Docs</a84 >Docs</a
85 >85 >
86 <a
87 href="/blog"
88 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors hidden sm:inline"
89 >Blog</a
90 >
86 <a91 <a
87 href={GH}92 href={GH}
88 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors"93 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors"
@@ -762,6 +767,7 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 673 unmodified lines
762 <a href="https://executor.sh/docs" class="hover:text-ink transition-colors"767 <a href="https://executor.sh/docs" class="hover:text-ink transition-colors"
763 >Docs</a768 >Docs</a
764 >769 >
770 <a href="/blog" class="hover:text-ink transition-colors">Blog</a>
765 <a href={GH} class="hover:text-ink transition-colors">GitHub</a>771 <a href={GH} class="hover:text-ink transition-colors">GitHub</a>
766 <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>772 <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>
767 <a href="/terms" class="hover:text-ink transition-colors">Terms</a>773 <a href="/terms" class="hover:text-ink transition-colors">Terms</a>
apps/marketing/src/styles/global.css+134−0
@@ -1519,3 +1519,137 @@ body { · 1518 unmodified lines
1519 transition: none;1519 transition: none;
1520 }1520 }
1521}1521}
1522
1523/* ════════════════════════════════════════════════════════════════
1524 BLOG: editorial prose for Markdown-rendered posts (/blog/[slug]).
1525 Reads off the same tokens as the rest of the site (ink / accent /
1526 rule / surface) so posts stay on-brand without a separate palette.
1527 ═══════════════════════════════════════════════════════════════ */
1528.blog-prose {
1529 color: color-mix(in srgb, var(--color-ink) 88%, transparent);
1530 font-size: 17px;
1531 line-height: 1.75;
1532 letter-spacing: -0.011em;
1533}
1534.blog-prose > :first-child {
1535 margin-top: 0;
1536}
1537.blog-prose p {
1538 margin: 1.25rem 0;
1539 text-wrap: pretty;
1540}
1541.blog-prose h2 {
1542 margin-top: 2.75rem;
1543 margin-bottom: 0.9rem;
1544 font-family: var(--font-sans);
1545 font-size: clamp(1.4rem, 2.6vw, 1.7rem);
1546 font-weight: 600;
1547 letter-spacing: -0.02em;
1548 line-height: 1.2;
1549 color: var(--color-ink);
1550}
1551.blog-prose h3 {
1552 margin-top: 2rem;
1553 margin-bottom: 0.5rem;
1554 font-size: 1.18rem;
1555 font-weight: 600;
1556 letter-spacing: -0.01em;
1557 color: var(--color-ink);
1558}
1559.blog-prose ul,
1560.blog-prose ol {
1561 margin: 1.25rem 0;
1562 padding-left: 0;
1563 list-style: none;
1564}
1565.blog-prose li {
1566 position: relative;
1567 margin: 0.5rem 0;
1568 padding-left: 1.4rem;
1569}
1570.blog-prose ul > li::before {
1571 content: "";
1572 position: absolute;
1573 left: 0.2rem;
1574 top: 0.78rem;
1575 width: 5px;
1576 height: 5px;
1577 border-radius: 50%;
1578 background: var(--color-accent);
1579 opacity: 0.75;
1580}
1581.blog-prose ol {
1582 counter-reset: blog-ol;
1583}
1584.blog-prose ol > li {
1585 counter-increment: blog-ol;
1586}
1587.blog-prose ol > li::before {
1588 content: counter(blog-ol) ".";
1589 position: absolute;
1590 left: 0;
1591 top: 0;
1592 font-variant-numeric: tabular-nums;
1593 font-weight: 600;
1594 color: var(--color-accent);
1595}
1596.blog-prose a {
1597 color: var(--color-accent);
1598 text-decoration: underline;
1599 text-decoration-color: color-mix(in srgb, var(--color-accent) 35%, transparent);
1600 text-underline-offset: 3px;
1601 transition:
1602 color 0.15s ease,
1603 text-decoration-color 0.15s ease;
1604}
1605.blog-prose a:hover {
1606 color: var(--color-accent-hover);
1607 text-decoration-color: var(--color-accent-hover);
1608}
1609.blog-prose strong {
1610 color: var(--color-ink);
1611 font-weight: 600;
1612}
1613.blog-prose em {
1614 font-style: italic;
1615}
1616.blog-prose blockquote {
1617 margin: 1.5rem 0;
1618 padding: 0.25rem 0 0.25rem 1.25rem;
1619 border-left: 2px solid color-mix(in srgb, var(--color-accent) 55%, transparent);
1620 color: var(--color-ink-2);
1621 font-style: italic;
1622}
1623.blog-prose code {
1624 font-family: var(--font-mono);
1625 font-size: 0.86em;
1626 background: var(--color-surface-2);
1627 border: 1px solid var(--color-rule);
1628 border-radius: 5px;
1629 padding: 0.12em 0.4em;
1630}
1631.blog-prose pre {
1632 margin: 1.5rem 0;
1633 padding: 1.1rem 1.25rem;
1634 background: var(--color-surface-2);
1635 border: 1px solid var(--color-rule);
1636 border-radius: 12px;
1637 overflow-x: auto;
1638 font-size: 14px;
1639 line-height: 1.6;
1640}
1641.blog-prose pre code {
1642 background: transparent;
1643 border: 0;
1644 padding: 0;
1645 font-size: inherit;
1646}
1647.blog-prose hr {
1648 margin: 2.5rem 0;
1649 border: 0;
1650 border-top: 1px solid var(--color-rule);
1651}
1652.blog-prose h2 + p,
1653.blog-prose h3 + p {
1654 margin-top: 0;
1655}