Drop the Cloud / Desktop / Self-hosted tabs from the hero. One Start on Cloud button, a ghost Copy setup prompt beside it, and a fine-print link to the Run it section for the local options. The rail stops selling: the Get started, star, and YC boxes become two quiet text lines. Extract the intro steps and the copy button into components.
Unified · Split
| @@ -23,3 +23,4 @@ pnpm-debug.log* · 22 unmodified lines | ||
| 23 | 23 | # jetbrains setting folder |
| 24 | 24 | .idea/ |
| 25 | 25 | .vercel |
| 26 | +.preview/ | |
| @@ -0,0 +1,36 @@ | ||
| 1 | +--- | |
| 2 | +// The "Copy setup prompt" button: a dashed ghost button beside the primary, | |
| 3 | +// carrying the three agent marks. Copies `setupPrompt`; the click handler | |
| 4 | +// lives in pages/index.astro. | |
| 5 | +import { setupPrompt } from "../../content/site-copy"; | |
| 6 | + | |
| 7 | +const logos = import.meta.glob<string>("../../assets/logos/*.svg", { | |
| 8 | + eager: true, | |
| 9 | + query: "?url", | |
| 10 | + import: "default", | |
| 11 | +}); | |
| 12 | +const logo = (name: string): string => { | |
| 13 | + const hit = Object.entries(logos).find(([k]) => k.endsWith(`/${name}.svg`)); | |
| 14 | + if (!hit) throw new Error(`missing logo ${name}`); | |
| 15 | + return hit[1]; | |
| 16 | +}; | |
| 17 | +const marks = ["claude", "codex", "cursor"]; | |
| 18 | + | |
| 19 | + | |
| 20 | +--- | |
| 21 | + | |
| 22 | +<button | |
| 23 | + type="button" | |
| 24 | + class="btn-ghost copy-btn doc__copy" | |
| 25 | + data-copy={setupPrompt} | |
| 26 | + data-event="marketing_setup_prompt_copied" | |
| 27 | +> | |
| 28 | + <span class="doc__copy-logos" aria-hidden="true"> | |
| 29 | + {marks.map((n) => <img src={logo(n)} alt="" width="14" height="14" />)} | |
| 30 | + </span> | |
| 31 | + <span>Copy setup prompt</span> | |
| 32 | + <span class="copy-icons" aria-hidden="true"> | |
| 33 | + <svg class="copy-icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg> | |
| 34 | + <svg class="copy-icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" 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"></path></svg> | |
| 35 | + </span> | |
| 36 | +</button> | |
| @@ -1,32 +1,19 @@ | ||
| 1 | 1 | --- |
| 2 | 2 | // Document-style homepage body. Reads like a short paper about the product: |
| 3 | 3 | // one modest headline, plain first-person prose, six beats separated by |
| 4 | −// hairlines, the two demos kept as numbered figures, and one way to start. | |
| 4 | +// hairlines, the two demos kept as numbered figures, and one door in: Cloud, | |
| 5 | +// with a setup prompt for visitors who already have an agent open. | |
| 5 | 6 | // Sits inside the rail layout; the rail's "On this page" list points at the |
| 6 | 7 | // section ids below. |
| 7 | 8 | import { AnimatedBeamDemo } from "../animated-beam-demo"; |
| 8 | 9 | import { ContextBloatDemo } from "../context-bloat-demo"; |
| 9 | −import { setupPrompt } from "../../content/site-copy"; | |
| 10 | 10 | import Testimonials from "./testimonials.astro"; |
| 11 | +import IntroSteps from "./intro-steps.astro"; | |
| 12 | +import CopyPrompt from "./copy-prompt.astro"; | |
| 11 | 13 | import "../../styles/variants/doc.css"; |
| 12 | 14 | // Avatar goes through Vite so it ships as a hashed asset; new /public files |
| 13 | 15 | // are not picked up by the deploy pipeline. |
| 14 | 16 | import rhysAvatar from "../../assets/rhys-sullivan.jpg"; |
| 15 | −// Brand marks for the hover reveals in the intro steps. | |
| 16 | −const logos = import.meta.glob<string>("../../assets/logos/*.svg", { | |
| 17 | − eager: true, | |
| 18 | − query: "?url", | |
| 19 | − import: "default", | |
| 20 | −}); | |
| 21 | −const logo = (name: string): string => { | |
| 22 | − const hit = Object.entries(logos).find(([k]) => k.endsWith(`/${name}.svg`)); | |
| 23 | − if (!hit) throw new Error(`missing logo ${name}`); | |
| 24 | − return hit[1]; | |
| 25 | −}; | |
| 26 | −const toolLogos = ["github", "linear", "sentry", "stripe", "notion", "slack", "jira", "gmail"]; | |
| 27 | −const agentLogos = ["claude", "codex", "cursor", "chatgpt", "gemini", "hermes", "copilot", "windsurf", "opencode", "zed", "grok"]; | |
| 28 | −// The three marks on the copy button; the full list would crowd it. | |
| 29 | −const agentLogosShort = ["claude", "codex", "cursor"]; | |
| 30 | 17 | const X_URL = "https://x.com/RhysSullivan"; |
| 31 | 18 | |
| 32 | 19 | interface Props { |
| @@ -39,91 +26,15 @@ const { gh } = Astro.props; · 6 unmodified lines | ||
| 39 | 26 | {/* ── Intro ── */} |
| 40 | 27 | <header class="doc__intro"> |
| 41 | 28 | <h1 class="doc__title">Executor is an MCP gateway.</h1> |
| 42 | − <ol class="doc__steps"> | |
| 43 | − <li> | |
| 44 | − <span class="doc__num">01</span> | |
| 45 | − <span> | |
| 46 | − Connect{" "} | |
| 47 | − <span class="doc__pop" tabindex="0"> | |
| 48 | − <span class="doc__pop-word">everything</span> | |
| 49 | − <span class="doc__pop-panel" aria-hidden="true" inert> | |
| 50 | − {toolLogos.map((n, i) => ( | |
| 51 | − <span class="doc__pop-tile" style={`--i:${i}`}><img src={logo(n)} alt="" width="18" height="18" /></span> | |
| 52 | − ))} | |
| 53 | − <span class="doc__pop-pill" style={`--i:${toolLogos.length}`}>any OpenAPI, GraphQL, or MCP</span> | |
| 54 | − </span> | |
| 55 | − </span> | |
| 56 | − {" "}to Executor. | |
| 57 | − </span> | |
| 58 | − </li> | |
| 59 | − <li> | |
| 60 | − <span class="doc__num">02</span> | |
| 61 | − <span> | |
| 62 | − Give{" "} | |
| 63 | − <span class="doc__pop" tabindex="0"> | |
| 64 | − <span class="doc__pop-word">your agent</span> | |
| 65 | − <span class="doc__pop-panel" aria-hidden="true" inert> | |
| 66 | − {agentLogos.map((n, i) => ( | |
| 67 | − <span class="doc__pop-tile" style={`--i:${i}`}><img src={logo(n)} alt="" width="18" height="18" /></span> | |
| 68 | − ))} | |
| 69 | − <span class="doc__pop-pill" style={`--i:${agentLogos.length}`}>any MCP client</span> | |
| 70 | − </span> | |
| 71 | − </span> | |
| 72 | − {" "}the Executor tool. | |
| 73 | − </span> | |
| 74 | − </li> | |
| 75 | − <li> | |
| 76 | − <span class="doc__num">03</span> | |
| 77 | − <span>That's it.{" "}<a href="#install">Get started.</a></span> | |
| 78 | − </li> | |
| 79 | − </ol> | |
| 80 | − <p class="doc__subhead">One MCP. All your accounts.</p> | |
| 81 | − <div class="doc__tabs" role="tablist" aria-label="How to run Executor"> | |
| 82 | − <button type="button" role="tab" aria-selected="true" data-doc-tab="cloud">Cloud</button> | |
| 83 | − <button type="button" role="tab" aria-selected="false" data-doc-tab="local">Desktop / CLI</button> | |
| 84 | − <button type="button" role="tab" aria-selected="false" data-doc-tab="self">Self-hosted</button> | |
| 85 | − </div> | |
| 86 | − <div class="doc__tabpanes"> | |
| 87 | − <div role="tabpanel" data-doc-pane="cloud"> | |
| 88 | − <p>Sign in, point your agent at one URL, done. Free for up to three people and 100,000 executions a month.</p> | |
| 89 | − <div class="doc__actions"><a href="/cloud" class="btn-primary">Start on Cloud</a><button type="button" class="btn-secondary copy-btn doc__copy" data-copy={setupPrompt} data-event="marketing_setup_prompt_copied"> | |
| 90 | − <span class="doc__copy-logos" aria-hidden="true"> | |
| 91 | − {agentLogosShort.map((n) => <img src={logo(n)} alt="" width="14" height="14" />)} | |
| 92 | − </span> | |
| 93 | − <span>Copy setup prompt</span> | |
| 94 | − <span class="copy-icons" aria-hidden="true"> | |
| 95 | − <svg class="copy-icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg> | |
| 96 | − <svg class="copy-icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" 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"></path></svg> | |
| 97 | − </span> | |
| 98 | − </button></div> | |
| 99 | − </div> | |
| 100 | − <div role="tabpanel" data-doc-pane="local" data-inactive aria-hidden="true" inert> | |
| 101 | − <p>Nothing leaves your machine. A desktop app for Mac, Windows, and Linux, or a CLI that runs in the background. Same local endpoint either way.</p> | |
| 102 | − <div class="doc__actions"><span class="doc__dl" data-download> | |
| 103 | − <a data-os="mac-arm64" href={`${gh}/releases/latest/download/executor-desktop-mac-arm64.dmg`} class="btn-primary">Download for Mac</a> | |
| 104 | − <a data-os="mac-x64" href={`${gh}/releases/latest/download/executor-desktop-mac-x64.dmg`} class="btn-primary">Download for Mac (Intel)</a> | |
| 105 | − <a data-os="win-x64" href={`${gh}/releases/latest/download/executor-desktop-win-x64.exe`} class="btn-primary">Download for Windows</a> | |
| 106 | − <a data-os="linux-x64" href={`${gh}/releases/latest/download/executor-desktop-linux-x86_64.AppImage`} class="btn-primary">Download for Linux</a> | |
| 107 | − <details class="download-other doc__dl-other hidden" data-download-other> | |
| 108 | − <summary>Other platforms ▾</summary> | |
| 109 | − <div class="download-other-list" data-download-other-list></div> | |
| 110 | − </details> | |
| 111 | − </span><code class="doc__cmd">npm i -g executor</code><button type="button" class="btn-secondary copy-btn doc__copy" data-copy={setupPrompt} data-event="marketing_setup_prompt_copied"> | |
| 112 | − <span class="doc__copy-logos" aria-hidden="true"> | |
| 113 | − {agentLogosShort.map((n) => <img src={logo(n)} alt="" width="14" height="14" />)} | |
| 114 | − </span> | |
| 115 | − <span>Copy setup prompt</span> | |
| 116 | − <span class="copy-icons" aria-hidden="true"> | |
| 117 | − <svg class="copy-icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg> | |
| 118 | − <svg class="copy-icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" 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"></path></svg> | |
| 119 | − </span> | |
| 120 | − </button></div> | |
| 121 | − </div> | |
| 122 | − <div role="tabpanel" data-doc-pane="self" data-inactive aria-hidden="true" inert> | |
| 123 | − <p>One Docker image on your own box. It is the same code Cloud runs.</p> | |
| 124 | − <div class="doc__actions"><a href="https://executor.sh/docs/hosted/docker" class="btn-primary">Self-hosting docs</a><a href={gh} class="btn-secondary">Source on GitHub</a></div> | |
| 125 | − </div> | |
| 29 | + <IntroSteps /> | |
| 30 | + <p class="doc__subhead">One MCP. All your accounts. Free for up to three people.</p> | |
| 31 | + <div class="doc__actions"> | |
| 32 | + <a href="/cloud" class="btn-primary">Start on Cloud</a> | |
| 33 | + <CopyPrompt /> | |
| 126 | 34 | </div> |
| 35 | + <p class="doc__subhead doc__subhead--fine"> | |
| 36 | + Rather keep it on your machine? <a href="#install">Desktop, CLI, or self-hosted ↓</a> | |
| 37 | + </p> | |
| 127 | 38 | </header> |
| 128 | 39 | |
| 129 | 40 | <figure class="doc__fig doc__fig--hero"> |
| @@ -305,29 +216,6 @@ const { gh } = Astro.props; · 175 unmodified lines | ||
| 305 | 216 | </section> |
| 306 | 217 | </article> |
| 307 | 218 | |
| 308 | −<script> | |
| 309 | − for (const list of document.querySelectorAll<HTMLElement>(".doc__tabs")) { | |
| 310 | − const root = list.parentElement!; | |
| 311 | − list.addEventListener("click", (e) => { | |
| 312 | − const tab = (e.target as HTMLElement).closest<HTMLElement>("[data-doc-tab]"); | |
| 313 | − if (!tab) return; | |
| 314 | − const key = tab.dataset.docTab; | |
| 315 | − for (const t of list.querySelectorAll<HTMLElement>("[data-doc-tab]")) { | |
| 316 | − t.setAttribute("aria-selected", String(t === tab)); | |
| 317 | − } | |
| 318 | − for (const p of root.querySelectorAll<HTMLElement>("[data-doc-pane]")) { | |
| 319 | − const off = p.dataset.docPane !== key; | |
| 320 | − p.toggleAttribute("data-inactive", off); | |
| 321 | − p.toggleAttribute("inert", off); | |
| 322 | − p.setAttribute("aria-hidden", String(off)); | |
| 323 | − for (const el of p.querySelectorAll<HTMLElement>("a, button")) { | |
| 324 | − el.tabIndex = off ? -1 : 0; | |
| 325 | − } | |
| 326 | − } | |
| 327 | − }); | |
| 328 | − } | |
| 329 | −</script> | |
| 330 | − | |
| 331 | 219 | <script> |
| 332 | 220 | // Show one download button for the visitor's OS and tuck the rest behind |
| 333 | 221 | // "Other platforms". Without JS or on an unknown OS, all four stay visible. |
| @@ -0,0 +1,54 @@ | ||
| 1 | +--- | |
| 2 | +// The three numbered intro steps with the hover-reveal logo panels. | |
| 3 | +const logos = import.meta.glob<string>("../../assets/logos/*.svg", { | |
| 4 | + eager: true, | |
| 5 | + query: "?url", | |
| 6 | + import: "default", | |
| 7 | +}); | |
| 8 | +const logo = (name: string): string => { | |
| 9 | + const hit = Object.entries(logos).find(([k]) => k.endsWith(`/${name}.svg`)); | |
| 10 | + if (!hit) throw new Error(`missing logo ${name}`); | |
| 11 | + return hit[1]; | |
| 12 | +}; | |
| 13 | +const toolLogos = ["github", "linear", "sentry", "stripe", "notion", "slack", "jira", "gmail"]; | |
| 14 | +const agentLogos = ["claude", "codex", "cursor", "chatgpt", "gemini", "hermes", "copilot", "windsurf", "opencode", "zed", "grok"]; | |
| 15 | +--- | |
| 16 | + | |
| 17 | +<ol class="doc__steps"> | |
| 18 | + <li> | |
| 19 | + <span class="doc__num">01</span> | |
| 20 | + <span> | |
| 21 | + Connect{" "} | |
| 22 | + <span class="doc__pop" tabindex="0"> | |
| 23 | + <span class="doc__pop-word">everything</span> | |
| 24 | + <span class="doc__pop-panel" aria-hidden="true" inert> | |
| 25 | + {toolLogos.map((n, i) => ( | |
| 26 | + <span class="doc__pop-tile" style={`--i:${i}`}><img src={logo(n)} alt="" width="18" height="18" /></span> | |
| 27 | + ))} | |
| 28 | + <span class="doc__pop-pill" style={`--i:${toolLogos.length}`}>any OpenAPI, GraphQL, or MCP</span> | |
| 29 | + </span> | |
| 30 | + </span> | |
| 31 | + {" "}to Executor. | |
| 32 | + </span> | |
| 33 | + </li> | |
| 34 | + <li> | |
| 35 | + <span class="doc__num">02</span> | |
| 36 | + <span> | |
| 37 | + Give{" "} | |
| 38 | + <span class="doc__pop" tabindex="0"> | |
| 39 | + <span class="doc__pop-word">your agent</span> | |
| 40 | + <span class="doc__pop-panel" aria-hidden="true" inert> | |
| 41 | + {agentLogos.map((n, i) => ( | |
| 42 | + <span class="doc__pop-tile" style={`--i:${i}`}><img src={logo(n)} alt="" width="18" height="18" /></span> | |
| 43 | + ))} | |
| 44 | + <span class="doc__pop-pill" style={`--i:${agentLogos.length}`}>any MCP client</span> | |
| 45 | + </span> | |
| 46 | + </span> | |
| 47 | + {" "}the Executor tool. | |
| 48 | + </span> | |
| 49 | + </li> | |
| 50 | + <li> | |
| 51 | + <span class="doc__num">03</span> | |
| 52 | + <span>That's it.</span> | |
| 53 | + </li> | |
| 54 | +</ol> | |
| @@ -1,9 +1,10 @@ | ||
| 1 | 1 | --- |
| 2 | 2 | // Rail, variant B: "Gridline rail". |
| 3 | −// Compact and technical: mark-only wordmark, a tight link stack, an outlined | |
| 4 | −// CTA, and a numbered mono index. A full-height hairline closes the right edge. | |
| 3 | +// Compact and technical: mark-only wordmark, a tight link stack, two quiet | |
| 4 | +// meta lines (open source, YC), and a numbered mono index. The rail does not | |
| 5 | +// sell; the hero owns the call to action. A full-height hairline closes the | |
| 6 | +// right edge. | |
| 5 | 7 | import "../../styles/variants/rail.css"; |
| 6 | −import ycBackedBy from "../../assets/yc-backed-by.svg?url"; | |
| 7 | 8 | import rhysAvatar from "../../assets/rhys-sullivan.jpg"; |
| 8 | 9 | |
| 9 | 10 | const X_URL = "https://x.com/RhysSullivan"; |
| @@ -47,38 +48,13 @@ const pad = (index: number) => String(index + 1).padStart(2, "0"); · 37 unmodified lines | ||
| 47 | 48 | <a href={SIGN_IN} class="rail__link rail__link--keep">Sign in</a> |
| 48 | 49 | </nav> |
| 49 | 50 | |
| 50 | − <div class="rail__cta"> | |
| 51 | − <a href="#install" class="rail__box rail__box--primary">Get started</a> | |
| 52 | − <a | |
| 53 | − href={GH} | |
| 54 | − target="_blank" | |
| 55 | − rel="noopener" | |
| 56 | − class="rail__box gh-star" | |
| 57 | − aria-label="Star Executor on GitHub" | |
| 58 | − > | |
| 59 | − <svg | |
| 60 | − class="gh-star__ico" | |
| 61 | − width="13" | |
| 62 | − height="13" | |
| 63 | − viewBox="0 0 16 16" | |
| 64 | − aria-hidden="true" | |
| 65 | − > | |
| 66 | − <path | |
| 67 | − fill="#e3b341" | |
| 68 | − d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.75.75 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25z" | |
| 69 | − ></path> | |
| 70 | − </svg> | |
| 71 | − <span>Star on GitHub</span> | |
| 72 | − {stars != null && <span class="gh-star__n">{stars}</span>} | |
| 51 | + <div class="rail__meta"> | |
| 52 | + <a href={GH} target="_blank" rel="noopener" class="rail__meta-line"> | |
| 53 | + <span>Open source</span> | |
| 54 | + {stars != null && <span class="rail__meta-n">★ {stars}</span>} | |
| 73 | 55 | </a> |
| 74 | − <a | |
| 75 | − href="https://www.ycombinator.com" | |
| 76 | − target="_blank" | |
| 77 | − rel="noopener noreferrer" | |
| 78 | − class="rail__box rail__box--yc" | |
| 79 | − aria-label="Backed by Y Combinator" | |
| 80 | − > | |
| 81 | − <img src={ycBackedBy} alt="Backed by Y Combinator" width="350" height="125" /> | |
| 56 | + <a href="https://www.ycombinator.com" target="_blank" rel="noopener noreferrer" class="rail__meta-line"> | |
| 57 | + <span>Backed by Y Combinator</span> | |
| 82 | 58 | </a> |
| 83 | 59 | </div> |
| 84 | 60 | |
| @@ -299,48 +299,6 @@ · 298 unmodified lines | ||
| 299 | 299 | text-decoration-color: var(--color-rule-strong); |
| 300 | 300 | } |
| 301 | 301 | |
| 302 | −.doc__tabs { | |
| 303 | − display: inline-flex; | |
| 304 | − gap: 0; | |
| 305 | − border: 1px solid var(--color-rule); | |
| 306 | − border-radius: 8px; | |
| 307 | − padding: 3px; | |
| 308 | − margin-bottom: 1rem; | |
| 309 | − background: var(--color-surface-2); | |
| 310 | −} | |
| 311 | −.doc__tabs button { | |
| 312 | − font: inherit; | |
| 313 | − font-size: 13.5px; | |
| 314 | − font-weight: 500; | |
| 315 | − padding: 0.4rem 0.85rem; | |
| 316 | − border: 0; | |
| 317 | − border-radius: 6px; | |
| 318 | − background: transparent; | |
| 319 | − color: var(--color-ink-2); | |
| 320 | − cursor: pointer; | |
| 321 | −} | |
| 322 | −.doc__tabs button[aria-selected="true"] { | |
| 323 | − background: var(--color-surface); | |
| 324 | − color: var(--color-ink); | |
| 325 | − box-shadow: | |
| 326 | − 0 1px 2px rgba(0, 0, 0, 0.06), | |
| 327 | − 0 0 0 1px var(--color-rule); | |
| 328 | −} | |
| 329 | −.doc__tabpanes p { | |
| 330 | − font-size: 15.5px; | |
| 331 | − line-height: 1.6; | |
| 332 | − color: var(--color-ink-2); | |
| 333 | − margin-bottom: 1rem; | |
| 334 | − max-width: 60ch; | |
| 335 | − /* Always two lines tall, so the buttons sit at the same height on every tab. */ | |
| 336 | − min-height: calc(2 * 1.6em); | |
| 337 | −} | |
| 338 | −.doc__tabpanes .doc__actions { | |
| 339 | − min-height: 46px; | |
| 340 | −} | |
| 341 | −.doc__tabpanes .doc__actions { | |
| 342 | − margin-bottom: 1.1rem; | |
| 343 | −} | |
| 344 | 302 | .doc__cmd { |
| 345 | 303 | font-family: var(--font-mono); |
| 346 | 304 | font-size: 12.5px; |
| @@ -737,20 +695,6 @@ a.doc__term-v:hover { · 390 unmodified lines | ||
| 737 | 695 | display: none; |
| 738 | 696 | } |
| 739 | 697 | |
| 740 | −/* Tab panes share one grid cell, so the tallest one sets the height and | |
| 741 | − switching tabs never shifts the content below. Hidden panes stay in the | |
| 742 | − layout but are invisible and inert. */ | |
| 743 | −.doc__tabpanes { | |
| 744 | − display: grid; | |
| 745 | −} | |
| 746 | −.doc__tabpanes > [role="tabpanel"] { | |
| 747 | − grid-area: 1 / 1; | |
| 748 | −} | |
| 749 | −.doc__tabpanes > [role="tabpanel"][data-inactive] { | |
| 750 | − visibility: hidden; | |
| 751 | − pointer-events: none; | |
| 752 | −} | |
| 753 | − | |
| 754 | 698 | .doc__subhead { |
| 755 | 699 | font-size: 17px; |
| 756 | 700 | line-height: 1.5; |
| @@ -858,25 +802,12 @@ a.doc__term-v:hover { · 101 unmodified lines | ||
| 858 | 802 | width: auto; |
| 859 | 803 | max-width: none; |
| 860 | 804 | } |
| 861 | − .doc__tabs { | |
| 862 | − display: flex; | |
| 863 | − width: 100%; | |
| 864 | − } | |
| 865 | − .doc__tabs button { | |
| 866 | − flex: 1; | |
| 867 | − padding: 0.45rem 0.4rem; | |
| 868 | − font-size: 13px; | |
| 869 | − white-space: nowrap; | |
| 870 | − } | |
| 871 | − .doc__tabpanes p { | |
| 872 | − min-height: 0; | |
| 873 | − } | |
| 874 | 805 | .doc__actions { |
| 875 | 806 | flex-direction: column; |
| 876 | 807 | align-items: stretch; |
| 877 | 808 | } |
| 878 | 809 | .doc__actions > .btn-primary, |
| 879 | − .doc__actions > .btn-secondary, | |
| 810 | + .doc__actions > .btn-ghost, | |
| 880 | 811 | .doc__actions > .doc__cmd, |
| 881 | 812 | .doc__actions > .doc__dl { |
| 882 | 813 | width: 100%; |
| @@ -936,12 +867,6 @@ a.doc__term-v:hover { · 53 unmodified lines | ||
| 936 | 867 | justify-content: center; |
| 937 | 868 | gap: 0.6rem; |
| 938 | 869 | } |
| 939 | − .doc__tabs { | |
| 940 | − margin-inline: auto; | |
| 941 | − } | |
| 942 | − .doc__tabpanes p { | |
| 943 | − margin-inline: auto; | |
| 944 | − } | |
| 945 | 870 | .doc__actions { |
| 946 | 871 | justify-content: center; |
| 947 | 872 | } |
| @@ -1015,3 +940,43 @@ a.doc__term-v:hover { · 67 unmodified lines | ||
| 1015 | 940 | max-width: 720px; |
| 1016 | 941 | } |
| 1017 | 942 | } |
| 943 | + | |
| 944 | +/* ── Fine print under the hero buttons ── */ | |
| 945 | +.doc__subhead--fine { | |
| 946 | + font-size: 15px; | |
| 947 | + color: var(--color-ink-3); | |
| 948 | + max-width: 52ch; | |
| 949 | +} | |
| 950 | +.doc__subhead--fine a { | |
| 951 | + color: var(--color-ink-2); | |
| 952 | + text-decoration: underline; | |
| 953 | + text-underline-offset: 3px; | |
| 954 | + text-decoration-color: var(--color-rule-strong); | |
| 955 | +} | |
| 956 | +.doc__subhead--fine a:hover { | |
| 957 | + color: var(--color-ink); | |
| 958 | +} | |
| 959 | + | |
| 960 | +/* ── Ghost copy button: text weight of a link, hit area of a button ── */ | |
| 961 | +.btn-ghost { | |
| 962 | + display: inline-flex; | |
| 963 | + align-items: center; | |
| 964 | + gap: 0.4rem; | |
| 965 | + background: transparent; | |
| 966 | + color: var(--color-ink-2); | |
| 967 | + border: 1px dashed var(--color-rule-strong); | |
| 968 | + border-radius: 8px; | |
| 969 | + padding: 0.7rem 1.1rem; | |
| 970 | + font-family: inherit; | |
| 971 | + font-weight: 500; | |
| 972 | + font-size: 14.5px; | |
| 973 | + letter-spacing: -0.005em; | |
| 974 | + cursor: pointer; | |
| 975 | + transition: | |
| 976 | + color 0.15s ease, | |
| 977 | + border-color 0.15s ease; | |
| 978 | +} | |
| 979 | +.btn-ghost:hover { | |
| 980 | + color: var(--color-ink); | |
| 981 | + border-color: var(--color-ink-3); | |
| 982 | +} | |
| @@ -133,75 +133,31 @@ · 132 unmodified lines | ||
| 133 | 133 | .rail--b .rail__link[aria-current="page"]::before { |
| 134 | 134 | opacity: 1; |
| 135 | 135 | } |
| 136 | −.rail--b .rail__cta { | |
| 136 | +/* ── Quiet rail meta: star count and YC as text lines, not boxes ── */ | |
| 137 | +.rail--b .rail__meta { | |
| 137 | 138 | display: grid; |
| 138 | − gap: 8px; | |
| 139 | + gap: 4px; | |
| 139 | 140 | margin-top: 16px; |
| 140 | − --rail-box-h: 36px; | |
| 141 | + padding-top: 12px; | |
| 142 | + border-top: 1px solid var(--color-rule); | |
| 141 | 143 | } |
| 142 | −/* One stack, one button language: same height, width, radius, hairline | |
| 143 | − border, and sans label. The primary is filled ink; the others are quiet. */ | |
| 144 | −.rail--b .rail__box { | |
| 144 | +.rail--b .rail__meta-line { | |
| 145 | 145 | display: flex; |
| 146 | 146 | align-items: center; |
| 147 | − justify-content: center; | |
| 148 | − gap: 7px; | |
| 149 | − height: var(--rail-box-h); | |
| 150 | − width: 100%; | |
| 151 | − box-sizing: border-box; | |
| 152 | − padding: 0 12px; | |
| 153 | − border: 1px solid var(--color-rule-strong); | |
| 154 | − border-radius: 6px; | |
| 155 | − background: var(--color-surface); | |
| 156 | − font-family: var(--font-sans); | |
| 157 | − font-size: 13px; | |
| 158 | − font-weight: 500; | |
| 159 | − color: var(--color-ink); | |
| 160 | − overflow: hidden; | |
| 161 | − transition: | |
| 162 | − background 0.15s ease, | |
| 163 | − color 0.15s ease, | |
| 164 | − border-color 0.15s ease; | |
| 165 | −} | |
| 166 | −.rail--b .rail__box:hover { | |
| 167 | − border-color: var(--color-ink); | |
| 168 | −} | |
| 169 | −.rail--b .rail__box--primary { | |
| 170 | − background: var(--color-ink); | |
| 171 | − border-color: var(--color-ink); | |
| 172 | − color: var(--color-surface); | |
| 173 | −} | |
| 174 | −.rail--b .rail__box--primary:hover { | |
| 175 | − background: #1f1f24; | |
| 147 | + justify-content: space-between; | |
| 148 | + gap: 8px; | |
| 149 | + font-size: 12.5px; | |
| 150 | + color: var(--color-ink-3); | |
| 151 | + line-height: 1.6; | |
| 176 | 152 | } |
| 177 | −/* Star pill: same box, sans label, count in mono like the app's metadata. */ | |
| 178 | −.rail--b .rail__box.gh-star { | |
| 153 | +.rail--b .rail__meta-line:hover { | |
| 179 | 154 | color: var(--color-ink); |
| 180 | 155 | } |
| 181 | −.rail--b .rail__box.gh-star .gh-star__ico { | |
| 182 | − width: 12px; | |
| 183 | − height: 12px; | |
| 184 | −} | |
| 185 | −.rail--b .rail__box.gh-star .gh-star__n { | |
| 156 | +.rail--b .rail__meta-n { | |
| 186 | 157 | font-family: var(--font-mono); |
| 187 | − font-weight: 400; | |
| 188 | − font-size: 12px; | |
| 189 | − color: var(--color-ink-3); | |
| 190 | − border-left: 1px solid var(--color-rule); | |
| 191 | − padding-left: 8px; | |
| 192 | − margin-left: 1px; | |
| 193 | −} | |
| 194 | −/* The YC badge is a rounded cream card at 350x125; letting it fill the box | |
| 195 | − edge to edge keeps the badge's own corners hidden behind ours. */ | |
| 196 | −.rail--b .rail__box--yc { | |
| 197 | − padding: 0; | |
| 198 | − background: #f6f6ef; | |
| 199 | −} | |
| 200 | −.rail--b .rail__box--yc img { | |
| 201 | − height: 100%; | |
| 202 | − width: auto; | |
| 203 | − display: block; | |
| 158 | + font-size: 11.5px; | |
| 204 | 159 | } |
| 160 | + | |
| 205 | 161 | .rail--b .rail__toc { |
| 206 | 162 | gap: 8px; |
| 207 | 163 | } |
| @@ -314,8 +270,7 @@ · 106 unmodified lines | ||
| 314 | 270 | .rail .rail__foot, |
| 315 | 271 | .rail .rail__contact, |
| 316 | 272 | .rail .rail__secondary, |
| 317 | − .rail .gh-star, | |
| 318 | − .rail .rail__box--yc { | |
| 273 | + .rail .rail__meta { | |
| 319 | 274 | display: none; |
| 320 | 275 | } |
| 321 | 276 | .rail .rail__stack { |
| @@ -337,16 +292,6 @@ · 15 unmodified lines | ||
| 337 | 292 | .rail .rail__link::before { |
| 338 | 293 | display: none; |
| 339 | 294 | } |
| 340 | − .rail .rail__cta { | |
| 341 | − display: block; | |
| 342 | − margin-top: 0; | |
| 343 | − } | |
| 344 | − .rail--b .rail__box--primary { | |
| 345 | − width: auto; | |
| 346 | − height: 32px; | |
| 347 | − padding: 0 12px; | |
| 348 | − font-size: 13px; | |
| 349 | − } | |
| 350 | 295 | } |
| 351 | 296 | @media (max-width: 479.98px) { |
| 352 | 297 | .rail .rail__nav .rail__link:not(.rail__link--keep) { |