History

Redesign marketing homepage (#1082)

* Redesign marketing homepage Rework the landing page: a For humans / For agents hero toggle (Run locally primary CTA plus a copyable agent-setup prompt), an interactive context efficiency demo (connect services and watch the system prompt balloon into thousands of tools versus one execute tool), a pricing section, reworked Pick your path cards (Cloud / Desktop / CLI), a Backed by Y Combinator badge, and copy, CTA, and layout cleanups throughout. * Marketing: remove the trust-strip section and the founder signature * Marketing: use svgl.app brand logos for the demo and hero icons Replace the hand-maintained monochrome icon paths with current full-color svgl.app logos served from public/logos and rendered as <img>. Covers the context-efficiency demo's service icons and the hero beam (Claude, Cursor, Codex, GitHub, Sentry, Linear). svgl has no Jira icon, so jira.svg is the Jira mark in Jira blue. * Marketing: enlarge the Backed by YC badge for legibility * Marketing: mark Trace every call as coming soon Remove the run-trace mockup, grey out the card, and add a Coming soon badge. * Marketing: drop the eyebrow labels on the Pick your path cards * Marketing: default the hero toggle to For agents

Rhys Sullivan committed 4 months ago

Commit f46ab131 parent 934700316 files · +2298 −390Browse files

Unified · Split

16 files
  1. apps/marketing/public/logos/claude.svg +1 −0
  2. apps/marketing/public/logos/codex.svg +1 −0
  3. apps/marketing/public/logos/cursor.svg +12 −0
  4. apps/marketing/public/logos/github.svg +1 −0
  5. apps/marketing/public/logos/gmail.svg +11 −0
  6. apps/marketing/public/logos/jira.svg +1 −0
  7. apps/marketing/public/logos/linear.svg +1 −0
  8. apps/marketing/public/logos/notion.svg +1 −0
  9. apps/marketing/public/logos/sentry.svg +1 −0
  10. apps/marketing/public/logos/slack.svg +24 −0
  11. apps/marketing/public/logos/stripe.svg +1 −0
  12. apps/marketing/public/yc-backed-by.svg +30 −0
  13. apps/marketing/src/components/animated-beam-demo.tsx +11 −31
  14. apps/marketing/src/components/context-bloat-demo.tsx +438 −0
  15. apps/marketing/src/pages/index.astro +700 −344
  16. apps/marketing/src/styles/global.css +1064 −15
apps/marketing/public/logos/claude.svg+1−0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="256" height="257" preserveAspectRatio="xMidYMid" viewBox="0 0 256 257"><path fill="#D97757" d="m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z"/></svg>
apps/marketing/public/logos/codex.svg+1−0
@@ -0,0 +1 @@
1+<svg fill="#111" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Codex</title><path clip-rule="evenodd" d="M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z"></path></svg>
apps/marketing/public/logos/cursor.svg+12−0
@@ -0,0 +1,12 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<svg id="Ebene_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 466.73 532.09">
3+ <!-- Generator: Adobe Illustrator 29.6.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 9) -->
4+ <defs>
5+ <style>
6+ .st0 {
7+ fill: #26251e;
8+ }
9+ </style>
10+ </defs>
11+ <path class="st0" d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"/>
12+</svg>
apps/marketing/public/logos/github.svg+1−0
@@ -0,0 +1 @@
1+<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" fill="none"><path fill="#1b1f23" fill-rule="evenodd" d="M512 0C229.12 0 0 229.12 0 512c0 226.56 146.56 417.92 350.08 485.76 25.6 4.48 35.2-10.88 35.2-24.32 0-12.16-.64-52.48-.64-95.36-128.64 23.68-161.92-31.36-172.16-60.16-5.76-14.72-30.72-60.16-52.48-72.32-17.92-9.6-43.52-33.28-.64-33.92 40.32-.64 69.12 37.12 78.72 52.48 46.08 77.44 119.68 55.68 149.12 42.24 4.48-33.28 17.92-55.68 32.64-68.48-113.92-12.8-232.96-56.96-232.96-252.8 0-55.68 19.84-101.76 52.48-137.6-5.12-12.8-23.04-65.28 5.12-135.68 0 0 42.88-13.44 140.8 52.48 40.96-11.52 84.48-17.28 128-17.28s87.04 5.76 128 17.28c97.92-66.56 140.8-52.48 140.8-52.48 28.16 70.4 10.24 122.88 5.12 135.68 32.64 35.84 52.48 81.28 52.48 137.6 0 196.48-119.68 240-233.6 252.8 18.56 16 34.56 46.72 34.56 94.72 0 68.48-.64 123.52-.64 140.8 0 13.44 9.6 29.44 35.2 24.32C877.44 929.92 1024 737.92 1024 512 1024 229.12 794.88 0 512 0" clip-rule="evenodd"/></svg>
apps/marketing/public/logos/gmail.svg+11−0
@@ -0,0 +1,11 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 49.4 512 399.42">
2+ <g fill="none" fill-rule="evenodd">
3+ <g fill-rule="nonzero">
4+ <path fill="#4285f4" d="M34.91 448.818h81.454V251L0 163.727V413.91c0 19.287 15.622 34.91 34.91 34.91z"/>
5+ <path fill="#34a853" d="M395.636 448.818h81.455c19.287 0 34.909-15.622 34.909-34.909V163.727L395.636 251z"/>
6+ <path fill="#fbbc04" d="M395.636 99.727V251L512 163.727v-46.545c0-43.142-49.25-67.782-83.782-41.891z"/>
7+ </g>
8+ <path fill="#ea4335" d="M116.364 251V99.727L256 204.455 395.636 99.727V251L256 355.727z"/>
9+ <path fill="#c5221f" fill-rule="nonzero" d="M0 117.182v46.545L116.364 251V99.727L83.782 75.291C49.25 49.4 0 74.04 0 117.18z"/>
10+ </g>
11+</svg>
apps/marketing/public/logos/jira.svg+1−0
@@ -0,0 +1 @@
1+<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#2684FF" d="M11.571 11.513H0a5.218 5.218 0 0 0 5.232 5.215h2.13v2.057A5.215 5.215 0 0 0 12.575 24V12.518a1.005 1.005 0 0 0-1.005-1.005zm5.723-5.756H5.736a5.215 5.215 0 0 0 5.215 5.214h2.129v2.058a5.218 5.218 0 0 0 5.215 5.214V6.758a1.001 1.001 0 0 0-1.001-1.001zM23.013 0H11.455a5.215 5.215 0 0 0 5.215 5.215h2.129v2.057A5.215 5.215 0 0 0 24 12.483V1.005A1.001 1.001 0 0 0 23.013 0Z"/></svg>
apps/marketing/public/logos/linear.svg+1−0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" fill="none" viewBox="0 0 100 100"><path fill="#5E6AD2" d="M1.225 61.523c-.222-.949.908-1.546 1.597-.857l36.512 36.512c.69.69.092 1.82-.857 1.597-18.425-4.323-32.93-18.827-37.252-37.252ZM.002 46.889a.99.99 0 0 0 .29.76L52.35 99.71c.201.2.478.307.76.29 2.37-.149 4.695-.46 6.963-.927.765-.157 1.03-1.096.478-1.648L2.576 39.448c-.552-.551-1.491-.286-1.648.479a50.067 50.067 0 0 0-.926 6.962ZM4.21 29.705a.988.988 0 0 0 .208 1.1l64.776 64.776c.289.29.726.375 1.1.208a49.908 49.908 0 0 0 5.185-2.684.981.981 0 0 0 .183-1.54L8.436 24.336a.981.981 0 0 0-1.541.183 49.896 49.896 0 0 0-2.684 5.185Zm8.448-11.631a.986.986 0 0 1-.045-1.354C21.78 6.46 35.111 0 49.952 0 77.592 0 100 22.407 100 50.048c0 14.84-6.46 28.172-16.72 37.338a.986.986 0 0 1-1.354-.045L12.659 18.074Z"/></svg>
apps/marketing/public/logos/notion.svg+1−0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="256" height="268" preserveAspectRatio="xMidYMid" viewBox="0 0 256 268"><path fill="#FFF" d="M16.092 11.538 164.09.608c18.179-1.56 22.85-.508 34.28 7.801l47.243 33.282C253.406 47.414 256 48.975 256 55.207v182.527c0 11.439-4.155 18.205-18.696 19.24L65.44 267.378c-10.913.517-16.11-1.043-21.825-8.327L8.826 213.814C2.586 205.487 0 199.254 0 191.97V29.726c0-9.352 4.155-17.153 16.092-18.188Z"/><path d="M164.09.608 16.092 11.538C4.155 12.573 0 20.374 0 29.726v162.245c0 7.284 2.585 13.516 8.826 21.843l34.789 45.237c5.715 7.284 10.912 8.844 21.825 8.327l171.864-10.404c14.532-1.035 18.696-7.801 18.696-19.24V55.207c0-5.911-2.336-7.614-9.21-12.66l-1.185-.856L198.37 8.409C186.94.1 182.27-.952 164.09.608ZM69.327 52.22c-14.033.945-17.216 1.159-25.186-5.323L23.876 30.778c-2.06-2.086-1.026-4.69 4.163-5.207l142.274-10.395c11.947-1.043 18.17 3.12 22.842 6.758l24.401 17.68c1.043.525 3.638 3.637.517 3.637L71.146 52.095l-1.819.125Zm-16.36 183.954V81.222c0-6.767 2.077-9.887 8.3-10.413L230.02 60.93c5.724-.517 8.31 3.12 8.31 9.879v153.917c0 6.767-1.044 12.49-10.387 13.008l-161.487 9.361c-9.343.517-13.489-2.594-13.489-10.921ZM212.377 89.53c1.034 4.681 0 9.362-4.681 9.897l-7.783 1.542v114.404c-6.758 3.637-12.981 5.715-18.18 5.715-8.308 0-10.386-2.604-16.609-10.396l-50.898-80.079v77.476l16.1 3.646s0 9.362-12.989 9.362l-35.814 2.077c-1.043-2.086 0-7.284 3.63-8.318l9.351-2.595V109.823l-12.98-1.052c-1.044-4.68 1.55-11.439 8.826-11.965l38.426-2.585 52.958 81.113v-71.76l-13.498-1.552c-1.043-5.733 3.111-9.896 8.3-10.404l35.84-2.087Z"/></svg>
apps/marketing/public/logos/sentry.svg+1−0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="256" height="227" preserveAspectRatio="xMidYMid" viewBox="0 0 256 227"><path fill="#362D59" d="M148.368 12.403a23.935 23.935 0 0 0-41.003 0L73.64 70.165c52.426 26.174 87.05 78.177 90.975 136.642h-23.679c-3.918-50.113-34.061-94.41-79.238-116.448l-31.213 53.97a81.595 81.595 0 0 1 47.307 62.375h-54.38a3.895 3.895 0 0 1-3.178-5.69l15.069-25.626a55.046 55.046 0 0 0-17.221-9.738L3.167 191.277a23.269 23.269 0 0 0 8.662 31.982 23.884 23.884 0 0 0 11.583 3.075h74.471a99.432 99.432 0 0 0-41.003-88.72l11.84-20.5c35.679 24.504 55.754 66.038 52.79 109.22h63.094c2.99-65.43-29.047-127.512-84.107-162.986l23.935-41.002a3.947 3.947 0 0 1 5.382-1.384c2.716 1.486 103.993 178.208 105.89 180.258a3.895 3.895 0 0 1-3.486 5.792h-24.396c.307 6.526.307 13.035 0 19.528h24.499A23.528 23.528 0 0 0 256 202.91a23.015 23.015 0 0 0-3.178-11.685L148.368 12.403Z"/></svg>
apps/marketing/public/logos/slack.svg+24−0
@@ -0,0 +1,24 @@
1+<svg
2+ enable-background="new 0 0 2447.6 2452.5"
3+ viewBox="0 0 2447.6 2452.5"
4+ xmlns="http://www.w3.org/2000/svg"
5+>
6+ <g clip-rule="evenodd" fill-rule="evenodd">
7+ <path
8+ d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z"
9+ fill="#36c5f0"
10+ />
11+ <path
12+ d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z"
13+ fill="#2eb67d"
14+ />
15+ <path
16+ d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z"
17+ fill="#ecb22e"
18+ />
19+ <path
20+ d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0"
21+ fill="#e01e5a"
22+ />
23+ </g>
24+</svg>
apps/marketing/public/logos/stripe.svg+1−0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" fill="none" viewBox="100 100 312 312"><path fill="#533afd" fill-rule="evenodd" d="m120 392 272-57.683V120l-272 58.357z" clip-rule="evenodd"/></svg>
apps/marketing/public/yc-backed-by.svg+30−0
@@ -0,0 +1,30 @@
1+<svg width="358" height="133" viewBox="0 0 358 133" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<g filter="url(#filter0_d_1_82)">
3+<rect x="4" width="350" height="125" rx="10" fill="#F6F6EF"/>
4+<path d="M130.178 31.768C131.474 31.096 132.266 29.944 132.266 28.36C132.266 25.672 130.01 23.68 127.034 23.68H120.026V40H127.322C130.298 40 132.53 37.984 132.53 35.296C132.53 33.592 131.618 32.416 130.178 31.768ZM127.034 26.08C128.738 26.08 129.746 27.04 129.746 28.36C129.746 29.656 128.714 30.64 127.034 30.64H122.546V26.08H127.034ZM127.322 37.6H122.546V33.04H127.322C129.002 33.04 130.01 33.976 130.01 35.296C130.01 36.616 129.002 37.6 127.322 37.6ZM140.55 27.736C138.75 27.736 136.878 28.744 135.846 30.256L137.79 31.624C138.366 30.64 139.542 29.92 140.742 29.92C142.206 29.92 142.926 30.952 143.118 32.08L139.374 32.8C137.262 33.208 135.414 34.312 135.414 36.616C135.414 38.92 137.238 40.24 139.35 40.24C141.03 40.24 142.446 39.4 143.166 38.344V40H145.566V32.68C145.566 29.704 143.55 27.736 140.55 27.736ZM139.926 38.032C138.702 38.032 137.814 37.408 137.814 36.52C137.814 35.584 138.798 35.08 139.83 34.84L143.166 34.168V35.032C143.166 36.88 141.63 38.032 139.926 38.032ZM158.123 36.208C157.379 37.312 156.155 37.96 154.883 37.96C152.771 37.96 151.235 36.208 151.235 33.976C151.235 31.744 152.771 29.992 154.883 29.992C156.155 29.992 157.379 30.64 158.123 31.744L160.139 30.568C158.987 28.84 157.043 27.712 154.859 27.712C151.379 27.712 148.835 30.496 148.835 33.976C148.835 37.456 151.379 40.24 154.859 40.24C157.043 40.24 158.987 39.112 160.139 37.384L158.123 36.208ZM174.088 28H170.92L165.688 33.448V23.2H163.288V40H165.688V36.448L167.608 34.504L171.28 40H174.088L169.36 32.752L174.088 28ZM186.545 32.968C186.137 29.296 183.617 27.76 181.049 27.76C177.569 27.76 175.265 30.544 175.265 34C175.265 37.456 177.569 40.24 181.049 40.24C183.233 40.24 185.297 39.112 186.329 37.384L184.313 36.208C183.737 37.24 182.465 37.96 181.025 37.96C179.249 37.96 178.097 36.904 177.785 35.104H186.545C186.617 34.336 186.617 33.616 186.545 32.968ZM180.977 30.04C182.465 30.04 184.073 30.88 184.145 32.944H177.761C178.073 31.144 179.153 30.04 180.977 30.04ZM198.459 23.2V29.728C197.787 28.6 196.515 27.76 194.787 27.76C191.715 27.76 189.387 30.352 189.363 34C189.339 37.72 191.715 40.24 194.763 40.24C196.587 40.24 197.835 39.328 198.459 38.128V40H200.859V23.2H198.459ZM195.171 37.96C193.083 37.96 191.763 36.256 191.763 34C191.763 31.744 193.083 30.04 195.171 30.04C197.259 30.04 198.579 31.744 198.579 34C198.579 36.256 197.259 37.96 195.171 37.96ZM224.47 34C224.446 30.352 222.118 27.76 219.046 27.76C217.318 27.76 216.046 28.6 215.374 29.728V23.2H212.974V40H215.374V38.128C215.998 39.328 217.246 40.24 219.07 40.24C222.118 40.24 224.494 37.72 224.47 34ZM218.662 37.96C216.574 37.96 215.254 36.256 215.254 34C215.254 31.744 216.574 30.04 218.662 30.04C220.75 30.04 222.07 31.744 222.07 34C222.07 36.256 220.75 37.96 218.662 37.96ZM236.419 28L232.915 36.424L229.171 28H226.531L231.643 39.448L229.411 44.8H231.955L238.891 28H236.419Z" fill="black"/>
5+<path fill-rule="evenodd" clip-rule="evenodd" d="M35 109H92.2727V49H35V109Z" fill="#FB651E"/>
6+<path fill-rule="evenodd" clip-rule="evenodd" d="M65.9631 81.9812V95.3125H61.1307V81.9812L48.7992 62.6875H54.6875L63.5648 76.9375L72.4063 62.6875H78.2946L65.9631 81.9812Z" fill="white"/>
7+<path fill-rule="evenodd" clip-rule="evenodd" d="M125.859 69.1219C120.561 69.1219 116.588 73.4157 116.588 78.9657C116.588 84.5157 120.561 88.8469 125.859 88.8469C129.259 88.8469 132.212 87.0094 133.805 83.9157L138.351 86.7469C135.81 91.3032 131.049 94.2657 125.859 94.2657C117.697 94.2469 111.218 87.4594 111.218 78.9657C111.218 70.4532 117.697 63.6657 125.859 63.6657C131.103 63.6657 135.81 66.5907 138.351 71.1844L133.805 74.0157C132.194 70.9219 129.259 69.1219 125.859 69.1219Z" fill="#231F20"/>
8+<path fill-rule="evenodd" clip-rule="evenodd" d="M155.819 82.8282C155.819 79.3594 153.403 76.7344 150.289 76.7344C147.085 76.7344 144.669 79.3594 144.669 82.8282C144.669 86.2969 147.085 88.9219 150.289 88.9219C153.349 88.9219 155.819 86.2969 155.819 82.8282ZM139.461 82.8282C139.461 76.5657 144.257 71.5407 150.289 71.5407C156.231 71.5407 161.028 76.5657 161.028 82.8282C161.028 89.0907 156.231 94.1157 150.289 94.1157C144.257 94.1157 139.461 89.0907 139.461 82.8282Z" fill="#231F20"/>
9+<path fill-rule="evenodd" clip-rule="evenodd" d="M194.819 81.0282V93.6282H189.628V81.9844C189.628 78.7219 188.107 76.6594 185.816 76.6594C183.471 76.6594 181.753 78.7219 181.753 81.9844V93.6282H176.742V81.9844C176.742 78.7219 175.167 76.6594 172.858 76.6594C170.549 76.6594 168.867 78.7219 168.867 81.9844V93.6282H163.677V72.0657H168.867V74.8219C170.138 72.7594 172.106 71.4657 174.523 71.4657C177.154 71.4657 179.23 73.0219 180.5 75.4407C181.807 73.2844 184.223 71.4657 187.266 71.4657C191.83 71.4657 194.819 75.6844 194.819 81.0282Z" fill="#231F20"/>
10+<path fill-rule="evenodd" clip-rule="evenodd" d="M213.271 83.0532C213.271 79.4532 211.141 76.8657 208.152 76.8657C205.235 76.8657 203.034 79.3219 203.034 82.9969C203.034 86.6532 205.289 89.1282 208.242 89.1282C211.141 89.1094 213.271 86.5782 213.271 83.0532ZM209.244 94.1907C206.381 94.1907 204.269 92.8219 203.052 90.9282V93.6282H197.861V63.6282H203.052V74.6907C204.304 72.9282 206.416 71.6532 209.155 71.6532C214.47 71.6532 218.533 76.4532 218.623 82.9219C218.712 89.4657 214.56 94.1907 209.244 94.1907Z" fill="#231F20"/>
11+<path fill-rule="evenodd" clip-rule="evenodd" d="M220.77 93.6282H225.96V72.0657H220.77V93.6282ZM220.108 65.9532C220.108 64.1157 221.576 62.7282 223.347 62.7282C225.066 62.7282 226.497 64.0969 226.497 65.9532C226.497 67.7907 225.066 69.1782 223.347 69.1782C221.593 69.1594 220.108 67.7907 220.108 65.9532Z" fill="#231F20"/>
12+<path fill-rule="evenodd" clip-rule="evenodd" d="M248.333 81.1219V93.6282H243.321V82.2844C243.321 78.8157 241.585 76.6594 239.08 76.6594C236.323 76.6594 234.373 79.2844 234.373 82.3782V93.6282H229.361V72.0657H234.373V74.7844C235.769 72.7594 238.006 71.4657 240.619 71.4657C245.236 71.4657 248.333 75.5907 248.333 81.1219Z" fill="#231F20"/>
13+<path fill-rule="evenodd" clip-rule="evenodd" d="M264.083 84.0657V83.5969L258.839 84.6657C257.085 85.0969 256.011 86.0344 256.011 87.2907C256.011 88.6219 257.174 89.6157 259.089 89.5594C261.845 89.4657 264.083 86.9532 264.083 84.0657ZM269.094 80.6157V93.6282H264.083V91.1344C262.561 92.8594 260.181 94.1344 257.55 94.1907C254.06 94.1907 250.982 91.7907 250.982 87.6657C250.982 83.7657 253.774 81.5344 257.926 80.6719L263.957 79.4719C263.546 77.6719 262.239 76.3032 260.253 76.3032C258.284 76.3032 256.44 77.5969 255.331 79.2282L251.554 76.3594C253.487 73.5282 256.888 71.6344 260.27 71.6344C265.318 71.5969 269.094 75.2532 269.094 80.6157Z" fill="#231F20"/>
14+<path fill-rule="evenodd" clip-rule="evenodd" d="M279.654 76.9407V85.9782C279.654 87.7407 280.388 88.5469 281.909 88.5469H284.486V93.6094H281.193C276.808 93.6094 274.642 91.3594 274.642 86.6344V76.9407H270.884V72.0657H274.463V66.7407L279.654 65.0719V72.0657H284.486V76.9407H279.654Z" fill="#231F20"/>
15+<path fill-rule="evenodd" clip-rule="evenodd" d="M302.545 82.8282C302.545 79.3594 300.129 76.7344 297.015 76.7344C293.811 76.7344 291.395 79.3594 291.395 82.8282C291.395 86.2969 293.811 88.9219 297.015 88.9219C300.075 88.9219 302.545 86.2969 302.545 82.8282ZM286.186 82.8282C286.186 76.5657 290.983 71.5407 297.015 71.5407C302.957 71.5407 307.753 76.5657 307.753 82.8282C307.753 89.0907 302.957 94.1157 297.015 94.1157C290.983 94.1157 286.186 89.0907 286.186 82.8282Z" fill="#231F20"/>
16+<path fill-rule="evenodd" clip-rule="evenodd" d="M322.071 71.5969V76.8282C317.436 76.8282 315.449 79.1907 315.449 82.2844V93.6282H310.438V72.0657H315.449V74.7657C316.863 72.8344 319.136 71.5969 322.071 71.5969Z" fill="#231F20"/>
17+</g>
18+<defs>
19+<filter id="filter0_d_1_82" x="0" y="0" width="358" height="133" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
20+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
21+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
22+<feOffset dy="4"/>
23+<feGaussianBlur stdDeviation="2"/>
24+<feComposite in2="hardAlpha" operator="out"/>
25+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
26+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_82"/>
27+<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_82" result="shape"/>
28+</filter>
29+</defs>
30+</svg>
apps/marketing/src/components/animated-beam-demo.tsx+11−31
@@ -182,7 +182,7 @@ export function AnimatedBeamDemo({ · 181 unmodified lines
182182 </Row>
183183 <Row label="Codex" reverse={false} labelStyle={labelStyle}>
184184 <Node ref={agent3} style={nodeStyle}>
185− <Icons.openai />
185+ <Icons.codex />
186186 </Node>
187187 </Row>
188188 </div>
@@ -337,35 +337,15 @@ function Row({ · 148 unmodified lines
337337 );
338338 }
339339
340+const logoIcon = (src: string) => () => (
341+ <img src={src} alt="" className="w-full h-full" style={{ objectFit: "contain" }} loading="lazy" />
342+);
343+
340344 const Icons = {
341− sentry: () => (
342− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#362D59]">
343− <path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z" />
344− </svg>
345− ),
346− github: () => (
347− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]">
348− <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
349− </svg>
350− ),
351− linear: () => (
352− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#5E6AD2]">
353− <path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z" />
354− </svg>
355− ),
356− claude: () => (
357− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#D97757]">
358− <path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z" />
359− </svg>
360− ),
361− cursor: () => (
362− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]">
363− <path d="M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23" />
364− </svg>
365− ),
366− openai: () => (
367− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]">
368− <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
369− </svg>
370− ),
345+ sentry: logoIcon("/logos/sentry.svg"),
346+ github: logoIcon("/logos/github.svg"),
347+ linear: logoIcon("/logos/linear.svg"),
348+ claude: logoIcon("/logos/claude.svg"),
349+ cursor: logoIcon("/logos/cursor.svg"),
350+ codex: logoIcon("/logos/codex.svg"),
371351 };
apps/marketing/src/components/context-bloat-demo.tsx+438−0
@@ -0,0 +1,438 @@
1+"use client";
2+
3+/* eslint-disable react/forbid-elements -- this marketing demo's service toggles
4+ are bespoke styled controls (icon + name + tool count + checkbox); the product
5+ design-system <Button> does not model that layout. */
6+
7+import React, { useEffect, useRef, useState } from "react";
8+
9+/**
10+ * Interactive "no context bloat" demo, laid out like Effect's "Production-grade
11+ * TypeScript" section: a complexity gauge + service checklist on top, then two
12+ * code windows below. Check services to connect them. The naive side grows a
13+ * system prompt that lists every tool name, ballooning into the thousands (tall
14+ * + scrollable); the Executor side stays at one `execute` tool with a short,
15+ * fixed description. Figures are illustrative; the shape is accurate.
16+ */
17+
18+type Integration = {
19+ readonly slug: string;
20+ readonly name: string;
21+ readonly tools: number;
22+ readonly naiveTok: number;
23+ readonly toolNames: ReadonlyArray<string>;
24+ readonly summary: string;
25+};
26+
27+// Each integration imports its whole API surface as tools (OpenAPI ops, MCP
28+// tools, GraphQL fields), so a handful already stacks into the thousands.
29+// naiveTok ~= tools * 170 (one tool definition with its JSON schema).
30+const INTEGRATIONS: ReadonlyArray<Integration> = [
31+ {
32+ slug: "github",
33+ name: "GitHub",
34+ tools: 720,
35+ naiveTok: 122400,
36+ toolNames: [
37+ "createIssue",
38+ "listPullRequests",
39+ "mergePullRequest",
40+ "createRelease",
41+ "addLabels",
42+ "createBranch",
43+ "getCommit",
44+ ],
45+ summary: "Production GitHub",
46+ },
47+ {
48+ slug: "stripe",
49+ name: "Stripe",
50+ tools: 510,
51+ naiveTok: 86700,
52+ toolNames: [
53+ "createCharge",
54+ "createCustomer",
55+ "createRefund",
56+ "listInvoices",
57+ "createSubscription",
58+ "capturePaymentIntent",
59+ "listPayouts",
60+ ],
61+ summary: "Live Stripe account",
62+ },
63+ {
64+ slug: "jira",
65+ name: "Jira",
66+ tools: 240,
67+ naiveTok: 40800,
68+ toolNames: [
69+ "createIssue",
70+ "transitionIssue",
71+ "addComment",
72+ "assignIssue",
73+ "listSprints",
74+ "createProject",
75+ "searchIssues",
76+ ],
77+ summary: "Team Jira",
78+ },
79+ {
80+ slug: "sentry",
81+ name: "Sentry",
82+ tools: 170,
83+ naiveTok: 28900,
84+ toolNames: [
85+ "listIssues",
86+ "resolveIssue",
87+ "listEvents",
88+ "getProject",
89+ "muteIssue",
90+ "createRelease",
91+ "listAlerts",
92+ ],
93+ summary: "Production Sentry",
94+ },
95+ {
96+ slug: "linear",
97+ name: "Linear",
98+ tools: 130,
99+ naiveTok: 22100,
100+ toolNames: [
101+ "createIssue",
102+ "updateIssue",
103+ "listProjects",
104+ "createComment",
105+ "archiveIssue",
106+ "listTeams",
107+ "createLabel",
108+ ],
109+ summary: "Linear workspace",
110+ },
111+ {
112+ slug: "gmail",
113+ name: "Gmail",
114+ tools: 95,
115+ naiveTok: 16150,
116+ toolNames: [
117+ "sendMessage",
118+ "listThreads",
119+ "createDraft",
120+ "addLabel",
121+ "trashMessage",
122+ "listMessages",
123+ "modifyMessage",
124+ ],
125+ summary: "Support inbox",
126+ },
127+ {
128+ slug: "notion",
129+ name: "Notion",
130+ tools: 80,
131+ naiveTok: 13600,
132+ toolNames: [
133+ "queryDatabase",
134+ "createPage",
135+ "updateBlock",
136+ "appendChildren",
137+ "search",
138+ "retrievePage",
139+ "listUsers",
140+ ],
141+ summary: "Internal Notion",
142+ },
143+ {
144+ slug: "slack",
145+ name: "Slack",
146+ tools: 70,
147+ naiveTok: 11900,
148+ toolNames: [
149+ "postMessage",
150+ "listChannels",
151+ "createChannel",
152+ "inviteToChannel",
153+ "uploadFile",
154+ "listUsers",
155+ "setTopic",
156+ ],
157+ summary: "Team Slack",
158+ },
159+];
160+
161+// The execute tool's description is a fixed preamble (workflow + rules) plus one
162+// short prefix line per connected integration. It stays flat as you add
163+// integrations, no matter how many tools each one carries.
164+const EXECUTOR_BASE = 980; // fixed workflow + rules preamble, served once
165+const EXECUTOR_PER = 16; // one connection-prefix line per integration
166+const NAIVE_MAX = INTEGRATIONS.reduce((s, i) => s + i.naiveTok, 0); // bar scale
167+
168+const fmt = (n: number) => n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
169+
170+function usePrefersReducedMotion(): boolean {
171+ const [reduced, setReduced] = useState(false);
172+ useEffect(() => {
173+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
174+ const sync = () => setReduced(mq.matches);
175+ sync();
176+ mq.addEventListener("change", sync);
177+ return () => mq.removeEventListener("change", sync);
178+ }, []);
179+ return reduced;
180+}
181+
182+/** Eases a displayed integer toward `target` with requestAnimationFrame. */
183+function useAnimatedNumber(target: number): number {
184+ const reduced = usePrefersReducedMotion();
185+ const [display, setDisplay] = useState(target);
186+ const fromRef = useRef(target);
187+ const rafRef = useRef<number | null>(null);
188+
189+ useEffect(() => {
190+ if (reduced) {
191+ setDisplay(target);
192+ fromRef.current = target;
193+ return;
194+ }
195+ const from = fromRef.current;
196+ if (from === target) return;
197+ const start = performance.now();
198+ const dur = 450;
199+ const tick = (now: number) => {
200+ const t = Math.min(1, (now - start) / dur);
201+ const eased = 1 - Math.pow(1 - t, 3);
202+ setDisplay(Math.round(from + (target - from) * eased));
203+ if (t < 1) {
204+ rafRef.current = requestAnimationFrame(tick);
205+ } else {
206+ fromRef.current = target;
207+ }
208+ };
209+ rafRef.current = requestAnimationFrame(tick);
210+ return () => {
211+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
212+ fromRef.current = target;
213+ };
214+ }, [target, reduced]);
215+
216+ return display;
217+}
218+
219+// Brand logos live in /public/logos as full-color svgl.app assets, rendered as
220+// <img> so each keeps its own colors. svgl has no Jira icon, so /logos/jira.svg
221+// is the Jira mark in Jira blue.
222+function IntegrationIcon({ slug }: { readonly slug: string }) {
223+ return (
224+ <img
225+ src={`/logos/${slug}.svg`}
226+ alt=""
227+ width={15}
228+ height={15}
229+ loading="lazy"
230+ style={{ objectFit: "contain" }}
231+ />
232+ );
233+}
234+
235+function CheckMark() {
236+ return (
237+ <svg
238+ viewBox="0 0 24 24"
239+ fill="none"
240+ stroke="currentColor"
241+ strokeWidth={3}
242+ strokeLinecap="round"
243+ strokeLinejoin="round"
244+ width="11"
245+ height="11"
246+ aria-hidden="true"
247+ >
248+ <path d="M20 6 9 17l-5-5" />
249+ </svg>
250+ );
251+}
252+
253+function TokenBar({
254+ pct,
255+ variant,
256+}: {
257+ readonly pct: number;
258+ readonly variant: "naive" | "executor";
259+}) {
260+ return (
261+ <div className={`cbloat-bar cbloat-bar--${variant}`} aria-hidden="true">
262+ <div className="cbloat-bar__fill" style={{ width: `${Math.min(100, Math.max(2, pct))}%` }} />
263+ </div>
264+ );
265+}
266+
267+export function ContextBloatDemo() {
268+ const [active, setActive] = useState<ReadonlyArray<string>>([
269+ "github",
270+ "stripe",
271+ "jira",
272+ "sentry",
273+ ]);
274+ const isOn = (slug: string) => active.includes(slug);
275+ const toggle = (slug: string) =>
276+ setActive((prev) => (prev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug]));
277+
278+ const activeIntegrations = INTEGRATIONS.filter((i) => isOn(i.slug));
279+ const naiveTok = activeIntegrations.reduce((s, i) => s + i.naiveTok, 0);
280+ const naiveTools = activeIntegrations.reduce((s, i) => s + i.tools, 0);
281+ const executorTok = EXECUTOR_BASE + active.length * EXECUTOR_PER;
282+
283+ const naiveDisplay = useAnimatedNumber(naiveTok);
284+ const executorDisplay = useAnimatedNumber(executorTok);
285+ const naiveToolsDisplay = useAnimatedNumber(naiveTools);
286+
287+ const naivePct = (naiveTok / NAIVE_MAX) * 100;
288+ const executorPct = (executorTok / NAIVE_MAX) * 100;
289+
290+ return (
291+ <div className="cbloat">
292+ <p className="sr-only" aria-live="polite">
293+ Without Executor: {fmt(naiveTools)} tools, about {fmt(naiveTok)} tokens. With Executor: 1
294+ tool, about {fmt(executorTok)} tokens.
295+ </p>
296+
297+ <div className="cbloat-top">
298+ {/* Complexity gauge */}
299+ <div className="cbloat-gauge">
300+ <div className="cbloat-gauge__title">Context window</div>
301+ <div className="cbloat-gauge__sub">Lower is better</div>
302+ <div className="cbloat-gauge__row">
303+ <div className="cbloat-gauge__line">
304+ <span className="cbloat-dot cbloat-dot--naive" />
305+ <span className="cbloat-gauge__name">Without Executor</span>
306+ <span className="cbloat-gauge__val">
307+ {fmt(naiveToolsDisplay)} tools &middot; ~{fmt(naiveDisplay)} tok
308+ </span>
309+ </div>
310+ <TokenBar pct={naivePct} variant="naive" />
311+ </div>
312+ <div className="cbloat-gauge__row">
313+ <div className="cbloat-gauge__line">
314+ <span className="cbloat-dot cbloat-dot--exec" />
315+ <span className="cbloat-gauge__name">With Executor</span>
316+ <span className="cbloat-gauge__val">1 tool &middot; ~{fmt(executorDisplay)} tok</span>
317+ </div>
318+ <TokenBar pct={executorPct} variant="executor" />
319+ </div>
320+ </div>
321+
322+ {/* Service checklist */}
323+ <div className="cbloat-checklist" role="group" aria-label="Connect services">
324+ {INTEGRATIONS.map((i) => (
325+ <button
326+ key={i.slug}
327+ type="button"
328+ className="cbloat-check"
329+ data-on={isOn(i.slug) ? "true" : undefined}
330+ aria-pressed={isOn(i.slug)}
331+ onClick={() => toggle(i.slug)}
332+ >
333+ <span className="cbloat-check__box" aria-hidden="true">
334+ {isOn(i.slug) ? <CheckMark /> : null}
335+ </span>
336+ <span className="cbloat-check__icon">
337+ <IntegrationIcon slug={i.slug} />
338+ </span>
339+ <span className="cbloat-check__name">{i.name}</span>
340+ <span className="cbloat-check__count">{fmt(i.tools)} tools</span>
341+ </button>
342+ ))}
343+ </div>
344+ </div>
345+
346+ <div className="cbloat-grid">
347+ {/* Without Executor: a system prompt that lists every tool, scrollable */}
348+ <div className="cbloat-col">
349+ <div className="cbloat-col__title">Without Executor</div>
350+ <div className="code-window cbloat-panel cbloat-panel--naive">
351+ <div className="code-window__bar">
352+ <span className="code-window__dots">
353+ <i />
354+ <i />
355+ <i />
356+ </span>
357+ <span className="cbloat-panel__count">
358+ <span className="cbloat-num">{fmt(naiveToolsDisplay)}</span> tools &middot; ~
359+ {fmt(naiveDisplay)} tok
360+ </span>
361+ </div>
362+ <pre className="code-window__body cbloat-body cbloat-body--scroll">
363+ <code>
364+ <span className="tok-s">{'"You are a helpful assistant.'}</span>
365+ {"\n\n"}
366+ {"Your tools are:"}
367+ {"\n\n"}
368+ {activeIntegrations.length === 0 ? (
369+ <span className="tok-c">{"(none yet, check a service)"}</span>
370+ ) : null}
371+ {activeIntegrations.map((i) => (
372+ <React.Fragment key={i.slug}>
373+ {i.toolNames.map((n) => (
374+ <React.Fragment key={n}>
375+ <span className="tok-a">{n}</span>
376+ <span className="tok-p">()</span>
377+ {"\n"}
378+ </React.Fragment>
379+ ))}
380+ <span className="tok-c">{`// + ${fmt(i.tools - i.toolNames.length)} more ${i.name} tools`}</span>
381+ {"\n"}
382+ </React.Fragment>
383+ ))}
384+ {activeIntegrations.length > 0 ? <span className="tok-s">{'..."'}</span> : null}
385+ </code>
386+ </pre>
387+ </div>
388+ </div>
389+
390+ {/* With Executor: one tool, the same trimmed description */}
391+ <div className="cbloat-col">
392+ <div className="cbloat-col__title">With Executor</div>
393+ <div className="code-window cbloat-panel cbloat-panel--executor">
394+ <div className="code-window__bar">
395+ <span className="code-window__dots">
396+ <i />
397+ <i />
398+ <i />
399+ </span>
400+ <span className="cbloat-panel__count">
401+ 1 tool &middot; ~<span className="cbloat-num">{fmt(executorDisplay)}</span> tok
402+ </span>
403+ </div>
404+ <pre className="code-window__body cbloat-body cbloat-body--scroll">
405+ <code>
406+ <span className="tok-c">{'// the only tool your client sees: "execute"'}</span>
407+ {"\n\n"}
408+ {
409+ "Execute TypeScript in a sandboxed runtime with access to\nconfigured API tools.\n\n"
410+ }
411+ <span className="tok-f">{"## Workflow"}</span>
412+ {"\n\n"}
413+ {"1. const { items } = await tools.search({ query });\n"}
414+ {"2. const path = items[0]?.path;\n"}
415+ {"3. const details = await tools.describe.tool({ path });\n"}
416+ {"4. const result = await tools[path](input);\n\n"}
417+ <span className="tok-f">{"## Available connection prefixes"}</span>
418+ {"\n\n"}
419+ {activeIntegrations.length === 0 ? (
420+ <span className="tok-c">{"(connect a service to add a prefix)"}</span>
421+ ) : null}
422+ {activeIntegrations.map((i) => (
423+ <span key={i.slug} className="cbloat-line">
424+ <span className="tok-p">{"- "}</span>
425+ <span className="tok-a">{`${i.slug}.org.main`}</span>
426+ <span className="tok-p">{": "}</span>
427+ <span className="tok-c">{i.summary}</span>
428+ {"\n"}
429+ </span>
430+ ))}
431+ </code>
432+ </pre>
433+ </div>
434+ </div>
435+ </div>
436+ </div>
437+ );
438+}
apps/marketing/src/pages/index.astro+700−344
@@ -1,8 +1,11 @@
11 ---
22 import Layout from "../layouts/Layout.astro";
33 import { AnimatedBeamDemo } from "../components/animated-beam-demo";
4+import { ContextBloatDemo } from "../components/context-bloat-demo";
45 import { SelfHostContactModal } from "../components/self-host-contact-modal";
56
7+const GH = "https://github.com/RhysSullivan/executor";
8+
69 // Copied to the clipboard by the "Set up with your agent" hero CTA. A visitor
710 // pastes it into their coding agent (Claude, Cursor, ...) and the agent picks
811 // the right form of Executor, installs it, connects over MCP, and gets a first
@@ -43,7 +46,7 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 34 unmodified lines
4346
4447 <Layout>
4548 <main class="bg-soft min-h-dvh relative antialiased">
46− {/* Pattern + fade — span the top 100vh, behind everything */}
49+ {/* Pattern + fade: span the top 100vh, behind everything */}
4750 <div
4851 class="pattern-bg pattern-graph"
4952 style="position: absolute; top: 0; left: 0; right: 0; height: 100vh; z-index: 1; pointer-events: none;"
@@ -55,7 +58,7 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 5 unmodified lines
5558 </div>
5659
5760 <div class="relative z-10">
58− {/* NAV */}
61+ {/* ─── NAV ─── */}
5962 <nav>
6063 <div
6164 class="max-w-[1200px] mx-auto px-6 sm:px-10 h-14 flex items-center justify-between"
@@ -68,18 +71,29 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 6 unmodified lines
6871 </a>
6972 <div class="flex items-center gap-5">
7073 <a
71− href="https://github.com/RhysSullivan/executor"
74+ href="#pricing"
75+ class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors hidden sm:inline"
76+ >Pricing</a
77+ >
78+ <a
79+ href="https://executor.sh/docs"
80+ class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors hidden sm:inline"
81+ >Docs</a
82+ >
83+ <a
84+ href={GH}
7285 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors"
7386 >GitHub</a
7487 >
75− <a href="/cloud" class="btn-primary">Try Cloud →</a>
88+ <a href="#install" class="btn-primary">Get started →</a>
7689 </div>
7790 </div>
7891 </nav>
7992
80− {/* HERO — centered, tight */}
93+ {/* ─── HERO ─── */}
8194 <section class="relative">
82− <div class="relative z-10 px-6 sm:px-10 pt-28 pb-32 sm:pt-36 sm:pb-44">
95+ <div class="hero-glow" aria-hidden="true"></div>
96+ <div class="relative z-10 px-6 sm:px-10 pt-24 pb-28 sm:pt-32 sm:pb-36">
8397 <div class="max-w-[920px] mx-auto text-center">
8498 <h1
8599 class="rise-1 text-[clamp(2.8rem,7.2vw,6rem)] font-semibold tracking-[-0.025em] leading-[1.02] text-ink mb-12 mx-auto max-w-[18ch] text-balance"
@@ -91,260 +105,530 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 5 unmodified lines
91105 <AnimatedBeamDemo client:load variant="stripe" />
92106 </div>
93107 </div>
94− <p
95− class="rise-3 text-[18px] leading-[1.55] text-ink-2 mx-auto max-w-[44ch] mb-9 text-pretty"
96− >
97− One gateway. Every tool. Every team.
98− </p>
99−
100− {/* ── Toggle: Try in Cloud / Try locally ── */}
101− <div
102− class="rise-4 hero-toggle flex flex-col items-center gap-7"
103− data-active="cloud"
104− >
105− <div
106− class="tab-toggle"
107− data-active="cloud"
108− role="tablist"
109− aria-label="Choose how to try Executor"
110− >
111− <span class="tab-indicator" aria-hidden="true"></span>
112− <button
113− type="button"
114− role="tab"
115− aria-selected="true"
116− data-tab="cloud"
117− >
118− Cloud
108+ {/* ── CTA: For humans / For agents ── */}
109+ <div class="rise-4 hero-cta" data-audience="agents">
110+ <div class="audience-toggle" role="tablist" aria-label="Choose your path">
111+ <button type="button" role="tab" data-audience-set="humans" aria-selected="false">
112+ For humans
119113 </button>
114+ <span class="audience-sep" aria-hidden="true"></span>
115+ <button type="button" role="tab" data-audience-set="agents" aria-selected="true">
116+ For agents
117+ </button>
118+ </div>
119+
120+ {/* For humans: sign up or run it yourself */}
121+ <div class="audience-pane" data-audience-pane="humans" role="tabpanel" hidden>
122+ <a href="#install" class="btn-primary btn-primary--hero">Run locally →</a>
123+ <a href="/cloud" class="btn-secondary">Try Cloud</a>
124+ </div>
125+
126+ {/* For agents: hand setup to a coding agent, or read the docs */}
127+ <div class="audience-pane" data-audience-pane="agents" role="tabpanel">
120128 <button
121129 type="button"
122− role="tab"
123− aria-selected="false"
124− data-tab="local"
130+ class="agent-pill copy-btn"
131+ data-copy={setupPrompt}
132+ data-event="marketing_setup_prompt_copied"
125133 >
126− Local
134+ <span class="agent-pill__label">Set up with your agent</span>
135+ <span class="copy-icons" aria-hidden="true">
136+ <svg
137+ class="copy-icon-copy" xmlns="http://www.w3.org/2000/svg"
138+ width="14" height="14" viewBox="0 0 24 24" fill="none"
139+ stroke="currentColor" stroke-width="2" stroke-linecap="round"
140+ stroke-linejoin="round"
141+ ><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path
142+ d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg
143+ >
144+ <svg
145+ class="copy-icon-check" xmlns="http://www.w3.org/2000/svg"
146+ width="14" height="14" viewBox="0 0 24 24" fill="none"
147+ stroke="currentColor" stroke-width="2" stroke-linecap="round"
148+ stroke-linejoin="round"
149+ ><path d="M20 6 9 17l-5-5"></path></svg
150+ >
151+ </span>
127152 </button>
153+ <a href="https://executor.sh/docs" class="btn-secondary">Read docs</a>
128154 </div>
155+ </div>
129156
130− {/* Cloud pane */}
131− <div
132− class="pane flex-col items-center gap-4"
133− data-pane="cloud"
134− role="tabpanel"
135− aria-labelledby="tab-cloud"
157+ <a
158+ href="https://www.ycombinator.com"
159+ target="_blank"
160+ rel="noopener noreferrer"
161+ class="rise-4 mt-10 inline-flex"
162+ aria-label="Backed by Y Combinator"
163+ >
164+ <img
165+ src="/yc-backed-by.svg"
166+ alt="Backed by Y Combinator"
167+ width="140"
168+ height="52"
169+ />
170+ </a>
171+ </div>
172+ </div>
173+ </section>
174+
175+ {/* ─── WEDGE ─── */}
176+ <section class="py-20 sm:py-28 border-t border-rule">
177+ <div class="reveal max-w-[840px] mx-auto px-6 sm:px-10">
178+ <p
179+ class="text-[clamp(1.5rem,3.4vw,2.2rem)] font-medium tracking-[-0.02em] leading-[1.28] text-ink text-balance"
180+ >
181+ Wiring tools to agents is fiddly, per-client, and easy to get wrong.
182+ Executor makes every tool, from any protocol, look the
183+ <span class="serif-italic">same</span>: one name, one input
184+ schema, one output schema, so any agent can call any of them
185+ the same way.
186+ </p>
187+ </div>
188+ </section>
189+
190+ {/* ─── NO CONTEXT BLOAT (interactive) ─── */}
191+ <section class="py-20 sm:py-28 border-t border-rule">
192+ <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10">
193+ <div class="text-center max-w-2xl mx-auto mb-12">
194+ <div class="eyebrow mb-4">Context efficiency</div>
195+ <h2 class="section-title mx-auto max-w-[20ch] mb-5">
196+ Thousands of tools, no <span class="serif-italic">bloat</span>.
197+ </h2>
198+ <p class="section-sub mx-auto max-w-[58ch]">
199+ Connect everything you use and Executor still shows the model a
200+ single tool. It searches your catalog and loads a tool's schema
201+ only when the code actually calls it, so the prompt never
202+ balloons.
203+ </p>
204+ </div>
205+
206+ <ContextBloatDemo client:load />
207+ </div>
208+ </section>
209+
210+ {/* ─── CAPABILITIES ─── */}
211+ <section class="py-20 sm:py-28 border-t border-rule">
212+ <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10">
213+ <div class="mb-12 max-w-[34ch]">
214+ <div class="eyebrow mb-4">What you get</div>
215+ <h2 class="section-title">
216+ The model reasons. Executor handles the
217+ <span class="serif-italic">rest</span>.
218+ </h2>
219+ </div>
220+
221+ <div class="cap-grid">
222+ {/* One tool shape */}
223+ <div class="cap-card">
224+ <span class="cap-icon"
225+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
226+ stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
227+ ><path d="m12 2 9 5-9 5-9-5 9-5Z"></path><path
228+ d="m3 12 9 5 9-5"></path><path d="m3 17 9 5 9-5"
229+ ></path></svg
230+ ></span
136231 >
137− <p class="pane-desc">
138− Built for teams. Plug into any agent via MCP.
139− </p>
140− <a href="/cloud" class="btn-primary btn-primary--hero"
141− >Try Cloud →</a
142− >
143− </div>
232+ <h3>One tool shape</h3>
233+ <p>
234+ MCP, OpenAPI, GraphQL, or a custom integration. Under the hood
235+ they all become a tool name, an input schema, and an output
236+ schema.
237+ </p>
238+ </div>
144239
145− {/* Local pane — auto-detected download */}
146− <div
147− class="pane flex-col items-center gap-4"
148− data-pane="local"
149− role="tabpanel"
150− aria-labelledby="tab-local"
151− data-download
240+ {/* Code-mode */}
241+ <div class="cap-card">
242+ <span class="cap-icon"
243+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
244+ stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
245+ ><polyline points="4 17 10 11 4 5"></polyline><line
246+ x1="12"
247+ y1="19"
248+ x2="20"
249+ y2="19"></line></svg
250+ ></span
152251 >
153− <p class="pane-desc">
154− Everything runs on your machine locally.
155− </p>
156− <div class="flex flex-wrap items-center justify-center gap-3">
157− <a
158− data-os="mac-arm64"
159− href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-arm64.dmg"
160− class="btn-primary btn-primary--hero"
161− >
162− Download for Mac (Apple Silicon)
163− </a>
164− <a
165− data-os="mac-x64"
166− href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-x64.dmg"
167− class="btn-primary btn-primary--hero"
168− >
169− Download for Mac (Intel)
170− </a>
171− <a
172− data-os="win-x64"
173− href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-win-x64.exe"
174− class="btn-primary btn-primary--hero"
175− >
176− Download for Windows
177− </a>
178− <a
179− data-os="linux-x64"
180− href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-linux-x86_64.AppImage"
181− class="btn-primary btn-primary--hero"
182− >
183− Download for Linux
184− </a>
185−
186− <details
187− class="download-other hidden"
188− data-download-other
189− >
190− <summary>Other platforms ▾</summary>
191− <div
192− class="download-other-list"
193− data-download-other-list
194− >
195− </div>
196− </details>
252+ <h3>Call it any way</h3>
253+ <p>
254+ Today it is a code-mode MCP. It could just as well be the
255+ Executor CLI, a one-off script, a gen-UI dashboard, or a reusable
256+ workflow. Same tools, every surface.
257+ </p>
258+ </div>
259+
260+ {/* Trace every call (coming soon) */}
261+ <div class="cap-card cap-card--soon">
262+ <span class="cap-soon-badge">Coming soon</span>
263+ <span class="cap-icon"
264+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
265+ stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
266+ ><path d="M3 12h4l3 8 4-16 3 8h4"></path></svg
267+ ></span
268+ >
269+ <h3>Trace every call</h3>
270+ <p>
271+ One place to see every run and tool call. Audit any
272+ decision after the fact.
273+ </p>
274+ </div>
275+
276+ {/* Teams */}
277+ <div class="cap-card">
278+ <span class="cap-icon"
279+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
280+ stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
281+ ><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"
282+ ></path><circle cx="9" cy="7" r="4"></circle><path
283+ d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"
284+ ></path></svg
285+ ></span
286+ >
287+ <h3>Set up once, whole team has it</h3>
288+ <p class="mb-1">
289+ Per-user credentials and shared ones. No onboarding ritual, no
290+ toggling MCPs on and off mid-task.
291+ </p>
292+ <div class="cap-visual matrix">
293+ <div class="matrix-head">
294+ <span></span><span>GH</span><span>Sntry</span><span>Linr</span
295+ ><span>Slack</span><span>Notn</span>
296+ </div>
297+ <div class="matrix-row">
298+ <span class="who">Aria</span>
299+ <span class="cell"><i class="stat stat-ok"></i></span>
300+ <span class="cell"><i class="stat stat-ok"></i></span>
301+ <span class="cell"><i class="stat stat-ok"></i></span>
302+ <span class="cell"><i class="stat stat-ok"></i></span>
303+ <span class="cell"><i class="stat stat-warn"></i></span>
304+ </div>
305+ <div class="matrix-row">
306+ <span class="who">Noah</span>
307+ <span class="cell"><i class="stat stat-ok"></i></span>
308+ <span class="cell"><i class="stat stat-ok"></i></span>
309+ <span class="cell"><i class="stat stat-off"></i></span>
310+ <span class="cell"><i class="stat stat-ok"></i></span>
311+ <span class="cell"><i class="stat stat-ok"></i></span>
312+ </div>
313+ <div class="matrix-row">
314+ <span class="who">Mina</span>
315+ <span class="cell"><i class="stat stat-ok"></i></span>
316+ <span class="cell"><i class="stat stat-ok"></i></span>
317+ <span class="cell"><i class="stat stat-ok"></i></span>
318+ <span class="cell"><i class="stat stat-ok"></i></span>
319+ <span class="cell"><i class="stat stat-ok"></i></span>
197320 </div>
198321 </div>
322+ </div>
323+
324+ {/* Destructive gating */}
325+ <div class="cap-card">
326+ <span class="cap-icon"
327+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
328+ stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
329+ ><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"
330+ ></path><path d="m9 12 2 2 4-4"></path></svg
331+ ></span
332+ >
333+ <h3>Destructive actions pull you back in</h3>
334+ <p>
335+ Executor keeps the semantics it imported: GET vs DELETE for
336+ OpenAPI, destructiveHint for MCP, mutations for GraphQL. Agents
337+ auto-run the safe stuff and ask before the rest.
338+ </p>
339+ </div>
199340
200− {/* Distribution-agnostic: hand the whole setup to your coding
201− agent. Copies the prompt above; the agent picks the form,
202− installs, and connects. */}
203− <button
204− type="button"
205− class="btn-secondary copy-btn"
206− data-copy={setupPrompt}
207− data-event="marketing_setup_prompt_copied"
341+ {/* Sandboxed */}
342+ <div class="cap-card">
343+ <span class="cap-icon"
344+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
345+ stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
346+ ><rect x="3" y="3" width="18" height="18" rx="2"
347+ ></rect><path d="m7 8 3 3-3 3M13 14h4"></path></svg
348+ ></span
208349 >
209− Set up with your agent
210− <span class="copy-icons" aria-hidden="true">
211− <svg
212− class="copy-icon-copy"
213− xmlns="http://www.w3.org/2000/svg"
214− width="14"
215− height="14"
216− viewBox="0 0 24 24"
217− fill="none"
218− stroke="currentColor"
219− stroke-width="2"
220− stroke-linecap="round"
221− stroke-linejoin="round"
222− >
223− <rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect>
224− <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"
225− ></path>
226− </svg>
227− <svg
228− class="copy-icon-check"
229− xmlns="http://www.w3.org/2000/svg"
230− width="14"
231− height="14"
232− viewBox="0 0 24 24"
233− fill="none"
234− stroke="currentColor"
235− stroke-width="2"
236− stroke-linecap="round"
237− stroke-linejoin="round"
238− >
239− <path d="M20 6 9 17l-5-5"></path>
240− </svg>
241− </span>
242− </button>
350+ <h3>Sandboxed execution</h3>
351+ <p>
352+ Tool calls run in an isolated JavaScript sandbox. Secrets are
353+ injected host-side at call time and never enter the sandbox
354+ heap, so the agent and model never see a raw token.
355+ </p>
243356 </div>
244357 </div>
245358 </div>
246359 </section>
247360
248− {/* ─── FOUNDER NOTE ─── */}
361+ {/* ─── FOUNDER NOTE (condensed) ─── */}
249362 <section class="py-20 sm:py-28 border-t border-rule">
250− <div class="max-w-[680px] mx-auto px-6 sm:px-10">
251− <h2
252− class="text-[clamp(2rem,3.6vw,2.6rem)] font-semibold tracking-[-0.02em] leading-[1.08] text-ink mb-10 text-balance"
253− >
254− What is <span class="serif-italic">Executor</span>?
255− </h2>
256−
257− <div
258− class="space-y-5 text-[16.5px] leading-[1.65] text-ink-2 text-pretty"
259− >
363+ <div class="reveal max-w-[680px] mx-auto px-6 sm:px-10">
364+ <div class="eyebrow mb-6">Why we built it</div>
365+ <div class="space-y-5 text-[16.5px] leading-[1.65] text-ink-2 text-pretty">
260366 <p>
261− Executor is a tool/source discovery and execution layer for
262− agent-callable tools.
367+ Your agent should be able to reach your company's resources in a
368+ way that isn't scary. Most setups make you choose between locked
369+ down and useless, or wide open and risky.
263370 </p>
264371 <p>
265− Executor doesn't care what you add. MCP, OpenAPI, GraphQL, custom
266− sources, under the hood they all become a tool name, an input
267− schema, and an output schema. Once they're in that shape, you can
268− call them however you want. Today that's implemented as a
269− code-mode MCP, but it could just as well be the Executor CLI, or a
270− native client you drop in.
271− </p>
272− <p>
273− Because every tool looks the same, your agent can call them from
274− gen-UI dashboards it writes on the fly, call them as one off
275− scripts in a chat, or use them in reusable workflows.
372+ Executor doesn't care what you add. Once a tool is in that one
373+ shape, a name and two schemas, you can call it however you want and
374+ the same guardrails apply everywhere. That is the whole idea: make
375+ the safe path the easy path.
276376 </p>
377+ </div>
378+ </div>
379+ </section>
277380
278− <h3
279− class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"
280− >
281− Teams
282− </h3>
283− <p>
284− Set your sources up once and your whole team has them. It supports
285− per-user credentials and shared ones. No more onboarding ritual,
286− no more toggling MCPs on and off mid-task. Your agents should be
287− able to reach your company's resources in a way that isn't scary.
288− </p>
381+ {/* ─── CHOOSE YOUR PATH ─── */}
382+ <section id="install" class="py-24 sm:py-32 border-t border-rule">
383+ <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10">
384+ <div class="mb-14 mx-auto text-center max-w-2xl">
385+ <div class="eyebrow mb-4">Get started</div>
386+ <h2 class="section-title mx-auto max-w-[20ch]">
387+ Pick your <span class="serif-italic">path</span>.
388+ </h2>
389+ </div>
289390
290− <h3
291− class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"
292− >
293− Sandboxing
294− </h3>
295− <p>
296− Tool calls run in a JavaScript sandbox. The agent has access to
297− secrets.
298− </p>
391+ <div class="plus-grid grid md:grid-cols-3 gap-6 max-w-[1000px] mx-auto">
392+ <span class="plus-mark" style="top:-7px;left:-7px"></span>
393+ <span class="plus-mark" style="top:-7px;right:-7px"></span>
394+ <span class="plus-mark" style="bottom:-7px;left:-7px"></span>
395+ <span class="plus-mark" style="bottom:-7px;right:-7px"></span>
299396
300− <h3
301− class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"
397+ {/* Cloud (recommended: fastest start) */}
398+ <div
399+ class="surface-card path-card beam-card p-8 flex flex-col"
400+ style="border-color: rgba(99,91,255,0.25)"
302401 >
303− Destructive actions
304− </h3>
305− <p>
306− Executor preserves the semantics of whatever it imported from: GET
307− vs DELETE for OpenAPI, <code
308− class="font-mono text-[14px] text-ink bg-surface-2 border border-rule px-1.5 py-0.5 rounded"
309− >destructiveHint</code
310− > for MCP, mutations for GraphQL. That tells the agent what it can auto-run
311− and what should pull you back into the loop, so it can work autonomously
312− without taking you out of it.
313− </p>
402+ <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3">
403+ Cloud
404+ </h3>
405+ <span class="path-chip mb-4 self-start">executor.sh/cloud</span>
406+ <p class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty flex-1">
407+ Hosted Executor. Auth, sync, policies, and your whole team online
408+ in five minutes. Free tier to start.
409+ </p>
410+ <a href="/cloud" class="btn-primary self-start">Try Cloud →</a>
411+ <p class="mt-3 min-h-[1.25rem] text-[13px] leading-[1.5] text-ink-3">
412+ <SelfHostContactModal client:load variant="inline" />
413+ </p>
414+ </div>
314415
315− <p class="pt-5">
316− It's all open source, built on the same SDK we publish to npm.
317− There's two ways to try it, either the local version via the
318− `executor` cli, or the cloud version.
319− </p>
416+ {/* Desktop (native app, local) */}
417+ <div class="surface-card path-card p-8 flex flex-col">
418+ <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3">
419+ Desktop
420+ </h3>
421+ <span class="path-chip mb-4 self-start">Mac · Windows · Linux</span>
422+ <p class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1">
423+ A native app that runs entirely on your machine. Your
424+ integrations, credentials, and sessions never leave the device.
425+ MIT licensed.
426+ </p>
427+ <div class="flex flex-col gap-3 self-stretch" data-download>
428+ <a
429+ data-os="mac-arm64"
430+ href={`${GH}/releases/latest/download/executor-desktop-mac-arm64.dmg`}
431+ class="btn-primary self-start"
432+ >Download for Mac</a
433+ >
434+ <a
435+ data-os="mac-x64"
436+ href={`${GH}/releases/latest/download/executor-desktop-mac-x64.dmg`}
437+ class="btn-primary self-start"
438+ >Download for Mac (Intel)</a
439+ >
440+ <a
441+ data-os="win-x64"
442+ href={`${GH}/releases/latest/download/executor-desktop-win-x64.exe`}
443+ class="btn-primary self-start"
444+ >Download for Windows</a
445+ >
446+ <a
447+ data-os="linux-x64"
448+ href={`${GH}/releases/latest/download/executor-desktop-linux-x86_64.AppImage`}
449+ class="btn-primary self-start"
450+ >Download for Linux</a
451+ >
452+ <details class="download-other hidden min-h-[1.25rem]" data-download-other>
453+ <summary>Other platforms ▾</summary>
454+ <div class="download-other-list" data-download-other-list></div>
455+ </details>
456+ </div>
457+ </div>
320458
321− <p class="text-ink-3 text-[14.5px] pt-3">— Rhys</p>
459+ {/* CLI (headless / servers) */}
460+ <div class="surface-card path-card p-8 flex flex-col">
461+ <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3">
462+ CLI
463+ </h3>
464+ <span class="path-chip mb-4 self-start">npm i -g executor</span>
465+ <p class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1">
466+ Run Executor as a background service and drive it from your
467+ terminal. Best for headless and server environments. MIT
468+ licensed.
469+ </p>
470+ <a href="https://executor.sh/docs" class="btn-primary self-start"
471+ >Read the docs →</a
472+ >
473+ <p class="mt-3 min-h-[1.25rem] text-[13px] leading-[1.5] text-ink-3">
474+ <a href={GH} class="hover:text-ink-2 transition-colors"
475+ >View source →</a
476+ >
477+ </p>
478+ </div>
322479 </div>
323480 </div>
324481 </section>
325482
326− {/* ─── GET STARTED ─── */}
327− <section id="install" class="py-24 sm:py-32 border-t border-rule">
328− <div class="max-w-[1200px] mx-auto px-6 sm:px-10">
483+ {/* ─── FAQ ─── */}
484+ <section class="py-20 sm:py-28 border-t border-rule">
485+ <div class="reveal max-w-[760px] mx-auto px-6 sm:px-10">
486+ <div class="mb-10">
487+ <h2 class="section-title">FAQ</h2>
488+ </div>
489+
490+ <div>
491+ <details class="faq-item">
492+ <summary>
493+ Where does my code run, and what touches my credentials?
494+ <span class="faq-mark"
495+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
496+ stroke-width="2" stroke-linecap="round"
497+ ><path d="M12 5v14M5 12h14"></path></svg
498+ ></span
499+ >
500+ </summary>
501+ <div class="faq-body">
502+ Tool calls run in an isolated JavaScript sandbox. Credentials are
503+ resolved host-side at call time and injected into the outbound
504+ request only. They never enter the sandbox heap, the code your
505+ agent wrote, the agent, or the model.
506+ </div>
507+ </details>
508+
509+ <details class="faq-item">
510+ <summary>
511+ Can the agent or the model ever see a raw token?
512+ <span class="faq-mark"
513+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
514+ stroke-width="2" stroke-linecap="round"
515+ ><path d="M12 5v14M5 12h14"></path></svg
516+ ></span
517+ >
518+ </summary>
519+ <div class="faq-body">
520+ No. Secrets stay host-side by design. The sandbox calls a tool by
521+ name; Executor attaches the credential to the real request
522+ outside the sandbox, so a token is never present in anything the
523+ model can read.
524+ </div>
525+ </details>
526+
527+ <details class="faq-item">
528+ <summary>
529+ What can call Executor?
530+ <span class="faq-mark"
531+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
532+ stroke-width="2" stroke-linecap="round"
533+ ><path d="M12 5v14M5 12h14"></path></svg
534+ ></span
535+ >
536+ </summary>
537+ <div class="faq-body">
538+ Any MCP client (Claude Code, Cursor, Codex, and others), the
539+ Executor CLI, or a native client you drop in. Because tools share
540+ one shape, the calling surface is interchangeable.
541+ </div>
542+ </details>
543+
544+ <details class="faq-item">
545+ <summary>
546+ How does it know what is safe to auto-run?
547+ <span class="faq-mark"
548+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
549+ stroke-width="2" stroke-linecap="round"
550+ ><path d="M12 5v14M5 12h14"></path></svg
551+ ></span
552+ >
553+ </summary>
554+ <div class="faq-body">
555+ Executor preserves the semantics of whatever it imported: GET vs
556+ DELETE for OpenAPI, destructiveHint for MCP, and mutations for
557+ GraphQL. That tells the agent what it can run on its own and what
558+ should pull you back into the loop.
559+ </div>
560+ </details>
561+
562+ <details class="faq-item">
563+ <summary>
564+ Is it open source? Can I self-host?
565+ <span class="faq-mark"
566+ ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
567+ stroke-width="2" stroke-linecap="round"
568+ ><path d="M12 5v14M5 12h14"></path></svg
569+ ></span
570+ >
571+ </summary>
572+ <div class="faq-body">
573+ Yes. Executor is MIT licensed and built on the SDK we publish to
574+ npm. Run the desktop app locally, self-host the server, or use
575+ the hosted cloud. Same code paths, different deployment.
576+ </div>
577+ </details>
578+ </div>
579+ </div>
580+ </section>
581+
582+ {/* ─── PRICING ─── */}
583+ <section id="pricing" class="py-20 sm:py-28 border-t border-rule">
584+ <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10">
329585 <div class="mb-14 mx-auto text-center max-w-2xl">
330− <div
331− class="font-mono text-[12px] uppercase tracking-[0.18em] text-ink-3 mb-4"
332− >
333− Get started
334− </div>
335− <h2
336− class="text-[clamp(2.2rem,4.5vw,3.4rem)] font-semibold tracking-[-0.02em] leading-[1.05] text-ink mx-auto max-w-[20ch] text-balance"
337− >
338− Pick your <span class="serif-italic">path</span>.
586+ <div class="eyebrow mb-4">Pricing</div>
587+ <h2 class="section-title mx-auto max-w-[20ch]">
588+ Start free, pay as you <span class="serif-italic">run</span>.
339589 </h2>
340590 </div>
341591
342− <div
343− class="grid md:grid-cols-2 gap-6 max-w-[760px] mx-auto"
344− >
345− {/* Cloud */}
592+ <div class="grid md:grid-cols-3 gap-6 max-w-[1000px] mx-auto items-stretch">
593+ {/* Free */}
594+ <div class="surface-card path-card p-8 flex flex-col">
595+ <div class="flex items-center gap-2 mb-4">
596+ <span class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3"
597+ >Get started</span
598+ >
599+ </div>
600+ <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1">
601+ Free
602+ </h3>
603+ <p class="text-[14px] text-ink-3 mb-5">For small teams getting started</p>
604+ <div class="flex items-baseline gap-1.5 mb-6">
605+ <span
606+ class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums"
607+ >$0</span
608+ >
609+ <span class="text-[13.5px] text-ink-3">/ month</span>
610+ </div>
611+ <a href="/cloud" class="btn-primary self-start mb-7">Start free →</a>
612+ <ul class="check-list">
613+ {[
614+ "Up to 3 members",
615+ "10,000 included executions per month",
616+ "$0.20 per 1,000 additional executions",
617+ "Unlimited integrations",
618+ ].map((f) => (
619+ <li>
620+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
621+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
622+ ><path d="M20 6 9 17l-5-5"></path></svg
623+ >{f}
624+ </li>
625+ ))}
626+ </ul>
627+ </div>
628+
629+ {/* Team */}
346630 <div
347− class="surface-card p-8 flex flex-col"
631+ class="surface-card path-card beam-card p-8 flex flex-col"
348632 style="border-color: rgba(99,91,255,0.25)"
349633 >
350634 <div class="flex items-center gap-2 mb-4">
@@ -353,54 +637,105 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 2 unmodified lines
353637 style="color: var(--color-accent)">Recommended</span
354638 >
355639 </div>
356− <h3
357− class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"
358− >
359− Cloud
640+ <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1">
641+ Team
360642 </h3>
361− <p
362− class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty"
363− >
364− Hosted Executor. Auth, sync, policies, and the whole team
365− online in five minutes. Free tier to start.
366− </p>
367− <p class="mb-6 text-[13.5px] leading-[1.55] text-ink-3 flex-1">
368− <SelfHostContactModal client:load variant="inline" />
369− </p>
370− <a href="/cloud" class="btn-primary self-start">Try Cloud →</a>
643+ <p class="text-[14px] text-ink-3 mb-5">For growing organizations</p>
644+ <div class="flex items-baseline gap-1.5 mb-6">
645+ <span
646+ class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums"
647+ >$49</span
648+ >
649+ <span class="text-[13.5px] text-ink-3">/ org / month</span>
650+ </div>
651+ <a href="/cloud" class="btn-primary self-start mb-7">Start Team →</a>
652+ <ul class="check-list">
653+ {[
654+ "Unlimited members",
655+ "250,000 included executions per month",
656+ "5 minute execution timeout",
657+ "Join by team domain",
658+ "$0.20 per 1,000 additional executions",
659+ ].map((f) => (
660+ <li>
661+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
662+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
663+ ><path d="M20 6 9 17l-5-5"></path></svg
664+ >{f}
665+ </li>
666+ ))}
667+ </ul>
371668 </div>
372669
373− {/* Desktop */}
374− <div class="surface-card p-8 flex flex-col">
670+ {/* Enterprise */}
671+ <div class="surface-card path-card p-8 flex flex-col">
375672 <div class="flex items-center gap-2 mb-4">
376− <span
377− class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3"
378− >Open source</span
673+ <span class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3"
674+ >Custom needs</span
379675 >
380676 </div>
381− <h3
382− class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"
383− >
384− Desktop
677+ <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1">
678+ Enterprise
385679 </h3>
386− <p
387− class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1"
388− >
389− A native app for Mac, Windows, and Linux. Everything stays
390− on your machine — sources, secrets, sessions. MIT licensed.
391− </p>
392− <button
393− type="button"
394− data-jump-to-local
395− class="btn-primary self-start"
680+ <p class="text-[14px] text-ink-3 mb-5">For orgs with custom needs</p>
681+ <div class="flex items-baseline gap-1.5 mb-6">
682+ <span
683+ class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums"
684+ >Custom</span
685+ >
686+ </div>
687+ <a
688+ href="mailto:rhys@executor.sh?subject=Executor%20Enterprise%20inquiry"
689+ class="btn-primary self-start mb-7">Contact us →</a
396690 >
397− Download →
398− </button>
691+ <p class="text-[12.5px] font-medium text-ink mb-2.5">Everything in Team, plus</p>
692+ <ul class="check-list">
693+ {[
694+ "Self-hosted or dedicated cloud deployment support",
695+ "SSO / SAML & SCIM provisioning",
696+ "Audit logs for every tool call",
697+ "Dedicated support & onboarding",
698+ "Security reviews, DPA & SOC 2 on request",
699+ ].map((f) => (
700+ <li>
701+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
702+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
703+ ><path d="M20 6 9 17l-5-5"></path></svg
704+ >{f}
705+ </li>
706+ ))}
707+ </ul>
399708 </div>
400709 </div>
401710 </div>
402711 </section>
403712
713+ {/* ─── FINAL CTA ─── */}
714+ <section class="cta-band py-20 sm:py-24">
715+ <div class="reveal max-w-[760px] mx-auto px-6 sm:px-10 text-center">
716+ <h2 class="section-title mb-8 mx-auto max-w-[18ch]">
717+ Connect your agent to
718+ <span class="serif-italic">everything</span>.
719+ </h2>
720+ <div class="flex flex-wrap items-center justify-center gap-3">
721+ <a href="#install" class="btn-primary btn-primary--hero">Run it locally →</a>
722+ <a
723+ href={GH}
724+ class="btn-primary btn-primary--hero"
725+ style="background:white;color:var(--color-ink);box-shadow:0 0 0 1px var(--color-rule-strong)"
726+ >Star on GitHub →</a
727+ >
728+ </div>
729+ <p class="mt-5 text-[13.5px] text-ink-3">
730+ or <a
731+ href="/cloud"
732+ class="underline underline-offset-2 hover:text-ink-2 transition-colors"
733+ >try Executor Cloud →</a
734+ >
735+ </p>
736+ </div>
737+ </section>
738+
404739 {/* ─── FOOTER ─── */}
405740 <footer class="border-t border-rule py-10">
406741 <div
@@ -414,13 +749,11 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 7 unmodified lines
414749 >
415750 </div>
416751 <div class="flex items-center gap-6 text-[13px] text-ink-2">
417− <a
418− href="https://github.com/RhysSullivan/executor"
419− class="hover:text-ink transition-colors">GitHub</a
420− >
421− <a href="/privacy" class="hover:text-ink transition-colors"
422− >Privacy</a
752+ <a href="https://executor.sh/docs" class="hover:text-ink transition-colors"
753+ >Docs</a
423754 >
755+ <a href={GH} class="hover:text-ink transition-colors">GitHub</a>
756+ <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>
424757 <a href="/terms" class="hover:text-ink transition-colors">Terms</a>
425758 </div>
426759 </div>
@@ -430,98 +763,93 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 3 unmodified lines
430763 </Layout>
431764
432765 <script>
433− document
434− .querySelectorAll<HTMLButtonElement>("button[data-copy]")
435− .forEach((btn) => {
436− let timer: ReturnType<typeof setTimeout> | null = null;
766+ // Copy buttons. Use the async Clipboard API in secure contexts and fall back
767+ // to a hidden textarea + execCommand otherwise, so copy (and the copied-state
768+ // animation) also works over plain HTTP, e.g. a LAN / tailnet dev host where
769+ // navigator.clipboard is undefined.
770+ const copyToClipboard = async (text: string): Promise<boolean> => {
771+ if (window.isSecureContext && navigator.clipboard?.writeText) {
772+ try {
773+ await navigator.clipboard.writeText(text);
774+ return true;
775+ } catch {}
776+ }
777+ const ta = document.createElement("textarea");
778+ ta.value = text;
779+ ta.setAttribute("readonly", "");
780+ ta.style.position = "fixed";
781+ ta.style.top = "0";
782+ ta.style.left = "0";
783+ ta.style.opacity = "0";
784+ document.body.appendChild(ta);
785+ ta.select();
786+ let ok = false;
787+ try {
788+ ok = document.execCommand("copy");
789+ } catch {}
790+ document.body.removeChild(ta);
791+ return ok;
792+ };
437793
438− btn.addEventListener("click", async () => {
439− const text = btn.dataset.copy ?? "";
440− try {
441− await navigator.clipboard.writeText(text);
442− btn.dataset.copied = "true";
443− const event = btn.dataset.event;
444− if (event) {
445− window.dispatchEvent(
446− new CustomEvent("analytics:capture", {
447− detail: { event, properties: { surface: "marketing_hero" } },
448− }),
449− );
450− }
451− } catch {
452− btn.dataset.copied = "false";
453− }
454− if (timer) clearTimeout(timer);
455− timer = setTimeout(() => {
456− delete btn.dataset.copied;
457− }, 1500);
458− });
794+ for (const btn of document.querySelectorAll<HTMLButtonElement>(
795+ "button[data-copy]",
796+ )) {
797+ let timer: ReturnType<typeof setTimeout> | null = null;
798+ btn.addEventListener("click", async () => {
799+ const ok = await copyToClipboard(btn.dataset.copy ?? "");
800+ btn.dataset.copied = ok ? "true" : "false";
801+ const event = btn.dataset.event;
802+ if (ok && event) {
803+ window.dispatchEvent(
804+ new CustomEvent("analytics:capture", {
805+ detail: { event, properties: { surface: "marketing_hero" } },
806+ }),
807+ );
808+ }
809+ if (timer) clearTimeout(timer);
810+ timer = setTimeout(() => {
811+ delete btn.dataset.copied;
812+ }, 1500);
459813 });
814+ }
460815 </script>
461816
462817 <script>
463− // ── Hero tab toggle: Try in Cloud / Try locally ────────────────────
464− // Drives the sliding pill indicator + pane visibility via a single
465− // `data-active` attribute on the wrapper. Default "cloud" so no-JS
466− // visitors still see the recommended path.
467− const heroToggle = document.querySelector<HTMLElement>(".hero-toggle");
468− const tabToggle = heroToggle?.querySelector<HTMLElement>(".tab-toggle");
469− const TAB_PARAM = "try";
470− const isHeroTab = (v: unknown): v is "cloud" | "local" =>
471− v === "cloud" || v === "local";
472− const syncTabToUrl = (tab: "cloud" | "local") => {
473− const url = new URL(window.location.href);
474− if (url.searchParams.get(TAB_PARAM) === tab) return;
475− url.searchParams.set(TAB_PARAM, tab);
476− window.history.replaceState(window.history.state, "", url);
477− };
478− const setActiveTab = (tab: "cloud" | "local", updateUrl = true) => {
479− if (!heroToggle || !tabToggle) return;
480− heroToggle.dataset.active = tab;
481− tabToggle.dataset.active = tab;
482− tabToggle.querySelectorAll<HTMLButtonElement>("button[data-tab]").forEach(
483− (btn) => {
484− btn.setAttribute(
485− "aria-selected",
486− btn.dataset.tab === tab ? "true" : "false",
487− );
488− },
818+ // ── Hero "For humans / For agents" audience toggle ─────────────────
819+ const heroCta = document.querySelector<HTMLElement>(".hero-cta");
820+ if (heroCta) {
821+ const tabs = Array.from(
822+ heroCta.querySelectorAll<HTMLButtonElement>("[data-audience-set]"),
489823 );
490− if (updateUrl) syncTabToUrl(tab);
491− };
492− if (heroToggle && tabToggle) {
493− const initial = new URLSearchParams(window.location.search).get(TAB_PARAM);
494− if (isHeroTab(initial)) setActiveTab(initial, false);
495− tabToggle.querySelectorAll<HTMLButtonElement>("button[data-tab]").forEach(
496− (btn) => {
497− btn.addEventListener("click", () => {
498− const tab = btn.dataset.tab as "cloud" | "local" | undefined;
499− if (tab) setActiveTab(tab);
500− });
501− },
824+ const panes = Array.from(
825+ heroCta.querySelectorAll<HTMLElement>("[data-audience-pane]"),
502826 );
503− window.addEventListener("popstate", () => {
504− const next = new URLSearchParams(window.location.search).get(TAB_PARAM);
505− setActiveTab(isHeroTab(next) ? next : "cloud", false);
506− });
827+ const setAudience = (aud: string) => {
828+ heroCta.dataset.audience = aud;
829+ for (const t of tabs) {
830+ t.setAttribute(
831+ "aria-selected",
832+ t.dataset.audienceSet === aud ? "true" : "false",
833+ );
834+ }
835+ for (const p of panes) {
836+ p.hidden = p.dataset.audiencePane !== aud;
837+ }
838+ };
839+ for (const t of tabs) {
840+ t.addEventListener("click", () =>
841+ setAudience(t.dataset.audienceSet ?? "humans"),
842+ );
843+ }
507844 }
845+</script>
508846
509− // Desktop card "Download →" CTA jumps to the hero, switches to Local,
510− // and lets the auto-detect script there pick the right OS button.
511− document
512− .querySelectorAll<HTMLButtonElement>("button[data-jump-to-local]")
513− .forEach((btn) => {
514− btn.addEventListener("click", () => {
515− setActiveTab("local");
516− heroToggle?.scrollIntoView({ behavior: "smooth", block: "center" });
517− });
518− });
519−
847+<script>
520848 // ── Auto-detect download platform ──────────────────────────────────
521− // Shows ONE primary button matching the visitor's OS in the "Try
522− // locally" pane; tucks the others behind an "Other platforms"
523− // popover. No-JS / unrecognised UA: all four buttons visible (still
524− // functional, just less polished).
849+ // Shows ONE primary button matching the visitor's OS in the Desktop
850+ // card; tucks the others behind an "Other platforms" popover. No-JS /
851+ // unrecognised UA: all four buttons visible (still functional, just
852+ // less polished).
525853 type DownloadOs = "mac-arm64" | "mac-x64" | "win-x64" | "linux-x64";
526854
527855 type UserAgentData = {
@@ -594,3 +922,31 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 66 unmodified lines
594922 other.classList.remove("hidden");
595923 })();
596924 </script>
925+
926+<script>
927+ // ── Scroll reveals ─────────────────────────────────────────────────
928+ // Sections fade + rise into view as they enter the viewport. The
929+ // hidden initial state is applied by JS (`.reveal-init`) so no-JS
930+ // visitors and reduced-motion users always see fully-rendered content.
931+ const reveals = Array.from(
932+ document.querySelectorAll<HTMLElement>(".reveal"),
933+ );
934+ const prefersReduced = window.matchMedia(
935+ "(prefers-reduced-motion: reduce)",
936+ ).matches;
937+ if (reveals.length > 0 && !prefersReduced && "IntersectionObserver" in window) {
938+ for (const el of reveals) el.classList.add("reveal-init");
939+ const io = new IntersectionObserver(
940+ (entries) => {
941+ for (const entry of entries) {
942+ if (entry.isIntersecting) {
943+ entry.target.classList.add("is-visible");
944+ io.unobserve(entry.target);
945+ }
946+ }
947+ },
948+ { threshold: 0.12, rootMargin: "0px 0px -8% 0px" },
949+ );
950+ for (const el of reveals) io.observe(el);
951+ }
952+</script>
apps/marketing/src/styles/global.css+1064−15
@@ -66,7 +66,7 @@ body { · 65 unmodified lines
6666 opacity: 0.08;
6767 }
6868
69−/* Soft fade overlay — paint the surface color back over the pattern's bottom */
69+/* Soft fade overlay: paint the surface color back over the pattern's bottom */
7070 .pattern-fade-overlay {
7171 position: absolute;
7272 inset: 0;
@@ -75,7 +75,7 @@ body { · 2 unmodified lines
7575 z-index: 0;
7676 }
7777
78−/* Italic serif accent — pairs with sharp Geist display */
78+/* Italic serif accent: pairs with sharp Geist display */
7979 .serif-italic {
8080 font-family: var(--font-serif);
8181 font-style: italic;
@@ -111,7 +111,7 @@ body { · 29 unmodified lines
111111 animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both;
112112 }
113113
114−/* Hairline-bordered surface for the beam panel — minimal elevation */
114+/* Hairline-bordered surface for the beam panel: minimal elevation */
115115 .surface-card {
116116 background: white;
117117 border: 1px solid var(--color-rule);
@@ -136,7 +136,7 @@ body { · 18 unmodified lines
136136 background: #1f1f24;
137137 }
138138
139−/* Light, bordered counterpart to .btn-primary — a real secondary CTA. Sized to
139+/* Light, bordered counterpart to .btn-primary, a real secondary CTA. Sized to
140140 match .btn-primary--hero so it balances under the hero's primary button. */
141141 .btn-secondary {
142142 display: inline-flex;
@@ -209,7 +209,7 @@ body { · 66 unmodified lines
209209 }
210210
211211 /* ──────────────────────────────────────────────────────────────
212− Hero toggle — segmented "Try in Cloud / Try locally" control.
212+ Hero toggle: segmented "Try in Cloud / Try locally" control.
213213 A sliding white pill animates between two equal-width tabs.
214214 The active pane is revealed below; inactive panes hide.
215215 ─────────────────────────────────────────────────────────── */
@@ -271,7 +271,7 @@ body { · 55 unmodified lines
271271 display: flex;
272272 }
273273
274−/* Per-pane description — sits above the CTA. Smaller than the hero tagline,
274+/* Per-pane description: sits above the CTA. Smaller than the hero tagline,
275275 tight max-width so two lines wrap naturally on narrow widths. */
276276 .pane-desc {
277277 font-size: 14.5px;
@@ -284,7 +284,7 @@ body { · 6 unmodified lines
284284 margin: 0;
285285 }
286286
287−/* Subtle metadata strip under each pane's CTA — uppercase mono dots */
287+/* Subtle metadata strip under each pane's CTA: uppercase mono dots */
288288 .pane-meta {
289289 display: inline-flex;
290290 flex-wrap: wrap;
@@ -334,18 +334,13 @@ body { · 43 unmodified lines
334334 user-select: none;
335335 font-size: 13px;
336336 color: var(--color-ink-3);
337− padding: 0.4rem 0.6rem;
338− border-radius: 8px;
339− transition:
340− color 0.15s ease,
341− background 0.15s ease;
337+ transition: color 0.15s ease;
342338 }
343339 .download-other > summary::-webkit-details-marker {
344340 display: none;
345341 }
346342 .download-other > summary:hover {
347343 color: var(--color-ink-2);
348− background: var(--color-surface-2);
349344 }
350345 .download-other[open] > summary {
351346 color: var(--color-ink);
@@ -384,14 +379,14 @@ body { · 32 unmodified lines
384379 background: var(--color-surface-2);
385380 }
386381
387−/* Larger primary button for the hero pane — keeps btn-primary base sizing
382+/* Larger primary button for the hero pane: keeps btn-primary base sizing
388383 for nav/cards but bumps the hero's CTA to a more confident scale */
389384 .btn-primary--hero {
390385 padding: 0.7rem 1.25rem;
391386 font-size: 14.5px;
392387 }
393388
394−/* Accent-colored inline link — for secondary callouts inside card bodies */
389+/* Accent-colored inline link: for secondary callouts inside card bodies */
395390 .link-accent {
396391 color: var(--color-accent);
397392 font-weight: 500;
@@ -470,3 +465,1057 @@ body { · 72 unmodified lines
470465 color: var(--color-ink);
471466 font-weight: 600;
472467 }
468+
469+/* ════════════════════════════════════════════════════════════════
470+ COMPONENT KIT: sections beyond the hero
471+ Built to echo the reference sites' best parts while staying on
472+ Executor's off-white / graph-paper / Geist + serif-italic brand:
473+ - Harbor: code window, trace waterfall, auditable security cards, FAQ
474+ - Corsair: connection matrix, permission-gate card, mono path chips,
475+ plus-mark grid corners
476+ - Zapier: trust strip with agent logos + capability badges
477+ ═══════════════════════════════════════════════════════════════ */
478+
479+/* ── Section heading helpers ── */
480+.eyebrow {
481+ font-family: var(--font-mono);
482+ font-size: 12px;
483+ font-weight: 500;
484+ text-transform: uppercase;
485+ letter-spacing: 0.18em;
486+ color: var(--color-ink-3);
487+}
488+.section-title {
489+ font-size: clamp(2rem, 4.2vw, 3rem);
490+ font-weight: 600;
491+ letter-spacing: -0.022em;
492+ line-height: 1.06;
493+ color: var(--color-ink);
494+ text-wrap: balance;
495+}
496+.section-sub {
497+ font-size: 16.5px;
498+ line-height: 1.6;
499+ color: var(--color-ink-2);
500+ text-wrap: pretty;
501+}
502+
503+/* ── Trust strip (Zapier): agent logos + capability badges ── */
504+.logo-wall {
505+ display: grid;
506+ grid-template-columns: repeat(2, minmax(0, 1fr));
507+ gap: 1px;
508+ background: var(--color-rule);
509+ border: 1px solid var(--color-rule);
510+ border-radius: 14px;
511+ overflow: hidden;
512+}
513+@media (min-width: 640px) {
514+ .logo-wall {
515+ grid-template-columns: repeat(3, minmax(0, 1fr));
516+ }
517+}
518+@media (min-width: 900px) {
519+ .logo-wall {
520+ grid-template-columns: repeat(6, minmax(0, 1fr));
521+ }
522+}
523+.logo-cell {
524+ display: flex;
525+ flex-direction: column;
526+ align-items: center;
527+ justify-content: center;
528+ gap: 0.55rem;
529+ padding: 1.4rem 0.75rem;
530+ background: white;
531+ transition: background 0.15s ease;
532+}
533+.logo-cell:hover {
534+ background: var(--color-surface);
535+}
536+.logo-cell svg {
537+ width: 26px;
538+ height: 26px;
539+ color: var(--color-ink);
540+ opacity: 0.82;
541+}
542+.logo-cell .logo-name {
543+ font-size: 12px;
544+ font-weight: 500;
545+ color: var(--color-ink-2);
546+ letter-spacing: -0.005em;
547+}
548+
549+.badge-chip {
550+ display: inline-flex;
551+ align-items: center;
552+ gap: 0.45rem;
553+ font-family: var(--font-mono);
554+ font-size: 11.5px;
555+ font-weight: 500;
556+ text-transform: uppercase;
557+ letter-spacing: 0.1em;
558+ color: var(--color-ink-3);
559+}
560+.badge-chip svg {
561+ width: 13px;
562+ height: 13px;
563+ color: var(--color-accent);
564+}
565+.badge-sep {
566+ width: 3px;
567+ height: 3px;
568+ border-radius: 999px;
569+ background: var(--color-ink-3);
570+ opacity: 0.4;
571+}
572+
573+/* ── Code window (Harbor / Corsair): light, brand-matched ── */
574+.code-window {
575+ background: white;
576+ border: 1px solid var(--color-rule);
577+ border-radius: 14px;
578+ overflow: hidden;
579+ box-shadow:
580+ 0 1px 2px rgba(13, 13, 16, 0.03),
581+ 0 18px 40px -28px rgba(13, 13, 16, 0.14);
582+}
583+.code-window__bar {
584+ display: flex;
585+ align-items: center;
586+ gap: 0.7rem;
587+ padding: 0.7rem 1rem;
588+ border-bottom: 1px solid var(--color-rule);
589+ background: color-mix(in srgb, var(--color-surface-2) 55%, white);
590+}
591+.code-window__dots {
592+ display: inline-flex;
593+ gap: 6px;
594+}
595+.code-window__dots i {
596+ width: 10px;
597+ height: 10px;
598+ border-radius: 999px;
599+ background: var(--color-rule-strong);
600+}
601+.code-window__file {
602+ font-family: var(--font-mono);
603+ font-size: 12.5px;
604+ color: var(--color-ink-2);
605+ letter-spacing: -0.005em;
606+}
607+.code-window__tag {
608+ margin-left: auto;
609+ font-family: var(--font-mono);
610+ font-size: 10.5px;
611+ font-weight: 600;
612+ text-transform: uppercase;
613+ letter-spacing: 0.12em;
614+ color: var(--color-accent);
615+}
616+.code-window__body {
617+ margin: 0;
618+ padding: 1.25rem 1.4rem;
619+ font-family: var(--font-mono);
620+ font-size: 13px;
621+ line-height: 1.75;
622+ color: var(--color-ink);
623+ overflow-x: auto;
624+ tab-size: 2;
625+}
626+.code-window__body code {
627+ font-family: inherit;
628+}
629+.tok-c {
630+ color: var(--color-ink-3);
631+}
632+.tok-k {
633+ color: #8250df;
634+}
635+.tok-s {
636+ color: #2f7d52;
637+}
638+.tok-f {
639+ color: var(--color-ink);
640+ font-weight: 600;
641+}
642+.tok-a {
643+ color: var(--color-accent);
644+ font-weight: 600;
645+}
646+.tok-n {
647+ color: #b4690e;
648+}
649+.tok-p {
650+ color: var(--color-ink-3);
651+}
652+
653+/* ── Capability grid ── */
654+.cap-grid {
655+ display: grid;
656+ gap: 1px;
657+ background: var(--color-rule);
658+ border: 1px solid var(--color-rule);
659+ border-radius: 16px;
660+ overflow: hidden;
661+}
662+@media (min-width: 720px) {
663+ .cap-grid {
664+ grid-template-columns: repeat(2, minmax(0, 1fr));
665+ }
666+}
667+@media (min-width: 1040px) {
668+ .cap-grid {
669+ grid-template-columns: repeat(3, minmax(0, 1fr));
670+ }
671+}
672+.cap-card {
673+ position: relative;
674+ display: flex;
675+ flex-direction: column;
676+ gap: 0.6rem;
677+ padding: 1.6rem 1.5rem 1.7rem;
678+ background: white;
679+ transition: background 0.15s ease;
680+}
681+/* Coming-soon card: greyed out, with a corner badge that stays legible. */
682+.cap-card--soon > :not(.cap-soon-badge) {
683+ opacity: 0.45;
684+}
685+.cap-soon-badge {
686+ position: absolute;
687+ top: 1.45rem;
688+ right: 1.4rem;
689+ font-family: var(--font-mono);
690+ font-size: 10px;
691+ text-transform: uppercase;
692+ letter-spacing: 0.1em;
693+ color: var(--color-ink-3);
694+ background: var(--color-surface-2);
695+ border: 1px solid var(--color-rule);
696+ border-radius: 999px;
697+ padding: 3px 9px;
698+}
699+.cap-card:hover {
700+ background: var(--color-surface);
701+}
702+.cap-card .cap-icon {
703+ display: inline-flex;
704+ align-items: center;
705+ justify-content: center;
706+ width: 34px;
707+ height: 34px;
708+ border-radius: 9px;
709+ background: color-mix(in srgb, var(--color-accent) 9%, white);
710+ border: 1px solid color-mix(in srgb, var(--color-accent) 18%, transparent);
711+ color: var(--color-accent);
712+ margin-bottom: 0.35rem;
713+}
714+.cap-card .cap-icon svg {
715+ width: 17px;
716+ height: 17px;
717+}
718+.cap-card h3 {
719+ font-size: 16px;
720+ font-weight: 600;
721+ letter-spacing: -0.01em;
722+ color: var(--color-ink);
723+}
724+.cap-card p {
725+ font-size: 14px;
726+ line-height: 1.55;
727+ color: var(--color-ink-2);
728+ text-wrap: pretty;
729+}
730+.cap-card .cap-visual {
731+ margin-top: 0.5rem;
732+}
733+
734+/* ── Trace waterfall (Harbor) ── */
735+.trace {
736+ display: flex;
737+ flex-direction: column;
738+ gap: 0.45rem;
739+}
740+.trace-row {
741+ display: flex;
742+ align-items: center;
743+ gap: 0.6rem;
744+ font-family: var(--font-mono);
745+ font-size: 11px;
746+}
747+.trace-row .lbl {
748+ flex-shrink: 0;
749+ width: 8.5rem;
750+ color: var(--color-ink-2);
751+ white-space: nowrap;
752+ overflow: hidden;
753+ text-overflow: ellipsis;
754+}
755+.trace-row .track {
756+ position: relative;
757+ flex: 1;
758+ height: 7px;
759+ background: var(--color-surface-2);
760+ border-radius: 999px;
761+}
762+.trace-row .bar {
763+ position: absolute;
764+ top: 0;
765+ bottom: 0;
766+ background: var(--color-accent);
767+ opacity: 0.85;
768+ border-radius: 999px;
769+}
770+.trace-row.is-root .bar {
771+ background: var(--color-ink);
772+ opacity: 0.75;
773+}
774+.trace-row .dur {
775+ flex-shrink: 0;
776+ width: 3rem;
777+ text-align: right;
778+ color: var(--color-ink-3);
779+}
780+
781+/* ── Connection matrix (Corsair) ── */
782+.matrix {
783+ font-family: var(--font-mono);
784+ font-size: 10.5px;
785+}
786+.matrix-head,
787+.matrix-row {
788+ display: grid;
789+ grid-template-columns: 5.5rem repeat(5, minmax(0, 1fr));
790+ align-items: center;
791+ gap: 0.3rem;
792+}
793+.matrix-head {
794+ color: var(--color-ink-3);
795+ padding-bottom: 0.4rem;
796+ margin-bottom: 0.4rem;
797+ border-bottom: 1px solid var(--color-rule);
798+}
799+.matrix-head span {
800+ text-align: center;
801+}
802+.matrix-row {
803+ padding: 0.28rem 0;
804+}
805+.matrix-row .who {
806+ color: var(--color-ink-2);
807+ white-space: nowrap;
808+ overflow: hidden;
809+ text-overflow: ellipsis;
810+}
811+.matrix-row .cell {
812+ display: flex;
813+ justify-content: center;
814+}
815+.stat {
816+ width: 9px;
817+ height: 9px;
818+ border-radius: 999px;
819+}
820+.stat-ok {
821+ background: #2f9e6f;
822+}
823+.stat-warn {
824+ background: #d9883a;
825+}
826+.stat-off {
827+ background: var(--color-ink-3);
828+ opacity: 0.3;
829+}
830+
831+/* ── Permission-gate card (Corsair) ── */
832+.gate-card {
833+ background: white;
834+ border: 1px solid var(--color-rule);
835+ border-radius: 12px;
836+ overflow: hidden;
837+}
838+.gate-card__head {
839+ display: flex;
840+ align-items: center;
841+ gap: 0.5rem;
842+ padding: 0.7rem 0.95rem;
843+ border-bottom: 1px solid var(--color-rule);
844+ font-size: 12.5px;
845+ font-weight: 500;
846+ color: var(--color-ink-2);
847+}
848+.gate-card__body {
849+ padding: 0.95rem;
850+ font-size: 13px;
851+ line-height: 1.5;
852+ color: var(--color-ink-2);
853+}
854+.gate-card__body .subj {
855+ font-weight: 600;
856+ color: var(--color-ink);
857+ margin-bottom: 0.3rem;
858+}
859+.gate-card__actions {
860+ display: flex;
861+ gap: 0.5rem;
862+ padding: 0 0.95rem 0.95rem;
863+}
864+.gate-btn {
865+ flex: 1;
866+ text-align: center;
867+ padding: 0.5rem 0.7rem;
868+ border-radius: 8px;
869+ font-size: 13px;
870+ font-weight: 500;
871+ border: 1px solid var(--color-rule);
872+}
873+.gate-btn.deny {
874+ color: var(--color-ink-2);
875+ background: white;
876+}
877+.gate-btn.approve {
878+ color: white;
879+ background: var(--color-accent);
880+ border-color: transparent;
881+}
882+
883+/* ── Auditable security cards (Harbor) ── */
884+.check-list {
885+ list-style: none;
886+ display: grid;
887+ gap: 0.65rem;
888+ padding: 0;
889+ margin: 0;
890+}
891+.check-list li {
892+ display: flex;
893+ align-items: flex-start;
894+ gap: 0.6rem;
895+ font-size: 14px;
896+ line-height: 1.5;
897+ color: var(--color-ink-2);
898+}
899+.check-list li svg {
900+ flex-shrink: 0;
901+ width: 15px;
902+ height: 15px;
903+ margin-top: 2px;
904+ color: var(--color-accent);
905+}
906+
907+/* ── Choose your path mono chips (Corsair) ── */
908+.path-chip {
909+ display: inline-flex;
910+ align-items: center;
911+ font-family: var(--font-mono);
912+ font-size: 12.5px;
913+ color: var(--color-ink-2);
914+ background: var(--color-surface-2);
915+ border: 1px solid var(--color-rule);
916+ border-radius: 7px;
917+ padding: 0.3rem 0.6rem;
918+}
919+
920+/* ── FAQ accordion (Harbor / Corsair) ── */
921+.faq-item {
922+ border-bottom: 1px solid var(--color-rule);
923+}
924+.faq-item summary {
925+ display: flex;
926+ align-items: center;
927+ justify-content: space-between;
928+ gap: 1rem;
929+ padding: 1.25rem 0;
930+ cursor: pointer;
931+ list-style: none;
932+ font-size: 16px;
933+ font-weight: 500;
934+ letter-spacing: -0.01em;
935+ color: var(--color-ink);
936+ transition: color 0.15s ease;
937+}
938+.faq-item summary::-webkit-details-marker {
939+ display: none;
940+}
941+.faq-item summary:hover {
942+ color: var(--color-accent);
943+}
944+.faq-item .faq-mark {
945+ flex-shrink: 0;
946+ position: relative;
947+ width: 14px;
948+ height: 14px;
949+ transition: transform 0.2s ease;
950+ color: var(--color-ink-3);
951+}
952+.faq-item[open] .faq-mark {
953+ transform: rotate(45deg);
954+ color: var(--color-accent);
955+}
956+.faq-item .faq-body {
957+ padding: 0 0 1.4rem;
958+ max-width: 68ch;
959+ font-size: 14.5px;
960+ line-height: 1.65;
961+ color: var(--color-ink-2);
962+ text-wrap: pretty;
963+}
964+
965+/* ── Plus-mark grid corners (Corsair signature) ── */
966+.plus-grid {
967+ position: relative;
968+}
969+.plus-mark {
970+ position: absolute;
971+ width: 14px;
972+ height: 14px;
973+ color: var(--color-accent);
974+ opacity: 0.4;
975+ pointer-events: none;
976+}
977+.plus-mark::before,
978+.plus-mark::after {
979+ content: "";
980+ position: absolute;
981+ background: currentColor;
982+}
983+.plus-mark::before {
984+ left: 50%;
985+ top: 0;
986+ width: 1.5px;
987+ height: 100%;
988+ transform: translateX(-50%);
989+}
990+.plus-mark::after {
991+ top: 50%;
992+ left: 0;
993+ height: 1.5px;
994+ width: 100%;
995+ transform: translateY(-50%);
996+}
997+
998+/* ── Final CTA band ── */
999+.cta-band {
1000+ background: var(--color-surface-2);
1001+ border-top: 1px solid var(--color-rule);
1002+ border-bottom: 1px solid var(--color-rule);
1003+}
1004+
1005+/* ════════════════════════════════════════════════════════════════
1006+ MOTION KIT: scroll reveals + accents
1007+ Adapted from magicui (blur-fade, border-beam, animated-shiny-text)
1008+ and launch-ui (hero glow, badge), kept restrained for the off-white
1009+ editorial brand. Every effect is subtle, accent-tinted, and fully
1010+ disabled under prefers-reduced-motion. The hidden reveal state is
1011+ only applied by JS, so no-JS visitors see everything revealed.
1012+ ═══════════════════════════════════════════════════════════════ */
1013+
1014+/* Scroll reveal (magicui blur-fade, restrained): blocks rise, fade,
1015+ and de-blur as they enter the viewport. `.reveal-init` is added by
1016+ the page script; without it (no JS / reduced motion) content is
1017+ fully visible. */
1018+.reveal.reveal-init {
1019+ opacity: 0;
1020+ transform: translateY(14px);
1021+ filter: blur(5px);
1022+ will-change: opacity, transform, filter;
1023+}
1024+.reveal.reveal-init.is-visible {
1025+ opacity: 1;
1026+ transform: none;
1027+ filter: none;
1028+ transition:
1029+ opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
1030+ transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
1031+ filter 0.7s cubic-bezier(0.22, 1, 0.36, 1);
1032+}
1033+
1034+/* Trace waterfall bars grow from their start as the capability grid
1035+ reveals, staggered so it reads like a live trace filling in. */
1036+.reveal.reveal-init .trace-row .bar {
1037+ transform: scaleX(0);
1038+ transform-origin: left center;
1039+}
1040+.reveal.reveal-init.is-visible .trace-row .bar {
1041+ transform: scaleX(1);
1042+ transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
1043+}
1044+.reveal.reveal-init.is-visible .trace-row:nth-child(1) .bar {
1045+ transition-delay: 0.18s;
1046+}
1047+.reveal.reveal-init.is-visible .trace-row:nth-child(2) .bar {
1048+ transition-delay: 0.3s;
1049+}
1050+.reveal.reveal-init.is-visible .trace-row:nth-child(3) .bar {
1051+ transition-delay: 0.42s;
1052+}
1053+.reveal.reveal-init.is-visible .trace-row:nth-child(4) .bar {
1054+ transition-delay: 0.54s;
1055+}
1056+
1057+@media (prefers-reduced-motion: reduce) {
1058+ .reveal.reveal-init,
1059+ .reveal.reveal-init.is-visible {
1060+ opacity: 1;
1061+ transform: none;
1062+ filter: none;
1063+ transition: none;
1064+ }
1065+ .reveal.reveal-init .trace-row .bar,
1066+ .reveal.reveal-init.is-visible .trace-row .bar {
1067+ transform: none;
1068+ transition: none;
1069+ }
1070+}
1071+
1072+/* Hero glow (launch-ui): a soft accent bloom sitting behind the beam
1073+ card, warming the off-white surface without fighting the graph
1074+ paper. Purely decorative, never interactive. */
1075+.hero-glow {
1076+ position: absolute;
1077+ left: 50%;
1078+ top: 54%;
1079+ width: min(720px, 86%);
1080+ height: 340px;
1081+ transform: translate(-50%, -50%);
1082+ background: radial-gradient(
1083+ closest-side,
1084+ color-mix(in srgb, var(--color-accent) 15%, transparent),
1085+ transparent 100%
1086+ );
1087+ filter: blur(36px);
1088+ opacity: 0.5;
1089+ pointer-events: none;
1090+ z-index: 0;
1091+}
1092+
1093+/* Hero announcement badge (launch-ui): a pill above the headline that
1094+ links to the repo, with a slow shine sweep over the label (magicui
1095+ animated-shiny-text, toned down so it stays readable mid-sweep). */
1096+.hero-badge {
1097+ display: inline-flex;
1098+ align-items: center;
1099+ gap: 0.5rem;
1100+ padding: 0.32rem 0.7rem 0.32rem 0.62rem;
1101+ border: 1px solid var(--color-rule);
1102+ border-radius: 999px;
1103+ background: white;
1104+ font-size: 12.5px;
1105+ font-weight: 500;
1106+ letter-spacing: -0.005em;
1107+ box-shadow: 0 1px 2px rgba(13, 13, 16, 0.03);
1108+ transition:
1109+ border-color 0.15s ease,
1110+ box-shadow 0.15s ease;
1111+}
1112+.hero-badge:hover {
1113+ border-color: var(--color-rule-strong);
1114+ box-shadow: 0 1px 3px rgba(13, 13, 16, 0.06);
1115+}
1116+.hero-badge .dot {
1117+ width: 6px;
1118+ height: 6px;
1119+ border-radius: 999px;
1120+ background: var(--color-accent);
1121+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 16%, transparent);
1122+ flex-shrink: 0;
1123+}
1124+.hero-badge .label {
1125+ background: linear-gradient(
1126+ 100deg,
1127+ var(--color-ink-2) 38%,
1128+ var(--color-ink) 50%,
1129+ var(--color-ink-2) 62%
1130+ );
1131+ background-size: 220% 100%;
1132+ -webkit-background-clip: text;
1133+ background-clip: text;
1134+ -webkit-text-fill-color: transparent;
1135+ color: transparent;
1136+ animation: badge-shine 6.5s linear infinite;
1137+}
1138+.hero-badge .arrow {
1139+ color: var(--color-ink-3);
1140+ transition: transform 0.15s ease;
1141+}
1142+.hero-badge:hover .arrow {
1143+ transform: translateX(2px);
1144+}
1145+@keyframes badge-shine {
1146+ to {
1147+ background-position: -220% 0;
1148+ }
1149+}
1150+@media (prefers-reduced-motion: reduce) {
1151+ .hero-badge .label {
1152+ animation: none;
1153+ }
1154+}
1155+
1156+/* Hero CTA: a "For humans / For agents" audience toggle. A lightweight text
1157+ switch (not a heavy segmented control) flips between the human signup path
1158+ and the agent-native copyable setup. */
1159+.hero-cta {
1160+ display: flex;
1161+ flex-direction: column;
1162+ align-items: center;
1163+}
1164+.audience-toggle {
1165+ display: inline-flex;
1166+ align-items: center;
1167+ gap: 0.85rem;
1168+ margin-bottom: 1.15rem;
1169+ font-size: 13.5px;
1170+ font-weight: 500;
1171+}
1172+.audience-toggle button {
1173+ padding: 0;
1174+ background: none;
1175+ border: none;
1176+ font: inherit;
1177+ color: var(--color-ink-3);
1178+ cursor: pointer;
1179+ transition: color 0.15s ease;
1180+}
1181+.audience-toggle button:hover {
1182+ color: var(--color-ink-2);
1183+}
1184+.audience-toggle button[aria-selected="true"] {
1185+ color: var(--color-ink);
1186+ font-weight: 600;
1187+}
1188+.audience-sep {
1189+ width: 1px;
1190+ height: 13px;
1191+ background: var(--color-rule-strong);
1192+}
1193+.audience-pane {
1194+ display: flex;
1195+ flex-wrap: wrap;
1196+ align-items: center;
1197+ justify-content: center;
1198+ gap: 0.75rem;
1199+}
1200+.audience-pane[hidden] {
1201+ display: none;
1202+}
1203+/* The agent "command pill": a rounded, mono, copyable control echoing a
1204+ terminal prompt. Copies the full setup prompt via the .copy-btn pattern. */
1205+.agent-pill {
1206+ display: inline-flex;
1207+ align-items: center;
1208+ gap: 0.6rem;
1209+ border: 1px solid var(--color-rule-strong);
1210+ border-radius: 999px;
1211+ background: var(--color-surface);
1212+ padding: 0.62rem 1rem 0.62rem 1.2rem;
1213+ font-family: var(--font-mono);
1214+ font-size: 13.5px;
1215+ letter-spacing: -0.005em;
1216+ color: var(--color-ink);
1217+ cursor: pointer;
1218+ transition:
1219+ background 0.15s ease,
1220+ border-color 0.15s ease;
1221+}
1222+.agent-pill:hover {
1223+ background: var(--color-surface-2);
1224+ border-color: var(--color-ink-3);
1225+}
1226+
1227+/* Border beam (magicui, restrained): a slow accent arc travels around
1228+ the featured card's border to draw the eye to the recommended path.
1229+ The ring is fixed to the card edges; only the gradient angle spins,
1230+ so the bright arc sweeps the border rather than detaching from it. */
1231+@property --beam-angle {
1232+ syntax: "<angle>";
1233+ initial-value: 0deg;
1234+ inherits: false;
1235+}
1236+.beam-card {
1237+ position: relative;
1238+}
1239+.beam-card::after {
1240+ content: "";
1241+ position: absolute;
1242+ inset: 0;
1243+ border-radius: inherit;
1244+ padding: 1.25px;
1245+ background: conic-gradient(
1246+ from var(--beam-angle),
1247+ transparent 0deg,
1248+ transparent 290deg,
1249+ color-mix(in srgb, var(--color-accent) 75%, transparent) 340deg,
1250+ transparent 360deg
1251+ );
1252+ -webkit-mask:
1253+ linear-gradient(#000 0 0) content-box,
1254+ linear-gradient(#000 0 0);
1255+ -webkit-mask-composite: xor;
1256+ mask-composite: exclude;
1257+ pointer-events: none;
1258+ animation: beam-rotate 7s linear infinite;
1259+}
1260+@keyframes beam-rotate {
1261+ to {
1262+ --beam-angle: 360deg;
1263+ }
1264+}
1265+@media (prefers-reduced-motion: reduce) {
1266+ .beam-card::after {
1267+ animation: none;
1268+ background: conic-gradient(
1269+ from 320deg,
1270+ transparent 0deg,
1271+ transparent 300deg,
1272+ color-mix(in srgb, var(--color-accent) 45%, transparent) 340deg,
1273+ transparent 360deg
1274+ );
1275+ }
1276+}
1277+
1278+/* Featured-path cards: a restrained lift on hover for tactile polish. */
1279+.path-card {
1280+ transition:
1281+ transform 0.2s ease,
1282+ box-shadow 0.2s ease,
1283+ border-color 0.2s ease;
1284+}
1285+.path-card:hover {
1286+ transform: translateY(-3px);
1287+ box-shadow:
1288+ 0 1px 2px rgba(13, 13, 16, 0.03),
1289+ 0 18px 40px -22px rgba(13, 13, 16, 0.18);
1290+}
1291+@media (prefers-reduced-motion: reduce) {
1292+ .path-card:hover {
1293+ transform: none;
1294+ }
1295+}
1296+
1297+/* ══ Context-bloat demo (interactive "no context bloat") ══ */
1298+/* Context-bloat demo: Effect-style layout (complexity gauge + service checklist
1299+ on top, two code windows below). Check a service to connect it; the naive
1300+ prompt balloons (tall + scrollable) while Executor stays at one tool. */
1301+.cbloat {
1302+ display: flex;
1303+ flex-direction: column;
1304+ gap: 2rem;
1305+}
1306+
1307+/* Top band: complexity gauge + service checklist */
1308+.cbloat-top {
1309+ display: grid;
1310+ grid-template-columns: 1fr;
1311+ gap: 2rem;
1312+}
1313+@media (min-width: 880px) {
1314+ .cbloat-top {
1315+ grid-template-columns: 0.82fr 1fr;
1316+ gap: 3rem;
1317+ align-items: start;
1318+ }
1319+}
1320+
1321+.cbloat-gauge__title {
1322+ font-size: 13px;
1323+ font-weight: 600;
1324+ color: var(--color-ink);
1325+}
1326+.cbloat-gauge__sub {
1327+ font-size: 12px;
1328+ color: var(--color-ink-3);
1329+ margin-bottom: 1.4rem;
1330+}
1331+.cbloat-gauge__row {
1332+ margin-bottom: 1.1rem;
1333+}
1334+.cbloat-gauge__line {
1335+ display: flex;
1336+ align-items: center;
1337+ gap: 0.5rem;
1338+ font-size: 12.5px;
1339+ margin-bottom: 0.45rem;
1340+}
1341+.cbloat-dot {
1342+ width: 8px;
1343+ height: 8px;
1344+ border-radius: 2px;
1345+ flex-shrink: 0;
1346+}
1347+.cbloat-dot--naive {
1348+ background: rgba(13, 13, 16, 0.4);
1349+}
1350+.cbloat-dot--exec {
1351+ background: var(--color-accent);
1352+}
1353+.cbloat-gauge__name {
1354+ color: var(--color-ink-2);
1355+ font-weight: 500;
1356+}
1357+.cbloat-gauge__val {
1358+ margin-left: auto;
1359+ font-family: var(--font-mono);
1360+ font-size: 11px;
1361+ color: var(--color-ink-3);
1362+ white-space: nowrap;
1363+}
1364+
1365+.cbloat-checklist {
1366+ display: grid;
1367+ grid-template-columns: repeat(2, minmax(0, 1fr));
1368+ gap: 0.5rem;
1369+}
1370+.cbloat-check {
1371+ display: flex;
1372+ align-items: center;
1373+ gap: 0.6rem;
1374+ padding: 0.58rem 0.72rem;
1375+ border: 1px solid var(--color-rule);
1376+ border-radius: 10px;
1377+ background: white;
1378+ cursor: pointer;
1379+ text-align: left;
1380+ transition:
1381+ border-color 0.15s ease,
1382+ background 0.15s ease;
1383+}
1384+.cbloat-check:hover {
1385+ border-color: var(--color-rule-strong);
1386+ background: var(--color-surface);
1387+}
1388+.cbloat-check[data-on="true"] {
1389+ border-color: rgba(99, 91, 255, 0.4);
1390+ background: color-mix(in srgb, var(--color-accent) 5%, white);
1391+}
1392+.cbloat-check:focus-visible {
1393+ outline: 2px solid var(--color-accent);
1394+ outline-offset: 2px;
1395+}
1396+.cbloat-check__box {
1397+ width: 18px;
1398+ height: 18px;
1399+ border-radius: 6px;
1400+ flex-shrink: 0;
1401+ border: 1.5px solid var(--color-rule-strong);
1402+ display: inline-flex;
1403+ align-items: center;
1404+ justify-content: center;
1405+ color: white;
1406+ transition:
1407+ background 0.15s ease,
1408+ border-color 0.15s ease;
1409+}
1410+.cbloat-check[data-on="true"] .cbloat-check__box {
1411+ background: var(--color-accent);
1412+ border-color: var(--color-accent);
1413+}
1414+.cbloat-check__icon {
1415+ display: inline-flex;
1416+ width: 15px;
1417+ height: 15px;
1418+ flex-shrink: 0;
1419+ color: var(--color-ink-2);
1420+}
1421+.cbloat-check__name {
1422+ flex: 1;
1423+ min-width: 0;
1424+ overflow: hidden;
1425+ text-overflow: ellipsis;
1426+ white-space: nowrap;
1427+ font-size: 13.5px;
1428+ font-weight: 500;
1429+ color: var(--color-ink);
1430+}
1431+.cbloat-check__count {
1432+ font-family: var(--font-mono);
1433+ font-size: 10.5px;
1434+ color: var(--color-ink-3);
1435+ white-space: nowrap;
1436+}
1437+
1438+/* Bottom band: two code windows */
1439+.cbloat-grid {
1440+ display: grid;
1441+ grid-template-columns: 1fr;
1442+ gap: 1.25rem;
1443+}
1444+@media (min-width: 880px) {
1445+ .cbloat-grid {
1446+ grid-template-columns: 1fr 1fr;
1447+ }
1448+}
1449+.cbloat-col {
1450+ display: flex;
1451+ flex-direction: column;
1452+ min-width: 0;
1453+}
1454+.cbloat-col__title {
1455+ text-align: center;
1456+ font-size: 15px;
1457+ font-weight: 600;
1458+ letter-spacing: -0.01em;
1459+ color: var(--color-ink);
1460+ margin-bottom: 0.75rem;
1461+}
1462+.cbloat-panel {
1463+ display: flex;
1464+ flex-direction: column;
1465+ min-width: 0;
1466+}
1467+.cbloat-panel__count {
1468+ margin-left: auto;
1469+ font-family: var(--font-mono);
1470+ font-size: 11.5px;
1471+ color: var(--color-ink-3);
1472+ white-space: nowrap;
1473+}
1474+.cbloat-panel--naive .cbloat-num {
1475+ color: var(--color-ink);
1476+ font-weight: 600;
1477+}
1478+.cbloat-panel--executor .cbloat-num {
1479+ color: var(--color-accent);
1480+ font-weight: 600;
1481+}
1482+
1483+.cbloat-bar {
1484+ height: 6px;
1485+ width: 100%;
1486+ background: var(--color-rule);
1487+ border-radius: 3px;
1488+ overflow: hidden;
1489+}
1490+.cbloat-bar__fill {
1491+ height: 100%;
1492+ min-width: 3px;
1493+ border-radius: 3px;
1494+ transition: width 0.45s cubic-bezier(0.22, 1, 0.36, 1);
1495+}
1496+.cbloat-bar--naive .cbloat-bar__fill {
1497+ background: rgba(13, 13, 16, 0.4);
1498+}
1499+.cbloat-bar--executor .cbloat-bar__fill {
1500+ background: var(--color-accent);
1501+}
1502+
1503+.cbloat-body {
1504+ font-size: 12.5px;
1505+ line-height: 1.7;
1506+ white-space: pre-wrap;
1507+ word-break: break-word;
1508+}
1509+.cbloat-body--scroll {
1510+ height: 440px;
1511+ overflow-y: auto;
1512+}
1513+.cbloat-line {
1514+ display: block;
1515+}
1516+
1517+@media (prefers-reduced-motion: reduce) {
1518+ .cbloat-bar__fill {
1519+ transition: none;
1520+ }
1521+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t