History

marketing: blog on the rail layout; one shared shell for home, pricing, and blog (#2019)

Rhys Sullivan committed 1 month ago

Commit 37f91a21 parent 6bd57756 files · +233 −415Browse files

Unified · Split

6 files
  1. apps/marketing/src/layouts/BlogPostLayout.astro +27 −153
  2. apps/marketing/src/layouts/RailLayout.astro +49 −0
  3. apps/marketing/src/pages/blog/index.astro +27 −126
  4. apps/marketing/src/pages/index.astro +7 −50
  5. apps/marketing/src/pages/pricing.astro +57 −86
  6. apps/marketing/src/styles/variants/doc.css +66 −0
apps/marketing/src/layouts/BlogPostLayout.astro+27−153
@@ -1,5 +1,5 @@
11 ---
2−import Layout from "./Layout.astro";
2+import RailLayout from "./RailLayout.astro";
33
44 interface Props {
55 title: string;
@@ -11,17 +11,9 @@ interface Props { · 5 unmodified lines
1111 authorAvatar: string;
1212 }
1313
14−const {
15− title,
16− description,
17− date,
18− author,
19− authorHandle,
20− authorRole,
21− authorAvatar,
22−} = Astro.props;
14+const { title, description, date, author, authorHandle, authorRole, authorAvatar } =
15+ Astro.props;
2316
24−const GH = "https://github.com/UsefulSoftwareCo/executor";
2517 const authorX = `https://x.com/${authorHandle}`;
2618
2719 const formattedDate = date.toLocaleDateString("en-US", {
@@ -36,147 +28,29 @@ const formattedDate = date.toLocaleDateString("en-US", { · 8 unmodified lines
3628 const isoDate = date.toISOString();
3729 ---
3830
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− >
31+<RailLayout title={`${title} — Executor`} description={description} current="blog">
32+ <article class="doc">
33+ <header class="doc__intro doc__intro--post">
34+ <a href="/blog" class="doc__crumb">← Writing</a>
35+ <h1 class="doc__title">{title}</h1>
36+ <p class="doc__lede">{description}</p>
37+ <a href={authorX} target="_blank" rel="noopener noreferrer" class="doc__sig doc__sig--post">
38+ <img src={authorAvatar} alt="" width="40" height="40" loading="lazy" />
39+ <span class="doc__sig-b">
40+ <strong>{author}</strong>
41+ <span>{authorRole} · <time datetime={isoDate}>{formattedDate}</time></span>
42+ </span>
43+ </a>
44+ </header>
45+
46+ <div class="blog-prose doc__prose">
47+ <slot />
4648 </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>
13349
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>
50+ <section class="doc__section">
51+ <p class="doc__quiet-p">
52+ <a href="/blog">More writing</a> · <a href="/">Back to Executor</a>
53+ </p>
54+ </section>
55+ </article>
56+</RailLayout>
apps/marketing/src/layouts/RailLayout.astro+49−0
@@ -0,0 +1,49 @@
1+---
2+// The site shell: sticky rail on the left, one centred frame, footer at the
3+// bottom of the body column. The homepage, pricing, and blog all use it.
4+// Prerendered pages resolve the star count once at build time.
5+import Layout from "./Layout.astro";
6+import RailB from "../components/variants/rail-b.astro";
7+import { getStars, formatStars } from "../lib/github";
8+import "../styles/variants/doc.css";
9+
10+interface Props {
11+ title: string;
12+ description: string;
13+ current?: "home" | "pricing" | "blog";
14+}
15+const { title, description, current = "home" } = Astro.props;
16+
17+const GH = "https://github.com/UsefulSoftwareCo/executor";
18+const stars = await getStars();
19+---
20+
21+<Layout title={title} description={description}>
22+ <main class="bg-soft min-h-dvh relative antialiased">
23+ <div class="relative z-10 page">
24+ <div class="page__rail page__rail--b">
25+ <RailB stars={stars != null ? formatStars(stars) : null} current={current} />
26+ </div>
27+ <div class="page__body">
28+ <slot />
29+
30+ <footer class="border-t border-rule py-10">
31+ <div class="max-w-[1200px] mx-auto px-6 sm:px-10 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6">
32+ <div class="flex items-center gap-2.5">
33+ <img src="/favicon-192.png" alt="" class="w-4 h-4 opacity-80" />
34+ <span class="text-[13px] font-medium text-ink-2">executor</span>
35+ <span class="text-[12px] text-ink-3 ml-2">© {new Date().getFullYear()}</span>
36+ </div>
37+ <div class="flex items-center gap-6 text-[13px] text-ink-2">
38+ <a href="https://executor.sh/docs" class="hover:text-ink transition-colors">Docs</a>
39+ <a href="/blog" class="hover:text-ink transition-colors">Blog</a>
40+ <a href={GH} class="hover:text-ink transition-colors">GitHub</a>
41+ <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>
42+ <a href="/terms" class="hover:text-ink transition-colors">Terms</a>
43+ </div>
44+ </div>
45+ </footer>
46+ </div>
47+ </div>
48+ </main>
49+</Layout>
apps/marketing/src/pages/blog/index.astro+27−126
@@ -1,10 +1,8 @@
11 ---
22 export const prerender = true;
3−import Layout from "../../layouts/Layout.astro";
3+import RailLayout from "../../layouts/RailLayout.astro";
44 import { getCollection } from "astro:content";
55
6−const GH = "https://github.com/UsefulSoftwareCo/executor";
7−
86 const posts = (await getCollection("blog", ({ data }) => !data.draft)).sort(
97 (a, b) => b.data.date.getTime() - a.data.date.getTime(),
108 );
@@ -20,126 +18,29 @@ const fmt = (date: Date) => · 9 unmodified lines
2018 });
2119 ---
2220
23−<Layout
24− title="Blog — Executor"
25− description="Latest news and updates from Executor."
26−>
27− <main class="bg-soft relative min-h-dvh antialiased">
28− <div
29− class="pattern-bg pattern-graph"
30− style="position:absolute;top:0;left:0;right:0;height:60vh;z-index:1;pointer-events:none;"
31− >
32− </div>
33− <div
34− 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%);"
35− >
36− </div>
37−
38− <div class="relative z-10">
39− {/* ─── NAV ─── */}
40− <nav>
41− <div
42− class="mx-auto flex h-14 max-w-[1200px] items-center justify-between px-6 sm:px-10"
43− >
44− <a href="/" class="flex items-center gap-2.5">
45− <img src="/favicon-192.png" alt="" class="h-5 w-5" />
46− <span class="text-[15px] font-semibold tracking-[-0.01em] text-ink"
47− >executor</span
48− >
49− </a>
50− <div class="flex items-center gap-5">
51− <a
52− href="/blog"
53− class="text-[14px] font-medium text-ink transition-colors">Blog</a
54− >
55− <a
56− href="https://executor.sh/docs"
57− class="hidden text-[14px] font-medium text-ink-2 transition-colors hover:text-ink sm:inline"
58− >Docs</a
59− >
60− <a
61− href={GH}
62− class="text-[14px] font-medium text-ink-2 transition-colors hover:text-ink"
63− >GitHub</a
64− >
65− <a href="/#install" class="btn-primary">Get started →</a>
66− </div>
67− </div>
68− </nav>
69−
70− {/* ─── HEADER ─── */}
71− <section class="px-6 pt-16 pb-10 sm:px-10 sm:pt-24 sm:pb-12">
72− <div class="mx-auto max-w-[760px]">
73− <h1
74− class="text-[clamp(2.4rem,5.6vw,4rem)] font-semibold leading-[1.02] tracking-[-0.025em] text-ink"
75− >
76− Blog
77− </h1>
78− <p class="mt-5 max-w-[54ch] text-[17px] leading-[1.6] text-ink-2 text-pretty">
79− Latest news and updates from Executor
80− </p>
81− </div>
82− </section>
83−
84− {/* ─── POST LIST ─── */}
85− <section class="px-6 pb-24 sm:px-10 sm:pb-32">
86− <div class="mx-auto max-w-[760px]">
87− {
88− posts.length === 0 ? (
89− <p class="text-[15px] text-ink-3">No posts yet. Check back soon.</p>
90− ) : (
91− <ul class="flex flex-col gap-4">
92− {posts.map((post) => (
93− <li>
94− <a
95− href={`/blog/${post.id}`}
96− class="surface-card group block p-6 transition-colors sm:p-7"
97− >
98− <div class="mb-2 font-mono text-[12px] uppercase tracking-[0.14em] text-ink-3">
99− {fmt(post.data.date)}
100− </div>
101− <h2 class="text-[20px] font-semibold tracking-[-0.01em] text-ink sm:text-[22px]">
102− {post.data.title}
103− </h2>
104− <p class="mt-2 text-[14.5px] leading-[1.55] text-ink-2 text-pretty">
105− {post.data.description}
106− </p>
107− <span class="mt-4 inline-flex items-center gap-1 text-[13.5px] font-medium text-ink-2 transition-colors group-hover:text-ink">
108− Read post →
109− </span>
110− </a>
111− </li>
112− ))}
113− </ul>
114− )
115− }
116− </div>
117− </section>
118−
119− {/* ─── FOOTER ─── */}
120− <footer class="border-t border-rule py-10">
121− <div
122− 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"
123− >
124− <div class="flex items-center gap-2.5">
125− <img src="/favicon-192.png" alt="" class="h-4 w-4 opacity-80" />
126− <span class="text-[13px] font-medium text-ink-2">executor</span>
127− <span class="ml-2 text-[12px] text-ink-3"
128− >© {new Date().getFullYear()}</span
129− >
130− </div>
131− <div class="flex items-center gap-6 text-[13px] text-ink-2">
132− <a href="/blog" class="transition-colors hover:text-ink">Blog</a>
133− <a
134− href="https://executor.sh/docs"
135− class="transition-colors hover:text-ink">Docs</a
136− >
137− <a href={GH} class="transition-colors hover:text-ink">GitHub</a>
138− <a href="/privacy" class="transition-colors hover:text-ink">Privacy</a>
139− <a href="/terms" class="transition-colors hover:text-ink">Terms</a>
140− </div>
141− </div>
142− </footer>
143− </div>
144− </main>
145−</Layout>
21+<RailLayout title="Blog — Executor" description="Latest news and updates from Executor." current="blog">
22+ <article class="doc">
23+ <header class="doc__intro">
24+ <h1 class="doc__title">Writing</h1>
25+ <p class="doc__lede">Notes from building Executor.</p>
26+ </header>
27+
28+ {
29+ posts.length === 0 ? (
30+ <p class="text-[15px] text-ink-3">No posts yet. Check back soon.</p>
31+ ) : (
32+ <ul class="doc__posts">
33+ {posts.map((post) => (
34+ <li>
35+ <a href={`/blog/${post.id}`} class="doc__post">
36+ <span class="doc__post-date">{fmt(post.data.date)}</span>
37+ <span class="doc__post-title">{post.data.title}</span>
38+ <span class="doc__post-desc">{post.data.description}</span>
39+ </a>
40+ </li>
41+ ))}
42+ </ul>
43+ )
44+ }
45+ </article>
46+</RailLayout>
apps/marketing/src/pages/index.astro+7−50
@@ -1,10 +1,8 @@
11 ---
2−import Layout from "../layouts/Layout.astro";
2+import RailLayout from "../layouts/RailLayout.astro";
33 // Imported so Vite emits it as a hashed build asset (served from /_astro/...);
44 // the deploy pipeline does not pick up newly added /public files.
5−import { getStars, formatStars } from "../lib/github";
65 import { setupPrompt } from "../content/site-copy";
7−import RailB from "../components/variants/rail-b.astro";
86 import DocBody from "../components/variants/doc-body.astro";
97
108 const GH = "https://github.com/UsefulSoftwareCo/executor";
@@ -12,57 +10,16 @@ const GH = "https://github.com/UsefulSoftwareCo/executor"; · 1 unmodified line
1210 // /api/* is app-owned (apps/cloud/src/app-paths.ts), so this hits the WorkOS
1311 // login flow directly. Returning users land here; new users use Get started.
1412 const SIGN_IN = "/api/auth/login";
15−const stars = await getStars();
1613
1714 ---
1815
19−<Layout>
20− <main class="bg-soft min-h-dvh relative antialiased">
21− {/* Graph-paper hero texture: grayscale (ink-masked), faded into the surface
22− at the bottom. Part of the registry-grade language, not a colored effect. */}
23− <div
24− class="pattern-bg pattern-graph"
25− style="position: absolute; top: 0; left: 0; right: 0; height: 100vh; z-index: 1; pointer-events: none;"
26− >
27− </div>
28− <div
29− style="position: absolute; top: 0; left: 0; right: 0; height: 100vh; z-index: 2; pointer-events: none; background: linear-gradient(180deg, transparent 0%, transparent 65%, var(--color-surface) 100%);"
30− >
31− </div>
16+<RailLayout
17+ title="Executor — The gateway to connect your agent to everything"
18+ description="One place every agent plugs into every tool you already use. Wire it up once, bring the whole team."
19+>
20+ <DocBody gh={GH} />
21+</RailLayout>
3222
33− <div class="relative z-10 page">
34− <div class="page__rail page__rail--b"><RailB stars={stars != null ? formatStars(stars) : null} /></div>
35− <div class="page__body">
36−
37− <DocBody gh={GH} />
38−
39− {/* ─── FOOTER ─── */}
40− <footer class="border-t border-rule py-10">
41− <div
42− class="max-w-[1200px] mx-auto px-6 sm:px-10 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6"
43− >
44− <div class="flex items-center gap-2.5">
45− <img src="/favicon-192.png" alt="" class="w-4 h-4 opacity-80" />
46− <span class="text-[13px] font-medium text-ink-2">executor</span>
47− <span class="text-[12px] text-ink-3 ml-2"
48− >© {new Date().getFullYear()}</span
49− >
50− </div>
51− <div class="flex items-center gap-6 text-[13px] text-ink-2">
52− <a href="https://executor.sh/docs" class="hover:text-ink transition-colors"
53− >Docs</a
54− >
55− <a href="/blog" class="hover:text-ink transition-colors">Blog</a>
56− <a href={GH} class="hover:text-ink transition-colors">GitHub</a>
57− <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>
58− <a href="/terms" class="hover:text-ink transition-colors">Terms</a>
59− </div>
60− </div>
61− </footer>
62− </div>
63− </div>
64− </main>
65−</Layout>
6623
6724 <script>
6825 // Copy buttons. Use the async Clipboard API in secure contexts and fall back
apps/marketing/src/pages/pricing.astro+57−86
@@ -1,13 +1,9 @@
11 ---
2−import Layout from "../layouts/Layout.astro";
3−import { getStars, formatStars } from "../lib/github";
2+import RailLayout from "../layouts/RailLayout.astro";
43 import { pricingTiers } from "../content/site-copy";
5−import RailB from "../components/variants/rail-b.astro";
6−import "../styles/variants/doc.css";
74 import "../styles/variants/pricing.css";
85
96 const GH = "https://github.com/UsefulSoftwareCo/executor";
10−const stars = await getStars();
117
128 // Same tiers the Markdown route serves; the card layout only needs a split
139 // between the number and its unit, and a real href for each call to action.
@@ -39,91 +35,66 @@ const cards = [ · 25 unmodified lines
3935 ];
4036 ---
4137
42−<Layout
38+<RailLayout
4339 title="Pricing — Executor"
4440 description="Start free, pay per member. Executor Cloud pricing for teams and enterprises."
41+ current="pricing"
4542 >
46− <main class="bg-soft min-h-dvh relative antialiased">
47− <div class="relative z-10 page">
48− <div class="page__rail page__rail--b">
49− <RailB stars={stars != null ? formatStars(stars) : null} current="pricing" />
50− </div>
51− <div class="page__body">
52− <article class="doc">
53− <header class="doc__intro">
54− <h1 class="doc__title">Start free, pay per member.</h1>
55− <p class="doc__lede">
56− Cloud is free for small teams. Team is one flat price per person. Enterprise is
57− for orgs that need it on their own infrastructure with their own controls.
58− </p>
59− </header>
60−
61− <div class="pricing">
62− {
63− cards.map(({ tier, amount, unit, href, label, recommended }) => (
64− <section class:list={["pricing__card", recommended && "pricing__card--rec"]}>
65− <span class="pricing__eyebrow">{recommended ? "Recommended" : tier.name === "Free" ? "Get started" : "Custom needs"}</span>
66− <h2 class="pricing__name">{tier.name}</h2>
67− <p class="pricing__aud">{tier.audience.replace(" (recommended)", "")}</p>
68− <p class="pricing__price">
69− <span class="pricing__amount">{amount}</span>
70− {unit ? <span class="pricing__unit">{unit}</span> : null}
71− </p>
72− <a href={href} class={recommended ? "btn-primary" : "btn-secondary"}>{label}</a>
73− {tier.featuresLabel ? <p class="pricing__flabel">{tier.featuresLabel}</p> : null}
74− <ul class="check-list">
75− {tier.features.map((f) => (
76− <li>
77− <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
78− {f}
79− </li>
80− ))}
81− </ul>
82− </section>
83− ))
84− }
85− </div>
86−
43+ <article class="doc">
44+ <header class="doc__intro">
45+ <h1 class="doc__title">Start free, pay per member.</h1>
46+ <p class="doc__lede">
47+ Cloud is free for small teams. Team is one flat price per person. Enterprise is
48+ for orgs that need it on their own infrastructure with their own controls.
49+ </p>
50+ </header>
8751
88− <section class="doc__section">
89− <h2 class="doc__h">Questions</h2>
90− <dl class="doc__beats">
91− <div>
92− <dt>What counts as an execution?</dt>
93− <dd>One call to the Executor tool from your agent. A single execution can make many tool calls inside it.</dd>
94− </div>
95− <div>
96− <dt>What happens when the free tier runs out?</dt>
97− <dd>Calls pause until the month resets or you move to Team. Nothing is deleted.</dd>
98− </div>
99− <div>
100− <dt>Can I mix Cloud and self-hosted?</dt>
101− <dd>Yes. They are the same code, and an agent can point at more than one Executor.</dd>
102− </div>
103− </dl>
104− <p class="doc__quiet-p">
105− Something else? <a href="mailto:rhys@executor.sh?subject=Executor%20pricing">Email Rhys</a>.
52+ <div class="pricing">
53+ {
54+ cards.map(({ tier, amount, unit, href, label, recommended }) => (
55+ <section class:list={["pricing__card", recommended && "pricing__card--rec"]}>
56+ <span class="pricing__eyebrow">{recommended ? "Recommended" : tier.name === "Free" ? "Get started" : "Custom needs"}</span>
57+ <h2 class="pricing__name">{tier.name}</h2>
58+ <p class="pricing__aud">{tier.audience.replace(" (recommended)", "")}</p>
59+ <p class="pricing__price">
60+ <span class="pricing__amount">{amount}</span>
61+ {unit ? <span class="pricing__unit">{unit}</span> : null}
10662 </p>
63+ <a href={href} class={recommended ? "btn-primary" : "btn-secondary"}>{label}</a>
64+ {tier.featuresLabel ? <p class="pricing__flabel">{tier.featuresLabel}</p> : null}
65+ <ul class="check-list">
66+ {tier.features.map((f) => (
67+ <li>
68+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
69+ {f}
70+ </li>
71+ ))}
72+ </ul>
10773 </section>
108− </article>
109−
110− <footer class="border-t border-rule py-10">
111− <div class="max-w-[1200px] mx-auto px-6 sm:px-10 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6">
112− <div class="flex items-center gap-2.5">
113− <img src="/favicon-192.png" alt="" class="w-4 h-4 opacity-80" />
114− <span class="text-[13px] font-medium text-ink-2">executor</span>
115− <span class="text-[12px] text-ink-3 ml-2">© {new Date().getFullYear()}</span>
116− </div>
117− <div class="flex items-center gap-6 text-[13px] text-ink-2">
118− <a href="https://executor.sh/docs" class="hover:text-ink transition-colors">Docs</a>
119− <a href="/blog" class="hover:text-ink transition-colors">Blog</a>
120− <a href={GH} class="hover:text-ink transition-colors">GitHub</a>
121− <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>
122− <a href="/terms" class="hover:text-ink transition-colors">Terms</a>
123− </div>
124− </div>
125− </footer>
126− </div>
74+ ))
75+ }
12776 </div>
128− </main>
129−</Layout>
77+
78+
79+ <section class="doc__section">
80+ <h2 class="doc__h">Questions</h2>
81+ <dl class="doc__beats">
82+ <div>
83+ <dt>What counts as an execution?</dt>
84+ <dd>One call to the Executor tool from your agent. A single execution can make many tool calls inside it.</dd>
85+ </div>
86+ <div>
87+ <dt>What happens when the free tier runs out?</dt>
88+ <dd>Calls pause until the month resets or you move to Team. Nothing is deleted.</dd>
89+ </div>
90+ <div>
91+ <dt>Can I mix Cloud and self-hosted?</dt>
92+ <dd>Yes. They are the same code, and an agent can point at more than one Executor.</dd>
93+ </div>
94+ </dl>
95+ <p class="doc__quiet-p">
96+ Something else? <a href="mailto:rhys@executor.sh?subject=Executor%20pricing">Email Rhys</a>.
97+ </p>
98+ </section>
99+ </article>
100+</RailLayout>
apps/marketing/src/styles/variants/doc.css+66−0
@@ -949,3 +949,69 @@ a.doc__term-v:hover { · 948 unmodified lines
949949 text-align: left;
950950 }
951951 }
952+
953+/* ── Blog ── */
954+.doc__posts {
955+ display: grid;
956+ margin-top: 0.5rem;
957+ border-top: 1px solid var(--color-rule);
958+}
959+.doc__post {
960+ display: grid;
961+ gap: 0.3rem;
962+ padding: 1.15rem 0 1.25rem;
963+ border-bottom: 1px solid var(--color-rule);
964+ text-decoration: none;
965+ color: var(--color-ink-2);
966+}
967+.doc__post-date {
968+ font-family: var(--font-mono);
969+ font-size: 11px;
970+ text-transform: uppercase;
971+ letter-spacing: 0.14em;
972+ color: var(--color-ink-3);
973+}
974+.doc__post-title {
975+ font-size: 19px;
976+ font-weight: 600;
977+ letter-spacing: -0.01em;
978+ color: var(--color-ink);
979+ text-decoration: underline;
980+ text-underline-offset: 4px;
981+ text-decoration-color: transparent;
982+ transition: text-decoration-color 0.15s ease;
983+}
984+.doc__post:hover .doc__post-title {
985+ text-decoration-color: var(--color-rule-strong);
986+}
987+.doc__post-desc {
988+ font-size: 15px;
989+ line-height: 1.55;
990+ text-wrap: pretty;
991+}
992+.doc__crumb {
993+ display: inline-block;
994+ font-family: var(--font-mono);
995+ font-size: 11px;
996+ text-transform: uppercase;
997+ letter-spacing: 0.14em;
998+ color: var(--color-ink-3);
999+ margin-bottom: 1rem;
1000+}
1001+.doc__crumb:hover {
1002+ color: var(--color-ink);
1003+}
1004+.doc__sig--post {
1005+ margin-top: 0.5rem;
1006+}
1007+.doc__intro--post {
1008+ padding-bottom: 2rem;
1009+ border-bottom: 1px solid var(--color-rule);
1010+ margin-bottom: 2rem;
1011+}
1012+@media (min-width: 1024px) {
1013+ .doc__prose,
1014+ .doc__posts {
1015+ max-width: 720px;
1016+ }
1017+}