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
182 </Row>182 </Row>
183 <Row label="Codex" reverse={false} labelStyle={labelStyle}>183 <Row label="Codex" reverse={false} labelStyle={labelStyle}>
184 <Node ref={agent3} style={nodeStyle}>184 <Node ref={agent3} style={nodeStyle}>
185 <Icons.openai />185 <Icons.codex />
186 </Node>186 </Node>
187 </Row>187 </Row>
188 </div>188 </div>
@@ -337,35 +337,15 @@ function Row({ · 148 unmodified lines
337 );337 );
338}338}
339 339
340const logoIcon = (src: string) => () => (
341 <img src={src} alt="" className="w-full h-full" style={{ objectFit: "contain" }} loading="lazy" />
342);
343
340const Icons = {344const Icons = {
341 sentry: () => (345 sentry: logoIcon("/logos/sentry.svg"),
342 <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#362D59]">346 github: logoIcon("/logos/github.svg"),
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" />347 linear: logoIcon("/logos/linear.svg"),
344 </svg>348 claude: logoIcon("/logos/claude.svg"),
345 ),349 cursor: logoIcon("/logos/cursor.svg"),
346 github: () => (350 codex: logoIcon("/logos/codex.svg"),
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 ),
371};351};
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
7import 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
18type 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).
30const 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.
164const EXECUTOR_BASE = 980; // fixed workflow + rules preamble, served once
165const EXECUTOR_PER = 16; // one connection-prefix line per integration
166const NAIVE_MAX = INTEGRATIONS.reduce((s, i) => s + i.naiveTok, 0); // bar scale
167
168const fmt = (n: number) => n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
169
170function 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. */
183function 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.
222function 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
235function 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
253function 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
267export 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 @@
1---1---
2import Layout from "../layouts/Layout.astro";2import Layout from "../layouts/Layout.astro";
3import { AnimatedBeamDemo } from "../components/animated-beam-demo";3import { AnimatedBeamDemo } from "../components/animated-beam-demo";
4import { ContextBloatDemo } from "../components/context-bloat-demo";
4import { SelfHostContactModal } from "../components/self-host-contact-modal";5import { SelfHostContactModal } from "../components/self-host-contact-modal";
5 6
7const GH = "https://github.com/RhysSullivan/executor";
8
6// Copied to the clipboard by the "Set up with your agent" hero CTA. A visitor9// Copied to the clipboard by the "Set up with your agent" hero CTA. A visitor
7// pastes it into their coding agent (Claude, Cursor, ...) and the agent picks10// pastes it into their coding agent (Claude, Cursor, ...) and the agent picks
8// the right form of Executor, installs it, connects over MCP, and gets a first11// 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
43 46
44<Layout>47<Layout>
45 <main class="bg-soft min-h-dvh relative antialiased">48 <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 */}
47 <div50 <div
48 class="pattern-bg pattern-graph"51 class="pattern-bg pattern-graph"
49 style="position: absolute; top: 0; left: 0; right: 0; height: 100vh; z-index: 1; pointer-events: none;"52 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
55 </div>58 </div>
56 59
57 <div class="relative z-10">60 <div class="relative z-10">
58 {/* NAV */}61 {/* ─── NAV ─── */}
59 <nav>62 <nav>
60 <div63 <div
61 class="max-w-[1200px] mx-auto px-6 sm:px-10 h-14 flex items-center justify-between"64 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
68 </a>71 </a>
69 <div class="flex items-center gap-5">72 <div class="flex items-center gap-5">
70 <a73 <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}
72 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors"85 class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors"
73 >GitHub</a86 >GitHub</a
74 >87 >
75 <a href="/cloud" class="btn-primary">Try Cloud →</a>88 <a href="#install" class="btn-primary">Get started →</a>
76 </div>89 </div>
77 </div>90 </div>
78 </nav>91 </nav>
79 92
80 {/* HERO — centered, tight */}93 {/* ─── HERO ─── */}
81 <section class="relative">94 <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">
83 <div class="max-w-[920px] mx-auto text-center">97 <div class="max-w-[920px] mx-auto text-center">
84 <h198 <h1
85 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"99 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
91 <AnimatedBeamDemo client:load variant="stripe" />105 <AnimatedBeamDemo client:load variant="stripe" />
92 </div>106 </div>
93 </div>107 </div>
94 <p108 {/* ── CTA: For humans / For agents ── */}
95 class="rise-3 text-[18px] leading-[1.55] text-ink-2 mx-auto max-w-[44ch] mb-9 text-pretty"109 <div class="rise-4 hero-cta" data-audience="agents">
96 >110 <div class="audience-toggle" role="tablist" aria-label="Choose your path">
97 One gateway. Every tool. Every team.111 <button type="button" role="tab" data-audience-set="humans" aria-selected="false">
98 </p>112 For humans
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
119 </button>113 </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">
120 <button128 <button
121 type="button"129 type="button"
122 role="tab"130 class="agent-pill copy-btn"
123 aria-selected="false"131 data-copy={setupPrompt}
124 data-tab="local"132 data-event="marketing_setup_prompt_copied"
125 >133 >
126 Local134 <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>
127 </button>152 </button>
153 <a href="https://executor.sh/docs" class="btn-secondary">Read docs</a>
128 </div>154 </div>
155 </div>
129 156
130 {/* Cloud pane */}157 <a
131 <div158 href="https://www.ycombinator.com"
132 class="pane flex-col items-center gap-4"159 target="_blank"
133 data-pane="cloud"160 rel="noopener noreferrer"
134 role="tabpanel"161 class="rise-4 mt-10 inline-flex"
135 aria-labelledby="tab-cloud"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
136 >231 >
137 <p class="pane-desc">232 <h3>One tool shape</h3>
138 Built for teams. Plug into any agent via MCP.233 <p>
139 </p>234 MCP, OpenAPI, GraphQL, or a custom integration. Under the hood
140 <a href="/cloud" class="btn-primary btn-primary--hero"235 they all become a tool name, an input schema, and an output
141 >Try Cloud →</a236 schema.
142 >237 </p>
143 </div>238 </div>
144 239
145 {/* Local pane — auto-detected download */}240 {/* Code-mode */}
146 <div241 <div class="cap-card">
147 class="pane flex-col items-center gap-4"242 <span class="cap-icon"
148 data-pane="local"243 ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
149 role="tabpanel"244 stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
150 aria-labelledby="tab-local"245 ><polyline points="4 17 10 11 4 5"></polyline><line
151 data-download246 x1="12"
247 y1="19"
248 x2="20"
249 y2="19"></line></svg
250 ></span
152 >251 >
153 <p class="pane-desc">252 <h3>Call it any way</h3>
154 Everything runs on your machine locally.253 <p>
155 </p>254 Today it is a code-mode MCP. It could just as well be the
156 <div class="flex flex-wrap items-center justify-center gap-3">255 Executor CLI, a one-off script, a gen-UI dashboard, or a reusable
157 <a256 workflow. Same tools, every surface.
158 data-os="mac-arm64"257 </p>
159 href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-arm64.dmg"258 </div>
160 class="btn-primary btn-primary--hero"259
161 >260 {/* Trace every call (coming soon) */}
162 Download for Mac (Apple Silicon)261 <div class="cap-card cap-card--soon">
163 </a>262 <span class="cap-soon-badge">Coming soon</span>
164 <a263 <span class="cap-icon"
165 data-os="mac-x64"264 ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
166 href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-x64.dmg"265 stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
167 class="btn-primary btn-primary--hero"266 ><path d="M3 12h4l3 8 4-16 3 8h4"></path></svg
168 >267 ></span
169 Download for Mac (Intel)268 >
170 </a>269 <h3>Trace every call</h3>
171 <a270 <p>
172 data-os="win-x64"271 One place to see every run and tool call. Audit any
173 href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-win-x64.exe"272 decision after the fact.
174 class="btn-primary btn-primary--hero"273 </p>
175 >274 </div>
176 Download for Windows275
177 </a>276 {/* Teams */}
178 <a277 <div class="cap-card">
179 data-os="linux-x64"278 <span class="cap-icon"
180 href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-linux-x86_64.AppImage"279 ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
181 class="btn-primary btn-primary--hero"280 stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
182 >281 ><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"
183 Download for Linux282 ></path><circle cx="9" cy="7" r="4"></circle><path
184 </a>283 d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"
185 284 ></path></svg
186 <details285 ></span
187 class="download-other hidden"286 >
188 data-download-other287 <h3>Set up once, whole team has it</h3>
189 >288 <p class="mb-1">
190 <summary>Other platforms ▾</summary>289 Per-user credentials and shared ones. No onboarding ritual, no
191 <div290 toggling MCPs on and off mid-task.
192 class="download-other-list"291 </p>
193 data-download-other-list292 <div class="cap-visual matrix">
194 >293 <div class="matrix-head">
195 </div>294 <span></span><span>GH</span><span>Sntry</span><span>Linr</span
196 </details>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>
197 </div>320 </div>
198 </div>321 </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>
199 340
200 {/* Distribution-agnostic: hand the whole setup to your coding341 {/* Sandboxed */}
201 agent. Copies the prompt above; the agent picks the form,342 <div class="cap-card">
202 installs, and connects. */}343 <span class="cap-icon"
203 <button344 ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
204 type="button"345 stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
205 class="btn-secondary copy-btn"346 ><rect x="3" y="3" width="18" height="18" rx="2"
206 data-copy={setupPrompt}347 ></rect><path d="m7 8 3 3-3 3M13 14h4"></path></svg
207 data-event="marketing_setup_prompt_copied"348 ></span
208 >349 >
209 Set up with your agent350 <h3>Sandboxed execution</h3>
210 <span class="copy-icons" aria-hidden="true">351 <p>
211 <svg352 Tool calls run in an isolated JavaScript sandbox. Secrets are
212 class="copy-icon-copy"353 injected host-side at call time and never enter the sandbox
213 xmlns="http://www.w3.org/2000/svg"354 heap, so the agent and model never see a raw token.
214 width="14"355 </p>
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>
243 </div>356 </div>
244 </div>357 </div>
245 </div>358 </div>
246 </section>359 </section>
247 360
248 {/* ─── FOUNDER NOTE ─── */}361 {/* ─── FOUNDER NOTE (condensed) ─── */}
249 <section class="py-20 sm:py-28 border-t border-rule">362 <section class="py-20 sm:py-28 border-t border-rule">
250 <div class="max-w-[680px] mx-auto px-6 sm:px-10">363 <div class="reveal max-w-[680px] mx-auto px-6 sm:px-10">
251 <h2364 <div class="eyebrow mb-6">Why we built it</div>
252 class="text-[clamp(2rem,3.6vw,2.6rem)] font-semibold tracking-[-0.02em] leading-[1.08] text-ink mb-10 text-balance"365 <div class="space-y-5 text-[16.5px] leading-[1.65] text-ink-2 text-pretty">
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 >
260 <p>366 <p>
261 Executor is a tool/source discovery and execution layer for367 Your agent should be able to reach your company's resources in a
262 agent-callable tools.368 way that isn't scary. Most setups make you choose between locked
369 down and useless, or wide open and risky.
263 </p>370 </p>
264 <p>371 <p>
265 Executor doesn't care what you add. MCP, OpenAPI, GraphQL, custom372 Executor doesn't care what you add. Once a tool is in that one
266 sources, under the hood they all become a tool name, an input373 shape, a name and two schemas, you can call it however you want and
267 schema, and an output schema. Once they're in that shape, you can374 the same guardrails apply everywhere. That is the whole idea: make
268 call them however you want. Today that's implemented as a375 the safe path the easy path.
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.
276 </p>376 </p>
377 </div>
378 </div>
379 </section>
277 380
278 <h3381 {/* ─── CHOOSE YOUR PATH ─── */}
279 class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"382 <section id="install" class="py-24 sm:py-32 border-t border-rule">
280 >383 <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10">
281 Teams384 <div class="mb-14 mx-auto text-center max-w-2xl">
282 </h3>385 <div class="eyebrow mb-4">Get started</div>
283 <p>386 <h2 class="section-title mx-auto max-w-[20ch]">
284 Set your sources up once and your whole team has them. It supports387 Pick your <span class="serif-italic">path</span>.
285 per-user credentials and shared ones. No more onboarding ritual,388 </h2>
286 no more toggling MCPs on and off mid-task. Your agents should be389 </div>
287 able to reach your company's resources in a way that isn't scary.
288 </p>
289 390
290 <h3391 <div class="plus-grid grid md:grid-cols-3 gap-6 max-w-[1000px] mx-auto">
291 class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"392 <span class="plus-mark" style="top:-7px;left:-7px"></span>
292 >393 <span class="plus-mark" style="top:-7px;right:-7px"></span>
293 Sandboxing394 <span class="plus-mark" style="bottom:-7px;left:-7px"></span>
294 </h3>395 <span class="plus-mark" style="bottom:-7px;right:-7px"></span>
295 <p>
296 Tool calls run in a JavaScript sandbox. The agent has access to
297 secrets.
298 </p>
299 396
300 <h3397 {/* Cloud (recommended: fastest start) */}
301 class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"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)"
302 >401 >
303 Destructive actions402 <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3">
304 </h3>403 Cloud
305 <p>404 </h3>
306 Executor preserves the semantics of whatever it imported from: GET405 <span class="path-chip mb-4 self-start">executor.sh/cloud</span>
307 vs DELETE for OpenAPI, <code406 <p class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty flex-1">
308 class="font-mono text-[14px] text-ink bg-surface-2 border border-rule px-1.5 py-0.5 rounded"407 Hosted Executor. Auth, sync, policies, and your whole team online
309 >destructiveHint</code408 in five minutes. Free tier to start.
310 > for MCP, mutations for GraphQL. That tells the agent what it can auto-run409 </p>
311 and what should pull you back into the loop, so it can work autonomously410 <a href="/cloud" class="btn-primary self-start">Try Cloud →</a>
312 without taking you out of it.411 <p class="mt-3 min-h-[1.25rem] text-[13px] leading-[1.5] text-ink-3">
313 </p>412 <SelfHostContactModal client:load variant="inline" />
413 </p>
414 </div>
314 415
315 <p class="pt-5">416 {/* Desktop (native app, local) */}
316 It's all open source, built on the same SDK we publish to npm.417 <div class="surface-card path-card p-8 flex flex-col">
317 There's two ways to try it, either the local version via the418 <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3">
318 `executor` cli, or the cloud version.419 Desktop
319 </p>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>
320 458
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>
322 </div>479 </div>
323 </div>480 </div>
324 </section>481 </section>
325 482
326 {/* ─── GET STARTED ─── */}483 {/* ─── FAQ ─── */}
327 <section id="install" class="py-24 sm:py-32 border-t border-rule">484 <section class="py-20 sm:py-28 border-t border-rule">
328 <div class="max-w-[1200px] mx-auto px-6 sm:px-10">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">
329 <div class="mb-14 mx-auto text-center max-w-2xl">585 <div class="mb-14 mx-auto text-center max-w-2xl">
330 <div586 <div class="eyebrow mb-4">Pricing</div>
331 class="font-mono text-[12px] uppercase tracking-[0.18em] text-ink-3 mb-4"587 <h2 class="section-title mx-auto max-w-[20ch]">
332 >588 Start free, pay as you <span class="serif-italic">run</span>.
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>.
339 </h2>589 </h2>
340 </div>590 </div>
341 591
342 <div592 <div class="grid md:grid-cols-3 gap-6 max-w-[1000px] mx-auto items-stretch">
343 class="grid md:grid-cols-2 gap-6 max-w-[760px] mx-auto"593 {/* Free */}
344 >594 <div class="surface-card path-card p-8 flex flex-col">
345 {/* Cloud */}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 */}
346 <div630 <div
347 class="surface-card p-8 flex flex-col"631 class="surface-card path-card beam-card p-8 flex flex-col"
348 style="border-color: rgba(99,91,255,0.25)"632 style="border-color: rgba(99,91,255,0.25)"
349 >633 >
350 <div class="flex items-center gap-2 mb-4">634 <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
353 style="color: var(--color-accent)">Recommended</span637 style="color: var(--color-accent)">Recommended</span
354 >638 >
355 </div>639 </div>
356 <h3640 <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1">
357 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"641 Team
358 >
359 Cloud
360 </h3>642 </h3>
361 <p643 <p class="text-[14px] text-ink-3 mb-5">For growing organizations</p>
362 class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty"644 <div class="flex items-baseline gap-1.5 mb-6">
363 >645 <span
364 Hosted Executor. Auth, sync, policies, and the whole team646 class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums"
365 online in five minutes. Free tier to start.647 >$49</span
366 </p>648 >
367 <p class="mb-6 text-[13.5px] leading-[1.55] text-ink-3 flex-1">649 <span class="text-[13.5px] text-ink-3">/ org / month</span>
368 <SelfHostContactModal client:load variant="inline" />650 </div>
369 </p>651 <a href="/cloud" class="btn-primary self-start mb-7">Start Team →</a>
370 <a href="/cloud" class="btn-primary self-start">Try Cloud →</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>
371 </div>668 </div>
372 669
373 {/* Desktop */}670 {/* Enterprise */}
374 <div class="surface-card p-8 flex flex-col">671 <div class="surface-card path-card p-8 flex flex-col">
375 <div class="flex items-center gap-2 mb-4">672 <div class="flex items-center gap-2 mb-4">
376 <span673 <span class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3"
377 class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3"674 >Custom needs</span
378 >Open source</span
379 >675 >
380 </div>676 </div>
381 <h3677 <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1">
382 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"678 Enterprise
383 >
384 Desktop
385 </h3>679 </h3>
386 <p680 <p class="text-[14px] text-ink-3 mb-5">For orgs with custom needs</p>
387 class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1"681 <div class="flex items-baseline gap-1.5 mb-6">
388 >682 <span
389 A native app for Mac, Windows, and Linux. Everything stays683 class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums"
390 on your machine — sources, secrets, sessions. MIT licensed.684 >Custom</span
391 </p>685 >
392 <button686 </div>
393 type="button"687 <a
394 data-jump-to-local688 href="mailto:rhys@executor.sh?subject=Executor%20Enterprise%20inquiry"
395 class="btn-primary self-start"689 class="btn-primary self-start mb-7">Contact us →</a
396 >690 >
397 Download →691 <p class="text-[12.5px] font-medium text-ink mb-2.5">Everything in Team, plus</p>
398 </button>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>
399 </div>708 </div>
400 </div>709 </div>
401 </div>710 </div>
402 </section>711 </section>
403 712
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
404 {/* ─── FOOTER ─── */}739 {/* ─── FOOTER ─── */}
405 <footer class="border-t border-rule py-10">740 <footer class="border-t border-rule py-10">
406 <div741 <div
@@ -414,13 +749,11 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 7 unmodified lines
414 >749 >
415 </div>750 </div>
416 <div class="flex items-center gap-6 text-[13px] text-ink-2">751 <div class="flex items-center gap-6 text-[13px] text-ink-2">
417 <a752 <a href="https://executor.sh/docs" class="hover:text-ink transition-colors"
418 href="https://github.com/RhysSullivan/executor"753 >Docs</a
419 class="hover:text-ink transition-colors">GitHub</a
420 >
421 <a href="/privacy" class="hover:text-ink transition-colors"
422 >Privacy</a
423 >754 >
755 <a href={GH} class="hover:text-ink transition-colors">GitHub</a>
756 <a href="/privacy" class="hover:text-ink transition-colors">Privacy</a>
424 <a href="/terms" class="hover:text-ink transition-colors">Terms</a>757 <a href="/terms" class="hover:text-ink transition-colors">Terms</a>
425 </div>758 </div>
426 </div>759 </div>
@@ -430,98 +763,93 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 3 unmodified lines
430</Layout>763</Layout>
431 764
432<script>765<script>
433 document766 // Copy buttons. Use the async Clipboard API in secure contexts and fall back
434 .querySelectorAll<HTMLButtonElement>("button[data-copy]")767 // to a hidden textarea + execCommand otherwise, so copy (and the copied-state
435 .forEach((btn) => {768 // animation) also works over plain HTTP, e.g. a LAN / tailnet dev host where
436 let timer: ReturnType<typeof setTimeout> | null = null;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 };
437 793
438 btn.addEventListener("click", async () => {794 for (const btn of document.querySelectorAll<HTMLButtonElement>(
439 const text = btn.dataset.copy ?? "";795 "button[data-copy]",
440 try {796 )) {
441 await navigator.clipboard.writeText(text);797 let timer: ReturnType<typeof setTimeout> | null = null;
442 btn.dataset.copied = "true";798 btn.addEventListener("click", async () => {
443 const event = btn.dataset.event;799 const ok = await copyToClipboard(btn.dataset.copy ?? "");
444 if (event) {800 btn.dataset.copied = ok ? "true" : "false";
445 window.dispatchEvent(801 const event = btn.dataset.event;
446 new CustomEvent("analytics:capture", {802 if (ok && event) {
447 detail: { event, properties: { surface: "marketing_hero" } },803 window.dispatchEvent(
448 }),804 new CustomEvent("analytics:capture", {
449 );805 detail: { event, properties: { surface: "marketing_hero" } },
450 }806 }),
451 } catch {807 );
452 btn.dataset.copied = "false";808 }
453 }809 if (timer) clearTimeout(timer);
454 if (timer) clearTimeout(timer);810 timer = setTimeout(() => {
455 timer = setTimeout(() => {811 delete btn.dataset.copied;
456 delete btn.dataset.copied;812 }, 1500);
457 }, 1500);
458 });
459 });813 });
814 }
460</script>815</script>
461 816
462<script>817<script>
463 // ── Hero tab toggle: Try in Cloud / Try locally ────────────────────818 // ── Hero "For humans / For agents" audience toggle ─────────────────
464 // Drives the sliding pill indicator + pane visibility via a single819 const heroCta = document.querySelector<HTMLElement>(".hero-cta");
465 // `data-active` attribute on the wrapper. Default "cloud" so no-JS820 if (heroCta) {
466 // visitors still see the recommended path.821 const tabs = Array.from(
467 const heroToggle = document.querySelector<HTMLElement>(".hero-toggle");822 heroCta.querySelectorAll<HTMLButtonElement>("[data-audience-set]"),
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 },
489 );823 );
490 if (updateUrl) syncTabToUrl(tab);824 const panes = Array.from(
491 };825 heroCta.querySelectorAll<HTMLElement>("[data-audience-pane]"),
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 },
502 );826 );
503 window.addEventListener("popstate", () => {827 const setAudience = (aud: string) => {
504 const next = new URLSearchParams(window.location.search).get(TAB_PARAM);828 heroCta.dataset.audience = aud;
505 setActiveTab(isHeroTab(next) ? next : "cloud", false);829 for (const t of tabs) {
506 });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 }
507 }844 }
845</script>
508 846
509 // Desktop card "Download →" CTA jumps to the hero, switches to Local,847<script>
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
520 // ── Auto-detect download platform ──────────────────────────────────848 // ── Auto-detect download platform ──────────────────────────────────
521 // Shows ONE primary button matching the visitor's OS in the "Try849 // Shows ONE primary button matching the visitor's OS in the Desktop
522 // locally" pane; tucks the others behind an "Other platforms"850 // card; tucks the others behind an "Other platforms" popover. No-JS /
523 // popover. No-JS / unrecognised UA: all four buttons visible (still851 // unrecognised UA: all four buttons visible (still functional, just
524 // functional, just less polished).852 // less polished).
525 type DownloadOs = "mac-arm64" | "mac-x64" | "win-x64" | "linux-x64";853 type DownloadOs = "mac-arm64" | "mac-x64" | "win-x64" | "linux-x64";
526 854
527 type UserAgentData = {855 type UserAgentData = {
@@ -594,3 +922,31 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 66 unmodified lines
594 other.classList.remove("hidden");922 other.classList.remove("hidden");
595 })();923 })();
596</script>924</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
66 opacity: 0.08;66 opacity: 0.08;
67}67}
68 68
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 */
70.pattern-fade-overlay {70.pattern-fade-overlay {
71 position: absolute;71 position: absolute;
72 inset: 0;72 inset: 0;
@@ -75,7 +75,7 @@ body { · 2 unmodified lines
75 z-index: 0;75 z-index: 0;
76}76}
77 77
78/* Italic serif accent — pairs with sharp Geist display */78/* Italic serif accent: pairs with sharp Geist display */
79.serif-italic {79.serif-italic {
80 font-family: var(--font-serif);80 font-family: var(--font-serif);
81 font-style: italic;81 font-style: italic;
@@ -111,7 +111,7 @@ body { · 29 unmodified lines
111 animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both;111 animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both;
112}112}
113 113
114/* Hairline-bordered surface for the beam panel — minimal elevation */114/* Hairline-bordered surface for the beam panel: minimal elevation */
115.surface-card {115.surface-card {
116 background: white;116 background: white;
117 border: 1px solid var(--color-rule);117 border: 1px solid var(--color-rule);
@@ -136,7 +136,7 @@ body { · 18 unmodified lines
136 background: #1f1f24;136 background: #1f1f24;
137}137}
138 138
139/* Light, bordered counterpart to .btn-primary — a real secondary CTA. Sized to139/* Light, bordered counterpart to .btn-primary, a real secondary CTA. Sized to
140 match .btn-primary--hero so it balances under the hero's primary button. */140 match .btn-primary--hero so it balances under the hero's primary button. */
141.btn-secondary {141.btn-secondary {
142 display: inline-flex;142 display: inline-flex;
@@ -209,7 +209,7 @@ body { · 66 unmodified lines
209}209}
210 210
211/* ──────────────────────────────────────────────────────────────211/* ──────────────────────────────────────────────────────────────
212 Hero toggle — segmented "Try in Cloud / Try locally" control.212 Hero toggle: segmented "Try in Cloud / Try locally" control.
213 A sliding white pill animates between two equal-width tabs.213 A sliding white pill animates between two equal-width tabs.
214 The active pane is revealed below; inactive panes hide.214 The active pane is revealed below; inactive panes hide.
215 ─────────────────────────────────────────────────────────── */215 ─────────────────────────────────────────────────────────── */
@@ -271,7 +271,7 @@ body { · 55 unmodified lines
271 display: flex;271 display: flex;
272}272}
273 273
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,
275 tight max-width so two lines wrap naturally on narrow widths. */275 tight max-width so two lines wrap naturally on narrow widths. */
276.pane-desc {276.pane-desc {
277 font-size: 14.5px;277 font-size: 14.5px;
@@ -284,7 +284,7 @@ body { · 6 unmodified lines
284 margin: 0;284 margin: 0;
285}285}
286 286
287/* Subtle metadata strip under each pane's CTA — uppercase mono dots */287/* Subtle metadata strip under each pane's CTA: uppercase mono dots */
288.pane-meta {288.pane-meta {
289 display: inline-flex;289 display: inline-flex;
290 flex-wrap: wrap;290 flex-wrap: wrap;
@@ -334,18 +334,13 @@ body { · 43 unmodified lines
334 user-select: none;334 user-select: none;
335 font-size: 13px;335 font-size: 13px;
336 color: var(--color-ink-3);336 color: var(--color-ink-3);
337 padding: 0.4rem 0.6rem;337 transition: color 0.15s ease;
338 border-radius: 8px;
339 transition:
340 color 0.15s ease,
341 background 0.15s ease;
342}338}
343.download-other > summary::-webkit-details-marker {339.download-other > summary::-webkit-details-marker {
344 display: none;340 display: none;
345}341}
346.download-other > summary:hover {342.download-other > summary:hover {
347 color: var(--color-ink-2);343 color: var(--color-ink-2);
348 background: var(--color-surface-2);
349}344}
350.download-other[open] > summary {345.download-other[open] > summary {
351 color: var(--color-ink);346 color: var(--color-ink);
@@ -384,14 +379,14 @@ body { · 32 unmodified lines
384 background: var(--color-surface-2);379 background: var(--color-surface-2);
385}380}
386 381
387/* Larger primary button for the hero pane — keeps btn-primary base sizing382/* Larger primary button for the hero pane: keeps btn-primary base sizing
388 for nav/cards but bumps the hero's CTA to a more confident scale */383 for nav/cards but bumps the hero's CTA to a more confident scale */
389.btn-primary--hero {384.btn-primary--hero {
390 padding: 0.7rem 1.25rem;385 padding: 0.7rem 1.25rem;
391 font-size: 14.5px;386 font-size: 14.5px;
392}387}
393 388
394/* Accent-colored inline link — for secondary callouts inside card bodies */389/* Accent-colored inline link: for secondary callouts inside card bodies */
395.link-accent {390.link-accent {
396 color: var(--color-accent);391 color: var(--color-accent);
397 font-weight: 500;392 font-weight: 500;
@@ -470,3 +465,1057 @@ body { · 72 unmodified lines
470 color: var(--color-ink);465 color: var(--color-ink);
471 font-weight: 600;466 font-weight: 600;
472}467}
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