Unified · Split
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | --- |
| 2 | −import Layout from "./Layout.astro"; | |
| 2 | +import RailLayout from "./RailLayout.astro"; | |
| 3 | 3 | |
| 4 | 4 | interface Props { |
| 5 | 5 | title: string; |
| @@ -11,17 +11,9 @@ interface Props { · 5 unmodified lines | ||
| 11 | 11 | authorAvatar: string; |
| 12 | 12 | } |
| 13 | 13 | |
| 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; | |
| 23 | 16 | |
| 24 | −const GH = "https://github.com/UsefulSoftwareCo/executor"; | |
| 25 | 17 | const authorX = `https://x.com/${authorHandle}`; |
| 26 | 18 | |
| 27 | 19 | const formattedDate = date.toLocaleDateString("en-US", { |
| @@ -36,147 +28,29 @@ const formattedDate = date.toLocaleDateString("en-US", { · 8 unmodified lines | ||
| 36 | 28 | const isoDate = date.toISOString(); |
| 37 | 29 | --- |
| 38 | 30 | |
| 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 /> | |
| 46 | 48 | </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 | 49 | |
| 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> | |
| @@ -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> | |
| @@ -1,10 +1,8 @@ | ||
| 1 | 1 | --- |
| 2 | 2 | export const prerender = true; |
| 3 | −import Layout from "../../layouts/Layout.astro"; | |
| 3 | +import RailLayout from "../../layouts/RailLayout.astro"; | |
| 4 | 4 | import { getCollection } from "astro:content"; |
| 5 | 5 | |
| 6 | −const GH = "https://github.com/UsefulSoftwareCo/executor"; | |
| 7 | − | |
| 8 | 6 | const posts = (await getCollection("blog", ({ data }) => !data.draft)).sort( |
| 9 | 7 | (a, b) => b.data.date.getTime() - a.data.date.getTime(), |
| 10 | 8 | ); |
| @@ -20,126 +18,29 @@ const fmt = (date: Date) => · 9 unmodified lines | ||
| 20 | 18 | }); |
| 21 | 19 | --- |
| 22 | 20 | |
| 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> | |
| @@ -1,10 +1,8 @@ | ||
| 1 | 1 | --- |
| 2 | −import Layout from "../layouts/Layout.astro"; | |
| 2 | +import RailLayout from "../layouts/RailLayout.astro"; | |
| 3 | 3 | // Imported so Vite emits it as a hashed build asset (served from /_astro/...); |
| 4 | 4 | // the deploy pipeline does not pick up newly added /public files. |
| 5 | −import { getStars, formatStars } from "../lib/github"; | |
| 6 | 5 | import { setupPrompt } from "../content/site-copy"; |
| 7 | −import RailB from "../components/variants/rail-b.astro"; | |
| 8 | 6 | import DocBody from "../components/variants/doc-body.astro"; |
| 9 | 7 | |
| 10 | 8 | const GH = "https://github.com/UsefulSoftwareCo/executor"; |
| @@ -12,57 +10,16 @@ const GH = "https://github.com/UsefulSoftwareCo/executor"; · 1 unmodified line | ||
| 12 | 10 | // /api/* is app-owned (apps/cloud/src/app-paths.ts), so this hits the WorkOS |
| 13 | 11 | // login flow directly. Returning users land here; new users use Get started. |
| 14 | 12 | const SIGN_IN = "/api/auth/login"; |
| 15 | −const stars = await getStars(); | |
| 16 | 13 | |
| 17 | 14 | --- |
| 18 | 15 | |
| 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> | |
| 32 | 22 | |
| 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> | |
| 66 | 23 | |
| 67 | 24 | <script> |
| 68 | 25 | // Copy buttons. Use the async Clipboard API in secure contexts and fall back |
| @@ -1,13 +1,9 @@ | ||
| 1 | 1 | --- |
| 2 | −import Layout from "../layouts/Layout.astro"; | |
| 3 | −import { getStars, formatStars } from "../lib/github"; | |
| 2 | +import RailLayout from "../layouts/RailLayout.astro"; | |
| 4 | 3 | import { pricingTiers } from "../content/site-copy"; |
| 5 | −import RailB from "../components/variants/rail-b.astro"; | |
| 6 | −import "../styles/variants/doc.css"; | |
| 7 | 4 | import "../styles/variants/pricing.css"; |
| 8 | 5 | |
| 9 | 6 | const GH = "https://github.com/UsefulSoftwareCo/executor"; |
| 10 | −const stars = await getStars(); | |
| 11 | 7 | |
| 12 | 8 | // Same tiers the Markdown route serves; the card layout only needs a split |
| 13 | 9 | // between the number and its unit, and a real href for each call to action. |
| @@ -39,91 +35,66 @@ const cards = [ · 25 unmodified lines | ||
| 39 | 35 | ]; |
| 40 | 36 | --- |
| 41 | 37 | |
| 42 | −<Layout | |
| 38 | +<RailLayout | |
| 43 | 39 | title="Pricing — Executor" |
| 44 | 40 | description="Start free, pay per member. Executor Cloud pricing for teams and enterprises." |
| 41 | + current="pricing" | |
| 45 | 42 | > |
| 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> | |
| 87 | 51 | |
| 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} | |
| 106 | 62 | </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> | |
| 107 | 73 | </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 | + } | |
| 127 | 76 | </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> | |
| @@ -949,3 +949,69 @@ a.doc__term-v:hover { · 948 unmodified lines | ||
| 949 | 949 | text-align: left; |
| 950 | 950 | } |
| 951 | 951 | } |
| 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 | +} | |