* 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
Unified · Split
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -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> | |
| @@ -182,7 +182,7 @@ export function AnimatedBeamDemo({ · 181 unmodified lines | ||
| 182 | 182 | </Row> |
| 183 | 183 | <Row label="Codex" reverse={false} labelStyle={labelStyle}> |
| 184 | 184 | <Node ref={agent3} style={nodeStyle}> |
| 185 | − <Icons.openai /> | |
| 185 | + <Icons.codex /> | |
| 186 | 186 | </Node> |
| 187 | 187 | </Row> |
| 188 | 188 | </div> |
| @@ -337,35 +337,15 @@ function Row({ · 148 unmodified lines | ||
| 337 | 337 | ); |
| 338 | 338 | } |
| 339 | 339 | |
| 340 | +const logoIcon = (src: string) => () => ( | |
| 341 | + <img src={src} alt="" className="w-full h-full" style={{ objectFit: "contain" }} loading="lazy" /> | |
| 342 | +); | |
| 343 | + | |
| 340 | 344 | const Icons = { |
| 341 | − sentry: () => ( | |
| 342 | − <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#362D59]"> | |
| 343 | − <path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z" /> | |
| 344 | − </svg> | |
| 345 | − ), | |
| 346 | − github: () => ( | |
| 347 | − <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]"> | |
| 348 | − <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" /> | |
| 349 | − </svg> | |
| 350 | − ), | |
| 351 | − linear: () => ( | |
| 352 | − <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#5E6AD2]"> | |
| 353 | − <path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z" /> | |
| 354 | − </svg> | |
| 355 | − ), | |
| 356 | − claude: () => ( | |
| 357 | − <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#D97757]"> | |
| 358 | − <path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z" /> | |
| 359 | − </svg> | |
| 360 | − ), | |
| 361 | − cursor: () => ( | |
| 362 | − <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]"> | |
| 363 | − <path d="M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23" /> | |
| 364 | − </svg> | |
| 365 | − ), | |
| 366 | − openai: () => ( | |
| 367 | − <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]"> | |
| 368 | − <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" /> | |
| 369 | − </svg> | |
| 370 | − ), | |
| 345 | + sentry: logoIcon("/logos/sentry.svg"), | |
| 346 | + github: logoIcon("/logos/github.svg"), | |
| 347 | + linear: logoIcon("/logos/linear.svg"), | |
| 348 | + claude: logoIcon("/logos/claude.svg"), | |
| 349 | + cursor: logoIcon("/logos/cursor.svg"), | |
| 350 | + codex: logoIcon("/logos/codex.svg"), | |
| 371 | 351 | }; |
| @@ -0,0 +1,438 @@ | ||
| 1 | +"use client"; | |
| 2 | + | |
| 3 | +/* eslint-disable react/forbid-elements -- this marketing demo's service toggles | |
| 4 | + are bespoke styled controls (icon + name + tool count + checkbox); the product | |
| 5 | + design-system <Button> does not model that layout. */ | |
| 6 | + | |
| 7 | +import React, { useEffect, useRef, useState } from "react"; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * Interactive "no context bloat" demo, laid out like Effect's "Production-grade | |
| 11 | + * TypeScript" section: a complexity gauge + service checklist on top, then two | |
| 12 | + * code windows below. Check services to connect them. The naive side grows a | |
| 13 | + * system prompt that lists every tool name, ballooning into the thousands (tall | |
| 14 | + * + scrollable); the Executor side stays at one `execute` tool with a short, | |
| 15 | + * fixed description. Figures are illustrative; the shape is accurate. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +type Integration = { | |
| 19 | + readonly slug: string; | |
| 20 | + readonly name: string; | |
| 21 | + readonly tools: number; | |
| 22 | + readonly naiveTok: number; | |
| 23 | + readonly toolNames: ReadonlyArray<string>; | |
| 24 | + readonly summary: string; | |
| 25 | +}; | |
| 26 | + | |
| 27 | +// Each integration imports its whole API surface as tools (OpenAPI ops, MCP | |
| 28 | +// tools, GraphQL fields), so a handful already stacks into the thousands. | |
| 29 | +// naiveTok ~= tools * 170 (one tool definition with its JSON schema). | |
| 30 | +const INTEGRATIONS: ReadonlyArray<Integration> = [ | |
| 31 | + { | |
| 32 | + slug: "github", | |
| 33 | + name: "GitHub", | |
| 34 | + tools: 720, | |
| 35 | + naiveTok: 122400, | |
| 36 | + toolNames: [ | |
| 37 | + "createIssue", | |
| 38 | + "listPullRequests", | |
| 39 | + "mergePullRequest", | |
| 40 | + "createRelease", | |
| 41 | + "addLabels", | |
| 42 | + "createBranch", | |
| 43 | + "getCommit", | |
| 44 | + ], | |
| 45 | + summary: "Production GitHub", | |
| 46 | + }, | |
| 47 | + { | |
| 48 | + slug: "stripe", | |
| 49 | + name: "Stripe", | |
| 50 | + tools: 510, | |
| 51 | + naiveTok: 86700, | |
| 52 | + toolNames: [ | |
| 53 | + "createCharge", | |
| 54 | + "createCustomer", | |
| 55 | + "createRefund", | |
| 56 | + "listInvoices", | |
| 57 | + "createSubscription", | |
| 58 | + "capturePaymentIntent", | |
| 59 | + "listPayouts", | |
| 60 | + ], | |
| 61 | + summary: "Live Stripe account", | |
| 62 | + }, | |
| 63 | + { | |
| 64 | + slug: "jira", | |
| 65 | + name: "Jira", | |
| 66 | + tools: 240, | |
| 67 | + naiveTok: 40800, | |
| 68 | + toolNames: [ | |
| 69 | + "createIssue", | |
| 70 | + "transitionIssue", | |
| 71 | + "addComment", | |
| 72 | + "assignIssue", | |
| 73 | + "listSprints", | |
| 74 | + "createProject", | |
| 75 | + "searchIssues", | |
| 76 | + ], | |
| 77 | + summary: "Team Jira", | |
| 78 | + }, | |
| 79 | + { | |
| 80 | + slug: "sentry", | |
| 81 | + name: "Sentry", | |
| 82 | + tools: 170, | |
| 83 | + naiveTok: 28900, | |
| 84 | + toolNames: [ | |
| 85 | + "listIssues", | |
| 86 | + "resolveIssue", | |
| 87 | + "listEvents", | |
| 88 | + "getProject", | |
| 89 | + "muteIssue", | |
| 90 | + "createRelease", | |
| 91 | + "listAlerts", | |
| 92 | + ], | |
| 93 | + summary: "Production Sentry", | |
| 94 | + }, | |
| 95 | + { | |
| 96 | + slug: "linear", | |
| 97 | + name: "Linear", | |
| 98 | + tools: 130, | |
| 99 | + naiveTok: 22100, | |
| 100 | + toolNames: [ | |
| 101 | + "createIssue", | |
| 102 | + "updateIssue", | |
| 103 | + "listProjects", | |
| 104 | + "createComment", | |
| 105 | + "archiveIssue", | |
| 106 | + "listTeams", | |
| 107 | + "createLabel", | |
| 108 | + ], | |
| 109 | + summary: "Linear workspace", | |
| 110 | + }, | |
| 111 | + { | |
| 112 | + slug: "gmail", | |
| 113 | + name: "Gmail", | |
| 114 | + tools: 95, | |
| 115 | + naiveTok: 16150, | |
| 116 | + toolNames: [ | |
| 117 | + "sendMessage", | |
| 118 | + "listThreads", | |
| 119 | + "createDraft", | |
| 120 | + "addLabel", | |
| 121 | + "trashMessage", | |
| 122 | + "listMessages", | |
| 123 | + "modifyMessage", | |
| 124 | + ], | |
| 125 | + summary: "Support inbox", | |
| 126 | + }, | |
| 127 | + { | |
| 128 | + slug: "notion", | |
| 129 | + name: "Notion", | |
| 130 | + tools: 80, | |
| 131 | + naiveTok: 13600, | |
| 132 | + toolNames: [ | |
| 133 | + "queryDatabase", | |
| 134 | + "createPage", | |
| 135 | + "updateBlock", | |
| 136 | + "appendChildren", | |
| 137 | + "search", | |
| 138 | + "retrievePage", | |
| 139 | + "listUsers", | |
| 140 | + ], | |
| 141 | + summary: "Internal Notion", | |
| 142 | + }, | |
| 143 | + { | |
| 144 | + slug: "slack", | |
| 145 | + name: "Slack", | |
| 146 | + tools: 70, | |
| 147 | + naiveTok: 11900, | |
| 148 | + toolNames: [ | |
| 149 | + "postMessage", | |
| 150 | + "listChannels", | |
| 151 | + "createChannel", | |
| 152 | + "inviteToChannel", | |
| 153 | + "uploadFile", | |
| 154 | + "listUsers", | |
| 155 | + "setTopic", | |
| 156 | + ], | |
| 157 | + summary: "Team Slack", | |
| 158 | + }, | |
| 159 | +]; | |
| 160 | + | |
| 161 | +// The execute tool's description is a fixed preamble (workflow + rules) plus one | |
| 162 | +// short prefix line per connected integration. It stays flat as you add | |
| 163 | +// integrations, no matter how many tools each one carries. | |
| 164 | +const EXECUTOR_BASE = 980; // fixed workflow + rules preamble, served once | |
| 165 | +const EXECUTOR_PER = 16; // one connection-prefix line per integration | |
| 166 | +const NAIVE_MAX = INTEGRATIONS.reduce((s, i) => s + i.naiveTok, 0); // bar scale | |
| 167 | + | |
| 168 | +const fmt = (n: number) => n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | |
| 169 | + | |
| 170 | +function usePrefersReducedMotion(): boolean { | |
| 171 | + const [reduced, setReduced] = useState(false); | |
| 172 | + useEffect(() => { | |
| 173 | + const mq = window.matchMedia("(prefers-reduced-motion: reduce)"); | |
| 174 | + const sync = () => setReduced(mq.matches); | |
| 175 | + sync(); | |
| 176 | + mq.addEventListener("change", sync); | |
| 177 | + return () => mq.removeEventListener("change", sync); | |
| 178 | + }, []); | |
| 179 | + return reduced; | |
| 180 | +} | |
| 181 | + | |
| 182 | +/** Eases a displayed integer toward `target` with requestAnimationFrame. */ | |
| 183 | +function useAnimatedNumber(target: number): number { | |
| 184 | + const reduced = usePrefersReducedMotion(); | |
| 185 | + const [display, setDisplay] = useState(target); | |
| 186 | + const fromRef = useRef(target); | |
| 187 | + const rafRef = useRef<number | null>(null); | |
| 188 | + | |
| 189 | + useEffect(() => { | |
| 190 | + if (reduced) { | |
| 191 | + setDisplay(target); | |
| 192 | + fromRef.current = target; | |
| 193 | + return; | |
| 194 | + } | |
| 195 | + const from = fromRef.current; | |
| 196 | + if (from === target) return; | |
| 197 | + const start = performance.now(); | |
| 198 | + const dur = 450; | |
| 199 | + const tick = (now: number) => { | |
| 200 | + const t = Math.min(1, (now - start) / dur); | |
| 201 | + const eased = 1 - Math.pow(1 - t, 3); | |
| 202 | + setDisplay(Math.round(from + (target - from) * eased)); | |
| 203 | + if (t < 1) { | |
| 204 | + rafRef.current = requestAnimationFrame(tick); | |
| 205 | + } else { | |
| 206 | + fromRef.current = target; | |
| 207 | + } | |
| 208 | + }; | |
| 209 | + rafRef.current = requestAnimationFrame(tick); | |
| 210 | + return () => { | |
| 211 | + if (rafRef.current) cancelAnimationFrame(rafRef.current); | |
| 212 | + fromRef.current = target; | |
| 213 | + }; | |
| 214 | + }, [target, reduced]); | |
| 215 | + | |
| 216 | + return display; | |
| 217 | +} | |
| 218 | + | |
| 219 | +// Brand logos live in /public/logos as full-color svgl.app assets, rendered as | |
| 220 | +// <img> so each keeps its own colors. svgl has no Jira icon, so /logos/jira.svg | |
| 221 | +// is the Jira mark in Jira blue. | |
| 222 | +function IntegrationIcon({ slug }: { readonly slug: string }) { | |
| 223 | + return ( | |
| 224 | + <img | |
| 225 | + src={`/logos/${slug}.svg`} | |
| 226 | + alt="" | |
| 227 | + width={15} | |
| 228 | + height={15} | |
| 229 | + loading="lazy" | |
| 230 | + style={{ objectFit: "contain" }} | |
| 231 | + /> | |
| 232 | + ); | |
| 233 | +} | |
| 234 | + | |
| 235 | +function CheckMark() { | |
| 236 | + return ( | |
| 237 | + <svg | |
| 238 | + viewBox="0 0 24 24" | |
| 239 | + fill="none" | |
| 240 | + stroke="currentColor" | |
| 241 | + strokeWidth={3} | |
| 242 | + strokeLinecap="round" | |
| 243 | + strokeLinejoin="round" | |
| 244 | + width="11" | |
| 245 | + height="11" | |
| 246 | + aria-hidden="true" | |
| 247 | + > | |
| 248 | + <path d="M20 6 9 17l-5-5" /> | |
| 249 | + </svg> | |
| 250 | + ); | |
| 251 | +} | |
| 252 | + | |
| 253 | +function TokenBar({ | |
| 254 | + pct, | |
| 255 | + variant, | |
| 256 | +}: { | |
| 257 | + readonly pct: number; | |
| 258 | + readonly variant: "naive" | "executor"; | |
| 259 | +}) { | |
| 260 | + return ( | |
| 261 | + <div className={`cbloat-bar cbloat-bar--${variant}`} aria-hidden="true"> | |
| 262 | + <div className="cbloat-bar__fill" style={{ width: `${Math.min(100, Math.max(2, pct))}%` }} /> | |
| 263 | + </div> | |
| 264 | + ); | |
| 265 | +} | |
| 266 | + | |
| 267 | +export function ContextBloatDemo() { | |
| 268 | + const [active, setActive] = useState<ReadonlyArray<string>>([ | |
| 269 | + "github", | |
| 270 | + "stripe", | |
| 271 | + "jira", | |
| 272 | + "sentry", | |
| 273 | + ]); | |
| 274 | + const isOn = (slug: string) => active.includes(slug); | |
| 275 | + const toggle = (slug: string) => | |
| 276 | + setActive((prev) => (prev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug])); | |
| 277 | + | |
| 278 | + const activeIntegrations = INTEGRATIONS.filter((i) => isOn(i.slug)); | |
| 279 | + const naiveTok = activeIntegrations.reduce((s, i) => s + i.naiveTok, 0); | |
| 280 | + const naiveTools = activeIntegrations.reduce((s, i) => s + i.tools, 0); | |
| 281 | + const executorTok = EXECUTOR_BASE + active.length * EXECUTOR_PER; | |
| 282 | + | |
| 283 | + const naiveDisplay = useAnimatedNumber(naiveTok); | |
| 284 | + const executorDisplay = useAnimatedNumber(executorTok); | |
| 285 | + const naiveToolsDisplay = useAnimatedNumber(naiveTools); | |
| 286 | + | |
| 287 | + const naivePct = (naiveTok / NAIVE_MAX) * 100; | |
| 288 | + const executorPct = (executorTok / NAIVE_MAX) * 100; | |
| 289 | + | |
| 290 | + return ( | |
| 291 | + <div className="cbloat"> | |
| 292 | + <p className="sr-only" aria-live="polite"> | |
| 293 | + Without Executor: {fmt(naiveTools)} tools, about {fmt(naiveTok)} tokens. With Executor: 1 | |
| 294 | + tool, about {fmt(executorTok)} tokens. | |
| 295 | + </p> | |
| 296 | + | |
| 297 | + <div className="cbloat-top"> | |
| 298 | + {/* Complexity gauge */} | |
| 299 | + <div className="cbloat-gauge"> | |
| 300 | + <div className="cbloat-gauge__title">Context window</div> | |
| 301 | + <div className="cbloat-gauge__sub">Lower is better</div> | |
| 302 | + <div className="cbloat-gauge__row"> | |
| 303 | + <div className="cbloat-gauge__line"> | |
| 304 | + <span className="cbloat-dot cbloat-dot--naive" /> | |
| 305 | + <span className="cbloat-gauge__name">Without Executor</span> | |
| 306 | + <span className="cbloat-gauge__val"> | |
| 307 | + {fmt(naiveToolsDisplay)} tools · ~{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 · ~{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 · ~ | |
| 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 · ~<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 | +} | |
| @@ -1,8 +1,11 @@ | ||
| 1 | 1 | --- |
| 2 | 2 | import Layout from "../layouts/Layout.astro"; |
| 3 | 3 | import { AnimatedBeamDemo } from "../components/animated-beam-demo"; |
| 4 | +import { ContextBloatDemo } from "../components/context-bloat-demo"; | |
| 4 | 5 | import { SelfHostContactModal } from "../components/self-host-contact-modal"; |
| 5 | 6 | |
| 7 | +const GH = "https://github.com/RhysSullivan/executor"; | |
| 8 | + | |
| 6 | 9 | // Copied to the clipboard by the "Set up with your agent" hero CTA. A visitor |
| 7 | 10 | // pastes it into their coding agent (Claude, Cursor, ...) and the agent picks |
| 8 | 11 | // 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 | 47 | <Layout> |
| 45 | 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 | 50 | <div |
| 48 | 51 | class="pattern-bg pattern-graph" |
| 49 | 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 | 58 | </div> |
| 56 | 59 | |
| 57 | 60 | <div class="relative z-10"> |
| 58 | − {/* NAV */} | |
| 61 | + {/* ─── NAV ─── */} | |
| 59 | 62 | <nav> |
| 60 | 63 | <div |
| 61 | 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 | 71 | </a> |
| 69 | 72 | <div class="flex items-center gap-5"> |
| 70 | 73 | <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 | 85 | class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors" |
| 73 | 86 | >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 | 89 | </div> |
| 77 | 90 | </div> |
| 78 | 91 | </nav> |
| 79 | 92 | |
| 80 | − {/* HERO — centered, tight */} | |
| 93 | + {/* ─── HERO ─── */} | |
| 81 | 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 | 97 | <div class="max-w-[920px] mx-auto text-center"> |
| 84 | 98 | <h1 |
| 85 | 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 | 105 | <AnimatedBeamDemo client:load variant="stripe" /> |
| 92 | 106 | </div> |
| 93 | 107 | </div> |
| 94 | − <p | |
| 95 | − class="rise-3 text-[18px] leading-[1.55] text-ink-2 mx-auto max-w-[44ch] mb-9 text-pretty" | |
| 96 | − > | |
| 97 | − One gateway. Every tool. Every team. | |
| 98 | − </p> | |
| 99 | − | |
| 100 | − {/* ── Toggle: Try in Cloud / Try locally ── */} | |
| 101 | − <div | |
| 102 | − class="rise-4 hero-toggle flex flex-col items-center gap-7" | |
| 103 | − data-active="cloud" | |
| 104 | − > | |
| 105 | − <div | |
| 106 | − class="tab-toggle" | |
| 107 | − data-active="cloud" | |
| 108 | − role="tablist" | |
| 109 | − aria-label="Choose how to try Executor" | |
| 110 | − > | |
| 111 | − <span class="tab-indicator" aria-hidden="true"></span> | |
| 112 | − <button | |
| 113 | − type="button" | |
| 114 | − role="tab" | |
| 115 | − aria-selected="true" | |
| 116 | − data-tab="cloud" | |
| 117 | − > | |
| 118 | − Cloud | |
| 108 | + {/* ── CTA: For humans / For agents ── */} | |
| 109 | + <div class="rise-4 hero-cta" data-audience="agents"> | |
| 110 | + <div class="audience-toggle" role="tablist" aria-label="Choose your path"> | |
| 111 | + <button type="button" role="tab" data-audience-set="humans" aria-selected="false"> | |
| 112 | + For humans | |
| 119 | 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 | 128 | <button |
| 121 | 129 | type="button" |
| 122 | − role="tab" | |
| 123 | − aria-selected="false" | |
| 124 | − data-tab="local" | |
| 130 | + class="agent-pill copy-btn" | |
| 131 | + data-copy={setupPrompt} | |
| 132 | + data-event="marketing_setup_prompt_copied" | |
| 125 | 133 | > |
| 126 | − Local | |
| 134 | + <span class="agent-pill__label">Set up with your agent</span> | |
| 135 | + <span class="copy-icons" aria-hidden="true"> | |
| 136 | + <svg | |
| 137 | + class="copy-icon-copy" xmlns="http://www.w3.org/2000/svg" | |
| 138 | + width="14" height="14" viewBox="0 0 24 24" fill="none" | |
| 139 | + stroke="currentColor" stroke-width="2" stroke-linecap="round" | |
| 140 | + stroke-linejoin="round" | |
| 141 | + ><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path | |
| 142 | + d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg | |
| 143 | + > | |
| 144 | + <svg | |
| 145 | + class="copy-icon-check" xmlns="http://www.w3.org/2000/svg" | |
| 146 | + width="14" height="14" viewBox="0 0 24 24" fill="none" | |
| 147 | + stroke="currentColor" stroke-width="2" stroke-linecap="round" | |
| 148 | + stroke-linejoin="round" | |
| 149 | + ><path d="M20 6 9 17l-5-5"></path></svg | |
| 150 | + > | |
| 151 | + </span> | |
| 127 | 152 | </button> |
| 153 | + <a href="https://executor.sh/docs" class="btn-secondary">Read docs</a> | |
| 128 | 154 | </div> |
| 155 | + </div> | |
| 129 | 156 | |
| 130 | − {/* Cloud pane */} | |
| 131 | − <div | |
| 132 | − class="pane flex-col items-center gap-4" | |
| 133 | − data-pane="cloud" | |
| 134 | − role="tabpanel" | |
| 135 | − aria-labelledby="tab-cloud" | |
| 157 | + <a | |
| 158 | + href="https://www.ycombinator.com" | |
| 159 | + target="_blank" | |
| 160 | + rel="noopener noreferrer" | |
| 161 | + class="rise-4 mt-10 inline-flex" | |
| 162 | + aria-label="Backed by Y Combinator" | |
| 163 | + > | |
| 164 | + <img | |
| 165 | + src="/yc-backed-by.svg" | |
| 166 | + alt="Backed by Y Combinator" | |
| 167 | + width="140" | |
| 168 | + height="52" | |
| 169 | + /> | |
| 170 | + </a> | |
| 171 | + </div> | |
| 172 | + </div> | |
| 173 | + </section> | |
| 174 | + | |
| 175 | + {/* ─── WEDGE ─── */} | |
| 176 | + <section class="py-20 sm:py-28 border-t border-rule"> | |
| 177 | + <div class="reveal max-w-[840px] mx-auto px-6 sm:px-10"> | |
| 178 | + <p | |
| 179 | + class="text-[clamp(1.5rem,3.4vw,2.2rem)] font-medium tracking-[-0.02em] leading-[1.28] text-ink text-balance" | |
| 180 | + > | |
| 181 | + Wiring tools to agents is fiddly, per-client, and easy to get wrong. | |
| 182 | + Executor makes every tool, from any protocol, look the | |
| 183 | + <span class="serif-italic">same</span>: one name, one input | |
| 184 | + schema, one output schema, so any agent can call any of them | |
| 185 | + the same way. | |
| 186 | + </p> | |
| 187 | + </div> | |
| 188 | + </section> | |
| 189 | + | |
| 190 | + {/* ─── NO CONTEXT BLOAT (interactive) ─── */} | |
| 191 | + <section class="py-20 sm:py-28 border-t border-rule"> | |
| 192 | + <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10"> | |
| 193 | + <div class="text-center max-w-2xl mx-auto mb-12"> | |
| 194 | + <div class="eyebrow mb-4">Context efficiency</div> | |
| 195 | + <h2 class="section-title mx-auto max-w-[20ch] mb-5"> | |
| 196 | + Thousands of tools, no <span class="serif-italic">bloat</span>. | |
| 197 | + </h2> | |
| 198 | + <p class="section-sub mx-auto max-w-[58ch]"> | |
| 199 | + Connect everything you use and Executor still shows the model a | |
| 200 | + single tool. It searches your catalog and loads a tool's schema | |
| 201 | + only when the code actually calls it, so the prompt never | |
| 202 | + balloons. | |
| 203 | + </p> | |
| 204 | + </div> | |
| 205 | + | |
| 206 | + <ContextBloatDemo client:load /> | |
| 207 | + </div> | |
| 208 | + </section> | |
| 209 | + | |
| 210 | + {/* ─── CAPABILITIES ─── */} | |
| 211 | + <section class="py-20 sm:py-28 border-t border-rule"> | |
| 212 | + <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10"> | |
| 213 | + <div class="mb-12 max-w-[34ch]"> | |
| 214 | + <div class="eyebrow mb-4">What you get</div> | |
| 215 | + <h2 class="section-title"> | |
| 216 | + The model reasons. Executor handles the | |
| 217 | + <span class="serif-italic">rest</span>. | |
| 218 | + </h2> | |
| 219 | + </div> | |
| 220 | + | |
| 221 | + <div class="cap-grid"> | |
| 222 | + {/* One tool shape */} | |
| 223 | + <div class="cap-card"> | |
| 224 | + <span class="cap-icon" | |
| 225 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 226 | + stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" | |
| 227 | + ><path d="m12 2 9 5-9 5-9-5 9-5Z"></path><path | |
| 228 | + d="m3 12 9 5 9-5"></path><path d="m3 17 9 5 9-5" | |
| 229 | + ></path></svg | |
| 230 | + ></span | |
| 136 | 231 | > |
| 137 | − <p class="pane-desc"> | |
| 138 | − Built for teams. Plug into any agent via MCP. | |
| 139 | − </p> | |
| 140 | − <a href="/cloud" class="btn-primary btn-primary--hero" | |
| 141 | − >Try Cloud →</a | |
| 142 | − > | |
| 143 | − </div> | |
| 232 | + <h3>One tool shape</h3> | |
| 233 | + <p> | |
| 234 | + MCP, OpenAPI, GraphQL, or a custom integration. Under the hood | |
| 235 | + they all become a tool name, an input schema, and an output | |
| 236 | + schema. | |
| 237 | + </p> | |
| 238 | + </div> | |
| 144 | 239 | |
| 145 | − {/* Local pane — auto-detected download */} | |
| 146 | − <div | |
| 147 | − class="pane flex-col items-center gap-4" | |
| 148 | − data-pane="local" | |
| 149 | − role="tabpanel" | |
| 150 | − aria-labelledby="tab-local" | |
| 151 | − data-download | |
| 240 | + {/* Code-mode */} | |
| 241 | + <div class="cap-card"> | |
| 242 | + <span class="cap-icon" | |
| 243 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 244 | + stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" | |
| 245 | + ><polyline points="4 17 10 11 4 5"></polyline><line | |
| 246 | + x1="12" | |
| 247 | + y1="19" | |
| 248 | + x2="20" | |
| 249 | + y2="19"></line></svg | |
| 250 | + ></span | |
| 152 | 251 | > |
| 153 | − <p class="pane-desc"> | |
| 154 | − Everything runs on your machine locally. | |
| 155 | − </p> | |
| 156 | − <div class="flex flex-wrap items-center justify-center gap-3"> | |
| 157 | − <a | |
| 158 | − data-os="mac-arm64" | |
| 159 | − href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-arm64.dmg" | |
| 160 | − class="btn-primary btn-primary--hero" | |
| 161 | − > | |
| 162 | − Download for Mac (Apple Silicon) | |
| 163 | − </a> | |
| 164 | − <a | |
| 165 | − data-os="mac-x64" | |
| 166 | − href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-x64.dmg" | |
| 167 | − class="btn-primary btn-primary--hero" | |
| 168 | − > | |
| 169 | − Download for Mac (Intel) | |
| 170 | − </a> | |
| 171 | − <a | |
| 172 | − data-os="win-x64" | |
| 173 | − href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-win-x64.exe" | |
| 174 | − class="btn-primary btn-primary--hero" | |
| 175 | − > | |
| 176 | − Download for Windows | |
| 177 | − </a> | |
| 178 | − <a | |
| 179 | − data-os="linux-x64" | |
| 180 | − href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-linux-x86_64.AppImage" | |
| 181 | − class="btn-primary btn-primary--hero" | |
| 182 | − > | |
| 183 | − Download for Linux | |
| 184 | − </a> | |
| 185 | − | |
| 186 | − <details | |
| 187 | − class="download-other hidden" | |
| 188 | − data-download-other | |
| 189 | − > | |
| 190 | − <summary>Other platforms ▾</summary> | |
| 191 | − <div | |
| 192 | − class="download-other-list" | |
| 193 | − data-download-other-list | |
| 194 | − > | |
| 195 | − </div> | |
| 196 | − </details> | |
| 252 | + <h3>Call it any way</h3> | |
| 253 | + <p> | |
| 254 | + Today it is a code-mode MCP. It could just as well be the | |
| 255 | + Executor CLI, a one-off script, a gen-UI dashboard, or a reusable | |
| 256 | + workflow. Same tools, every surface. | |
| 257 | + </p> | |
| 258 | + </div> | |
| 259 | + | |
| 260 | + {/* Trace every call (coming soon) */} | |
| 261 | + <div class="cap-card cap-card--soon"> | |
| 262 | + <span class="cap-soon-badge">Coming soon</span> | |
| 263 | + <span class="cap-icon" | |
| 264 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 265 | + stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" | |
| 266 | + ><path d="M3 12h4l3 8 4-16 3 8h4"></path></svg | |
| 267 | + ></span | |
| 268 | + > | |
| 269 | + <h3>Trace every call</h3> | |
| 270 | + <p> | |
| 271 | + One place to see every run and tool call. Audit any | |
| 272 | + decision after the fact. | |
| 273 | + </p> | |
| 274 | + </div> | |
| 275 | + | |
| 276 | + {/* Teams */} | |
| 277 | + <div class="cap-card"> | |
| 278 | + <span class="cap-icon" | |
| 279 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 280 | + stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" | |
| 281 | + ><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" | |
| 282 | + ></path><circle cx="9" cy="7" r="4"></circle><path | |
| 283 | + d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" | |
| 284 | + ></path></svg | |
| 285 | + ></span | |
| 286 | + > | |
| 287 | + <h3>Set up once, whole team has it</h3> | |
| 288 | + <p class="mb-1"> | |
| 289 | + Per-user credentials and shared ones. No onboarding ritual, no | |
| 290 | + toggling MCPs on and off mid-task. | |
| 291 | + </p> | |
| 292 | + <div class="cap-visual matrix"> | |
| 293 | + <div class="matrix-head"> | |
| 294 | + <span></span><span>GH</span><span>Sntry</span><span>Linr</span | |
| 295 | + ><span>Slack</span><span>Notn</span> | |
| 296 | + </div> | |
| 297 | + <div class="matrix-row"> | |
| 298 | + <span class="who">Aria</span> | |
| 299 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 300 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 301 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 302 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 303 | + <span class="cell"><i class="stat stat-warn"></i></span> | |
| 304 | + </div> | |
| 305 | + <div class="matrix-row"> | |
| 306 | + <span class="who">Noah</span> | |
| 307 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 308 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 309 | + <span class="cell"><i class="stat stat-off"></i></span> | |
| 310 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 311 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 312 | + </div> | |
| 313 | + <div class="matrix-row"> | |
| 314 | + <span class="who">Mina</span> | |
| 315 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 316 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 317 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 318 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 319 | + <span class="cell"><i class="stat stat-ok"></i></span> | |
| 197 | 320 | </div> |
| 198 | 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 coding | |
| 201 | − agent. Copies the prompt above; the agent picks the form, | |
| 202 | − installs, and connects. */} | |
| 203 | − <button | |
| 204 | − type="button" | |
| 205 | − class="btn-secondary copy-btn" | |
| 206 | − data-copy={setupPrompt} | |
| 207 | − data-event="marketing_setup_prompt_copied" | |
| 341 | + {/* Sandboxed */} | |
| 342 | + <div class="cap-card"> | |
| 343 | + <span class="cap-icon" | |
| 344 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 345 | + stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" | |
| 346 | + ><rect x="3" y="3" width="18" height="18" rx="2" | |
| 347 | + ></rect><path d="m7 8 3 3-3 3M13 14h4"></path></svg | |
| 348 | + ></span | |
| 208 | 349 | > |
| 209 | − Set up with your agent | |
| 210 | − <span class="copy-icons" aria-hidden="true"> | |
| 211 | − <svg | |
| 212 | − class="copy-icon-copy" | |
| 213 | − xmlns="http://www.w3.org/2000/svg" | |
| 214 | − width="14" | |
| 215 | − height="14" | |
| 216 | − viewBox="0 0 24 24" | |
| 217 | − fill="none" | |
| 218 | − stroke="currentColor" | |
| 219 | − stroke-width="2" | |
| 220 | − stroke-linecap="round" | |
| 221 | − stroke-linejoin="round" | |
| 222 | − > | |
| 223 | − <rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect> | |
| 224 | − <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" | |
| 225 | − ></path> | |
| 226 | − </svg> | |
| 227 | − <svg | |
| 228 | − class="copy-icon-check" | |
| 229 | − xmlns="http://www.w3.org/2000/svg" | |
| 230 | − width="14" | |
| 231 | − height="14" | |
| 232 | − viewBox="0 0 24 24" | |
| 233 | − fill="none" | |
| 234 | − stroke="currentColor" | |
| 235 | − stroke-width="2" | |
| 236 | − stroke-linecap="round" | |
| 237 | − stroke-linejoin="round" | |
| 238 | − > | |
| 239 | − <path d="M20 6 9 17l-5-5"></path> | |
| 240 | − </svg> | |
| 241 | − </span> | |
| 242 | − </button> | |
| 350 | + <h3>Sandboxed execution</h3> | |
| 351 | + <p> | |
| 352 | + Tool calls run in an isolated JavaScript sandbox. Secrets are | |
| 353 | + injected host-side at call time and never enter the sandbox | |
| 354 | + heap, so the agent and model never see a raw token. | |
| 355 | + </p> | |
| 243 | 356 | </div> |
| 244 | 357 | </div> |
| 245 | 358 | </div> |
| 246 | 359 | </section> |
| 247 | 360 | |
| 248 | − {/* ─── FOUNDER NOTE ─── */} | |
| 361 | + {/* ─── FOUNDER NOTE (condensed) ─── */} | |
| 249 | 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"> | |
| 251 | − <h2 | |
| 252 | − class="text-[clamp(2rem,3.6vw,2.6rem)] font-semibold tracking-[-0.02em] leading-[1.08] text-ink mb-10 text-balance" | |
| 253 | − > | |
| 254 | − What is <span class="serif-italic">Executor</span>? | |
| 255 | − </h2> | |
| 256 | − | |
| 257 | − <div | |
| 258 | − class="space-y-5 text-[16.5px] leading-[1.65] text-ink-2 text-pretty" | |
| 259 | − > | |
| 363 | + <div class="reveal max-w-[680px] mx-auto px-6 sm:px-10"> | |
| 364 | + <div class="eyebrow mb-6">Why we built it</div> | |
| 365 | + <div class="space-y-5 text-[16.5px] leading-[1.65] text-ink-2 text-pretty"> | |
| 260 | 366 | <p> |
| 261 | − Executor is a tool/source discovery and execution layer for | |
| 262 | − agent-callable tools. | |
| 367 | + Your agent should be able to reach your company's resources in a | |
| 368 | + way that isn't scary. Most setups make you choose between locked | |
| 369 | + down and useless, or wide open and risky. | |
| 263 | 370 | </p> |
| 264 | 371 | <p> |
| 265 | − Executor doesn't care what you add. MCP, OpenAPI, GraphQL, custom | |
| 266 | − sources, under the hood they all become a tool name, an input | |
| 267 | − schema, and an output schema. Once they're in that shape, you can | |
| 268 | − call them however you want. Today that's implemented as a | |
| 269 | − code-mode MCP, but it could just as well be the Executor CLI, or a | |
| 270 | − native client you drop in. | |
| 271 | − </p> | |
| 272 | − <p> | |
| 273 | − Because every tool looks the same, your agent can call them from | |
| 274 | − gen-UI dashboards it writes on the fly, call them as one off | |
| 275 | − scripts in a chat, or use them in reusable workflows. | |
| 372 | + Executor doesn't care what you add. Once a tool is in that one | |
| 373 | + shape, a name and two schemas, you can call it however you want and | |
| 374 | + the same guardrails apply everywhere. That is the whole idea: make | |
| 375 | + the safe path the easy path. | |
| 276 | 376 | </p> |
| 377 | + </div> | |
| 378 | + </div> | |
| 379 | + </section> | |
| 277 | 380 | |
| 278 | − <h3 | |
| 279 | − class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5" | |
| 280 | − > | |
| 281 | − Teams | |
| 282 | − </h3> | |
| 283 | − <p> | |
| 284 | − Set your sources up once and your whole team has them. It supports | |
| 285 | − per-user credentials and shared ones. No more onboarding ritual, | |
| 286 | − no more toggling MCPs on and off mid-task. Your agents should be | |
| 287 | − able to reach your company's resources in a way that isn't scary. | |
| 288 | − </p> | |
| 381 | + {/* ─── CHOOSE YOUR PATH ─── */} | |
| 382 | + <section id="install" class="py-24 sm:py-32 border-t border-rule"> | |
| 383 | + <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10"> | |
| 384 | + <div class="mb-14 mx-auto text-center max-w-2xl"> | |
| 385 | + <div class="eyebrow mb-4">Get started</div> | |
| 386 | + <h2 class="section-title mx-auto max-w-[20ch]"> | |
| 387 | + Pick your <span class="serif-italic">path</span>. | |
| 388 | + </h2> | |
| 389 | + </div> | |
| 289 | 390 | |
| 290 | − <h3 | |
| 291 | − class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5" | |
| 292 | − > | |
| 293 | − Sandboxing | |
| 294 | − </h3> | |
| 295 | − <p> | |
| 296 | − Tool calls run in a JavaScript sandbox. The agent has access to | |
| 297 | − secrets. | |
| 298 | − </p> | |
| 391 | + <div class="plus-grid grid md:grid-cols-3 gap-6 max-w-[1000px] mx-auto"> | |
| 392 | + <span class="plus-mark" style="top:-7px;left:-7px"></span> | |
| 393 | + <span class="plus-mark" style="top:-7px;right:-7px"></span> | |
| 394 | + <span class="plus-mark" style="bottom:-7px;left:-7px"></span> | |
| 395 | + <span class="plus-mark" style="bottom:-7px;right:-7px"></span> | |
| 299 | 396 | |
| 300 | − <h3 | |
| 301 | − class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5" | |
| 397 | + {/* Cloud (recommended: fastest start) */} | |
| 398 | + <div | |
| 399 | + class="surface-card path-card beam-card p-8 flex flex-col" | |
| 400 | + style="border-color: rgba(99,91,255,0.25)" | |
| 302 | 401 | > |
| 303 | − Destructive actions | |
| 304 | − </h3> | |
| 305 | − <p> | |
| 306 | − Executor preserves the semantics of whatever it imported from: GET | |
| 307 | − vs DELETE for OpenAPI, <code | |
| 308 | − class="font-mono text-[14px] text-ink bg-surface-2 border border-rule px-1.5 py-0.5 rounded" | |
| 309 | − >destructiveHint</code | |
| 310 | − > for MCP, mutations for GraphQL. That tells the agent what it can auto-run | |
| 311 | − and what should pull you back into the loop, so it can work autonomously | |
| 312 | − without taking you out of it. | |
| 313 | − </p> | |
| 402 | + <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"> | |
| 403 | + Cloud | |
| 404 | + </h3> | |
| 405 | + <span class="path-chip mb-4 self-start">executor.sh/cloud</span> | |
| 406 | + <p class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty flex-1"> | |
| 407 | + Hosted Executor. Auth, sync, policies, and your whole team online | |
| 408 | + in five minutes. Free tier to start. | |
| 409 | + </p> | |
| 410 | + <a href="/cloud" class="btn-primary self-start">Try Cloud →</a> | |
| 411 | + <p class="mt-3 min-h-[1.25rem] text-[13px] leading-[1.5] text-ink-3"> | |
| 412 | + <SelfHostContactModal client:load variant="inline" /> | |
| 413 | + </p> | |
| 414 | + </div> | |
| 314 | 415 | |
| 315 | − <p class="pt-5"> | |
| 316 | − It's all open source, built on the same SDK we publish to npm. | |
| 317 | − There's two ways to try it, either the local version via the | |
| 318 | − `executor` cli, or the cloud version. | |
| 319 | − </p> | |
| 416 | + {/* Desktop (native app, local) */} | |
| 417 | + <div class="surface-card path-card p-8 flex flex-col"> | |
| 418 | + <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"> | |
| 419 | + Desktop | |
| 420 | + </h3> | |
| 421 | + <span class="path-chip mb-4 self-start">Mac · Windows · Linux</span> | |
| 422 | + <p class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1"> | |
| 423 | + A native app that runs entirely on your machine. Your | |
| 424 | + integrations, credentials, and sessions never leave the device. | |
| 425 | + MIT licensed. | |
| 426 | + </p> | |
| 427 | + <div class="flex flex-col gap-3 self-stretch" data-download> | |
| 428 | + <a | |
| 429 | + data-os="mac-arm64" | |
| 430 | + href={`${GH}/releases/latest/download/executor-desktop-mac-arm64.dmg`} | |
| 431 | + class="btn-primary self-start" | |
| 432 | + >Download for Mac</a | |
| 433 | + > | |
| 434 | + <a | |
| 435 | + data-os="mac-x64" | |
| 436 | + href={`${GH}/releases/latest/download/executor-desktop-mac-x64.dmg`} | |
| 437 | + class="btn-primary self-start" | |
| 438 | + >Download for Mac (Intel)</a | |
| 439 | + > | |
| 440 | + <a | |
| 441 | + data-os="win-x64" | |
| 442 | + href={`${GH}/releases/latest/download/executor-desktop-win-x64.exe`} | |
| 443 | + class="btn-primary self-start" | |
| 444 | + >Download for Windows</a | |
| 445 | + > | |
| 446 | + <a | |
| 447 | + data-os="linux-x64" | |
| 448 | + href={`${GH}/releases/latest/download/executor-desktop-linux-x86_64.AppImage`} | |
| 449 | + class="btn-primary self-start" | |
| 450 | + >Download for Linux</a | |
| 451 | + > | |
| 452 | + <details class="download-other hidden min-h-[1.25rem]" data-download-other> | |
| 453 | + <summary>Other platforms ▾</summary> | |
| 454 | + <div class="download-other-list" data-download-other-list></div> | |
| 455 | + </details> | |
| 456 | + </div> | |
| 457 | + </div> | |
| 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 | 479 | </div> |
| 323 | 480 | </div> |
| 324 | 481 | </section> |
| 325 | 482 | |
| 326 | − {/* ─── GET STARTED ─── */} | |
| 327 | − <section id="install" class="py-24 sm:py-32 border-t border-rule"> | |
| 328 | − <div class="max-w-[1200px] mx-auto px-6 sm:px-10"> | |
| 483 | + {/* ─── FAQ ─── */} | |
| 484 | + <section class="py-20 sm:py-28 border-t border-rule"> | |
| 485 | + <div class="reveal max-w-[760px] mx-auto px-6 sm:px-10"> | |
| 486 | + <div class="mb-10"> | |
| 487 | + <h2 class="section-title">FAQ</h2> | |
| 488 | + </div> | |
| 489 | + | |
| 490 | + <div> | |
| 491 | + <details class="faq-item"> | |
| 492 | + <summary> | |
| 493 | + Where does my code run, and what touches my credentials? | |
| 494 | + <span class="faq-mark" | |
| 495 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 496 | + stroke-width="2" stroke-linecap="round" | |
| 497 | + ><path d="M12 5v14M5 12h14"></path></svg | |
| 498 | + ></span | |
| 499 | + > | |
| 500 | + </summary> | |
| 501 | + <div class="faq-body"> | |
| 502 | + Tool calls run in an isolated JavaScript sandbox. Credentials are | |
| 503 | + resolved host-side at call time and injected into the outbound | |
| 504 | + request only. They never enter the sandbox heap, the code your | |
| 505 | + agent wrote, the agent, or the model. | |
| 506 | + </div> | |
| 507 | + </details> | |
| 508 | + | |
| 509 | + <details class="faq-item"> | |
| 510 | + <summary> | |
| 511 | + Can the agent or the model ever see a raw token? | |
| 512 | + <span class="faq-mark" | |
| 513 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 514 | + stroke-width="2" stroke-linecap="round" | |
| 515 | + ><path d="M12 5v14M5 12h14"></path></svg | |
| 516 | + ></span | |
| 517 | + > | |
| 518 | + </summary> | |
| 519 | + <div class="faq-body"> | |
| 520 | + No. Secrets stay host-side by design. The sandbox calls a tool by | |
| 521 | + name; Executor attaches the credential to the real request | |
| 522 | + outside the sandbox, so a token is never present in anything the | |
| 523 | + model can read. | |
| 524 | + </div> | |
| 525 | + </details> | |
| 526 | + | |
| 527 | + <details class="faq-item"> | |
| 528 | + <summary> | |
| 529 | + What can call Executor? | |
| 530 | + <span class="faq-mark" | |
| 531 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 532 | + stroke-width="2" stroke-linecap="round" | |
| 533 | + ><path d="M12 5v14M5 12h14"></path></svg | |
| 534 | + ></span | |
| 535 | + > | |
| 536 | + </summary> | |
| 537 | + <div class="faq-body"> | |
| 538 | + Any MCP client (Claude Code, Cursor, Codex, and others), the | |
| 539 | + Executor CLI, or a native client you drop in. Because tools share | |
| 540 | + one shape, the calling surface is interchangeable. | |
| 541 | + </div> | |
| 542 | + </details> | |
| 543 | + | |
| 544 | + <details class="faq-item"> | |
| 545 | + <summary> | |
| 546 | + How does it know what is safe to auto-run? | |
| 547 | + <span class="faq-mark" | |
| 548 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 549 | + stroke-width="2" stroke-linecap="round" | |
| 550 | + ><path d="M12 5v14M5 12h14"></path></svg | |
| 551 | + ></span | |
| 552 | + > | |
| 553 | + </summary> | |
| 554 | + <div class="faq-body"> | |
| 555 | + Executor preserves the semantics of whatever it imported: GET vs | |
| 556 | + DELETE for OpenAPI, destructiveHint for MCP, and mutations for | |
| 557 | + GraphQL. That tells the agent what it can run on its own and what | |
| 558 | + should pull you back into the loop. | |
| 559 | + </div> | |
| 560 | + </details> | |
| 561 | + | |
| 562 | + <details class="faq-item"> | |
| 563 | + <summary> | |
| 564 | + Is it open source? Can I self-host? | |
| 565 | + <span class="faq-mark" | |
| 566 | + ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 567 | + stroke-width="2" stroke-linecap="round" | |
| 568 | + ><path d="M12 5v14M5 12h14"></path></svg | |
| 569 | + ></span | |
| 570 | + > | |
| 571 | + </summary> | |
| 572 | + <div class="faq-body"> | |
| 573 | + Yes. Executor is MIT licensed and built on the SDK we publish to | |
| 574 | + npm. Run the desktop app locally, self-host the server, or use | |
| 575 | + the hosted cloud. Same code paths, different deployment. | |
| 576 | + </div> | |
| 577 | + </details> | |
| 578 | + </div> | |
| 579 | + </div> | |
| 580 | + </section> | |
| 581 | + | |
| 582 | + {/* ─── PRICING ─── */} | |
| 583 | + <section id="pricing" class="py-20 sm:py-28 border-t border-rule"> | |
| 584 | + <div class="reveal max-w-[1100px] mx-auto px-6 sm:px-10"> | |
| 329 | 585 | <div class="mb-14 mx-auto text-center max-w-2xl"> |
| 330 | − <div | |
| 331 | − class="font-mono text-[12px] uppercase tracking-[0.18em] text-ink-3 mb-4" | |
| 332 | − > | |
| 333 | − Get started | |
| 334 | − </div> | |
| 335 | − <h2 | |
| 336 | − class="text-[clamp(2.2rem,4.5vw,3.4rem)] font-semibold tracking-[-0.02em] leading-[1.05] text-ink mx-auto max-w-[20ch] text-balance" | |
| 337 | − > | |
| 338 | − Pick your <span class="serif-italic">path</span>. | |
| 586 | + <div class="eyebrow mb-4">Pricing</div> | |
| 587 | + <h2 class="section-title mx-auto max-w-[20ch]"> | |
| 588 | + Start free, pay as you <span class="serif-italic">run</span>. | |
| 339 | 589 | </h2> |
| 340 | 590 | </div> |
| 341 | 591 | |
| 342 | − <div | |
| 343 | − class="grid md:grid-cols-2 gap-6 max-w-[760px] mx-auto" | |
| 344 | − > | |
| 345 | − {/* Cloud */} | |
| 592 | + <div class="grid md:grid-cols-3 gap-6 max-w-[1000px] mx-auto items-stretch"> | |
| 593 | + {/* Free */} | |
| 594 | + <div class="surface-card path-card p-8 flex flex-col"> | |
| 595 | + <div class="flex items-center gap-2 mb-4"> | |
| 596 | + <span class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3" | |
| 597 | + >Get started</span | |
| 598 | + > | |
| 599 | + </div> | |
| 600 | + <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1"> | |
| 601 | + Free | |
| 602 | + </h3> | |
| 603 | + <p class="text-[14px] text-ink-3 mb-5">For small teams getting started</p> | |
| 604 | + <div class="flex items-baseline gap-1.5 mb-6"> | |
| 605 | + <span | |
| 606 | + class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums" | |
| 607 | + >$0</span | |
| 608 | + > | |
| 609 | + <span class="text-[13.5px] text-ink-3">/ month</span> | |
| 610 | + </div> | |
| 611 | + <a href="/cloud" class="btn-primary self-start mb-7">Start free →</a> | |
| 612 | + <ul class="check-list"> | |
| 613 | + {[ | |
| 614 | + "Up to 3 members", | |
| 615 | + "10,000 included executions per month", | |
| 616 | + "$0.20 per 1,000 additional executions", | |
| 617 | + "Unlimited integrations", | |
| 618 | + ].map((f) => ( | |
| 619 | + <li> | |
| 620 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 621 | + stroke-width="2" stroke-linecap="round" stroke-linejoin="round" | |
| 622 | + ><path d="M20 6 9 17l-5-5"></path></svg | |
| 623 | + >{f} | |
| 624 | + </li> | |
| 625 | + ))} | |
| 626 | + </ul> | |
| 627 | + </div> | |
| 628 | + | |
| 629 | + {/* Team */} | |
| 346 | 630 | <div |
| 347 | − class="surface-card p-8 flex flex-col" | |
| 631 | + class="surface-card path-card beam-card p-8 flex flex-col" | |
| 348 | 632 | style="border-color: rgba(99,91,255,0.25)" |
| 349 | 633 | > |
| 350 | 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 | 637 | style="color: var(--color-accent)">Recommended</span |
| 354 | 638 | > |
| 355 | 639 | </div> |
| 356 | − <h3 | |
| 357 | − class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3" | |
| 358 | − > | |
| 359 | − Cloud | |
| 640 | + <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1"> | |
| 641 | + Team | |
| 360 | 642 | </h3> |
| 361 | − <p | |
| 362 | − class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty" | |
| 363 | − > | |
| 364 | − Hosted Executor. Auth, sync, policies, and the whole team | |
| 365 | − online in five minutes. Free tier to start. | |
| 366 | − </p> | |
| 367 | − <p class="mb-6 text-[13.5px] leading-[1.55] text-ink-3 flex-1"> | |
| 368 | − <SelfHostContactModal client:load variant="inline" /> | |
| 369 | − </p> | |
| 370 | − <a href="/cloud" class="btn-primary self-start">Try Cloud →</a> | |
| 643 | + <p class="text-[14px] text-ink-3 mb-5">For growing organizations</p> | |
| 644 | + <div class="flex items-baseline gap-1.5 mb-6"> | |
| 645 | + <span | |
| 646 | + class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums" | |
| 647 | + >$49</span | |
| 648 | + > | |
| 649 | + <span class="text-[13.5px] text-ink-3">/ org / month</span> | |
| 650 | + </div> | |
| 651 | + <a href="/cloud" class="btn-primary self-start mb-7">Start Team →</a> | |
| 652 | + <ul class="check-list"> | |
| 653 | + {[ | |
| 654 | + "Unlimited members", | |
| 655 | + "250,000 included executions per month", | |
| 656 | + "5 minute execution timeout", | |
| 657 | + "Join by team domain", | |
| 658 | + "$0.20 per 1,000 additional executions", | |
| 659 | + ].map((f) => ( | |
| 660 | + <li> | |
| 661 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 662 | + stroke-width="2" stroke-linecap="round" stroke-linejoin="round" | |
| 663 | + ><path d="M20 6 9 17l-5-5"></path></svg | |
| 664 | + >{f} | |
| 665 | + </li> | |
| 666 | + ))} | |
| 667 | + </ul> | |
| 371 | 668 | </div> |
| 372 | 669 | |
| 373 | − {/* Desktop */} | |
| 374 | − <div class="surface-card p-8 flex flex-col"> | |
| 670 | + {/* Enterprise */} | |
| 671 | + <div class="surface-card path-card p-8 flex flex-col"> | |
| 375 | 672 | <div class="flex items-center gap-2 mb-4"> |
| 376 | − <span | |
| 377 | − class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3" | |
| 378 | − >Open source</span | |
| 673 | + <span class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3" | |
| 674 | + >Custom needs</span | |
| 379 | 675 | > |
| 380 | 676 | </div> |
| 381 | − <h3 | |
| 382 | − class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3" | |
| 383 | − > | |
| 384 | − Desktop | |
| 677 | + <h3 class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-1"> | |
| 678 | + Enterprise | |
| 385 | 679 | </h3> |
| 386 | − <p | |
| 387 | − class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1" | |
| 388 | − > | |
| 389 | − A native app for Mac, Windows, and Linux. Everything stays | |
| 390 | − on your machine — sources, secrets, sessions. MIT licensed. | |
| 391 | − </p> | |
| 392 | − <button | |
| 393 | − type="button" | |
| 394 | − data-jump-to-local | |
| 395 | − class="btn-primary self-start" | |
| 680 | + <p class="text-[14px] text-ink-3 mb-5">For orgs with custom needs</p> | |
| 681 | + <div class="flex items-baseline gap-1.5 mb-6"> | |
| 682 | + <span | |
| 683 | + class="text-[40px] leading-none font-semibold tracking-[-0.02em] text-ink tabular-nums" | |
| 684 | + >Custom</span | |
| 685 | + > | |
| 686 | + </div> | |
| 687 | + <a | |
| 688 | + href="mailto:rhys@executor.sh?subject=Executor%20Enterprise%20inquiry" | |
| 689 | + class="btn-primary self-start mb-7">Contact us →</a | |
| 396 | 690 | > |
| 397 | − Download → | |
| 398 | − </button> | |
| 691 | + <p class="text-[12.5px] font-medium text-ink mb-2.5">Everything in Team, plus</p> | |
| 692 | + <ul class="check-list"> | |
| 693 | + {[ | |
| 694 | + "Self-hosted or dedicated cloud deployment support", | |
| 695 | + "SSO / SAML & SCIM provisioning", | |
| 696 | + "Audit logs for every tool call", | |
| 697 | + "Dedicated support & onboarding", | |
| 698 | + "Security reviews, DPA & SOC 2 on request", | |
| 699 | + ].map((f) => ( | |
| 700 | + <li> | |
| 701 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" | |
| 702 | + stroke-width="2" stroke-linecap="round" stroke-linejoin="round" | |
| 703 | + ><path d="M20 6 9 17l-5-5"></path></svg | |
| 704 | + >{f} | |
| 705 | + </li> | |
| 706 | + ))} | |
| 707 | + </ul> | |
| 399 | 708 | </div> |
| 400 | 709 | </div> |
| 401 | 710 | </div> |
| 402 | 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 | 739 | {/* ─── FOOTER ─── */} |
| 405 | 740 | <footer class="border-t border-rule py-10"> |
| 406 | 741 | <div |
| @@ -414,13 +749,11 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 7 unmodified lines | ||
| 414 | 749 | > |
| 415 | 750 | </div> |
| 416 | 751 | <div class="flex items-center gap-6 text-[13px] text-ink-2"> |
| 417 | − <a | |
| 418 | − href="https://github.com/RhysSullivan/executor" | |
| 419 | − class="hover:text-ink transition-colors">GitHub</a | |
| 420 | − > | |
| 421 | − <a href="/privacy" class="hover:text-ink transition-colors" | |
| 422 | − >Privacy</a | |
| 752 | + <a href="https://executor.sh/docs" class="hover:text-ink transition-colors" | |
| 753 | + >Docs</a | |
| 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 | 757 | <a href="/terms" class="hover:text-ink transition-colors">Terms</a> |
| 425 | 758 | </div> |
| 426 | 759 | </div> |
| @@ -430,98 +763,93 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 3 unmodified lines | ||
| 430 | 763 | </Layout> |
| 431 | 764 | |
| 432 | 765 | <script> |
| 433 | − document | |
| 434 | − .querySelectorAll<HTMLButtonElement>("button[data-copy]") | |
| 435 | − .forEach((btn) => { | |
| 436 | − let timer: ReturnType<typeof setTimeout> | null = null; | |
| 766 | + // Copy buttons. Use the async Clipboard API in secure contexts and fall back | |
| 767 | + // to a hidden textarea + execCommand otherwise, so copy (and the copied-state | |
| 768 | + // animation) also works over plain HTTP, e.g. a LAN / tailnet dev host where | |
| 769 | + // navigator.clipboard is undefined. | |
| 770 | + const copyToClipboard = async (text: string): Promise<boolean> => { | |
| 771 | + if (window.isSecureContext && navigator.clipboard?.writeText) { | |
| 772 | + try { | |
| 773 | + await navigator.clipboard.writeText(text); | |
| 774 | + return true; | |
| 775 | + } catch {} | |
| 776 | + } | |
| 777 | + const ta = document.createElement("textarea"); | |
| 778 | + ta.value = text; | |
| 779 | + ta.setAttribute("readonly", ""); | |
| 780 | + ta.style.position = "fixed"; | |
| 781 | + ta.style.top = "0"; | |
| 782 | + ta.style.left = "0"; | |
| 783 | + ta.style.opacity = "0"; | |
| 784 | + document.body.appendChild(ta); | |
| 785 | + ta.select(); | |
| 786 | + let ok = false; | |
| 787 | + try { | |
| 788 | + ok = document.execCommand("copy"); | |
| 789 | + } catch {} | |
| 790 | + document.body.removeChild(ta); | |
| 791 | + return ok; | |
| 792 | + }; | |
| 437 | 793 | |
| 438 | − btn.addEventListener("click", async () => { | |
| 439 | − const text = btn.dataset.copy ?? ""; | |
| 440 | − try { | |
| 441 | − await navigator.clipboard.writeText(text); | |
| 442 | − btn.dataset.copied = "true"; | |
| 443 | − const event = btn.dataset.event; | |
| 444 | − if (event) { | |
| 445 | − window.dispatchEvent( | |
| 446 | − new CustomEvent("analytics:capture", { | |
| 447 | − detail: { event, properties: { surface: "marketing_hero" } }, | |
| 448 | − }), | |
| 449 | − ); | |
| 450 | − } | |
| 451 | − } catch { | |
| 452 | − btn.dataset.copied = "false"; | |
| 453 | − } | |
| 454 | − if (timer) clearTimeout(timer); | |
| 455 | − timer = setTimeout(() => { | |
| 456 | − delete btn.dataset.copied; | |
| 457 | − }, 1500); | |
| 458 | − }); | |
| 794 | + for (const btn of document.querySelectorAll<HTMLButtonElement>( | |
| 795 | + "button[data-copy]", | |
| 796 | + )) { | |
| 797 | + let timer: ReturnType<typeof setTimeout> | null = null; | |
| 798 | + btn.addEventListener("click", async () => { | |
| 799 | + const ok = await copyToClipboard(btn.dataset.copy ?? ""); | |
| 800 | + btn.dataset.copied = ok ? "true" : "false"; | |
| 801 | + const event = btn.dataset.event; | |
| 802 | + if (ok && event) { | |
| 803 | + window.dispatchEvent( | |
| 804 | + new CustomEvent("analytics:capture", { | |
| 805 | + detail: { event, properties: { surface: "marketing_hero" } }, | |
| 806 | + }), | |
| 807 | + ); | |
| 808 | + } | |
| 809 | + if (timer) clearTimeout(timer); | |
| 810 | + timer = setTimeout(() => { | |
| 811 | + delete btn.dataset.copied; | |
| 812 | + }, 1500); | |
| 459 | 813 | }); |
| 814 | + } | |
| 460 | 815 | </script> |
| 461 | 816 | |
| 462 | 817 | <script> |
| 463 | − // ── Hero tab toggle: Try in Cloud / Try locally ──────────────────── | |
| 464 | − // Drives the sliding pill indicator + pane visibility via a single | |
| 465 | − // `data-active` attribute on the wrapper. Default "cloud" so no-JS | |
| 466 | − // visitors still see the recommended path. | |
| 467 | − const heroToggle = document.querySelector<HTMLElement>(".hero-toggle"); | |
| 468 | − const tabToggle = heroToggle?.querySelector<HTMLElement>(".tab-toggle"); | |
| 469 | − const TAB_PARAM = "try"; | |
| 470 | − const isHeroTab = (v: unknown): v is "cloud" | "local" => | |
| 471 | − v === "cloud" || v === "local"; | |
| 472 | − const syncTabToUrl = (tab: "cloud" | "local") => { | |
| 473 | − const url = new URL(window.location.href); | |
| 474 | − if (url.searchParams.get(TAB_PARAM) === tab) return; | |
| 475 | − url.searchParams.set(TAB_PARAM, tab); | |
| 476 | − window.history.replaceState(window.history.state, "", url); | |
| 477 | − }; | |
| 478 | − const setActiveTab = (tab: "cloud" | "local", updateUrl = true) => { | |
| 479 | − if (!heroToggle || !tabToggle) return; | |
| 480 | − heroToggle.dataset.active = tab; | |
| 481 | − tabToggle.dataset.active = tab; | |
| 482 | − tabToggle.querySelectorAll<HTMLButtonElement>("button[data-tab]").forEach( | |
| 483 | − (btn) => { | |
| 484 | − btn.setAttribute( | |
| 485 | − "aria-selected", | |
| 486 | − btn.dataset.tab === tab ? "true" : "false", | |
| 487 | − ); | |
| 488 | − }, | |
| 818 | + // ── Hero "For humans / For agents" audience toggle ───────────────── | |
| 819 | + const heroCta = document.querySelector<HTMLElement>(".hero-cta"); | |
| 820 | + if (heroCta) { | |
| 821 | + const tabs = Array.from( | |
| 822 | + heroCta.querySelectorAll<HTMLButtonElement>("[data-audience-set]"), | |
| 489 | 823 | ); |
| 490 | − if (updateUrl) syncTabToUrl(tab); | |
| 491 | − }; | |
| 492 | − if (heroToggle && tabToggle) { | |
| 493 | − const initial = new URLSearchParams(window.location.search).get(TAB_PARAM); | |
| 494 | − if (isHeroTab(initial)) setActiveTab(initial, false); | |
| 495 | − tabToggle.querySelectorAll<HTMLButtonElement>("button[data-tab]").forEach( | |
| 496 | − (btn) => { | |
| 497 | − btn.addEventListener("click", () => { | |
| 498 | − const tab = btn.dataset.tab as "cloud" | "local" | undefined; | |
| 499 | − if (tab) setActiveTab(tab); | |
| 500 | − }); | |
| 501 | − }, | |
| 824 | + const panes = Array.from( | |
| 825 | + heroCta.querySelectorAll<HTMLElement>("[data-audience-pane]"), | |
| 502 | 826 | ); |
| 503 | − window.addEventListener("popstate", () => { | |
| 504 | − const next = new URLSearchParams(window.location.search).get(TAB_PARAM); | |
| 505 | − setActiveTab(isHeroTab(next) ? next : "cloud", false); | |
| 506 | − }); | |
| 827 | + const setAudience = (aud: string) => { | |
| 828 | + heroCta.dataset.audience = aud; | |
| 829 | + for (const t of tabs) { | |
| 830 | + t.setAttribute( | |
| 831 | + "aria-selected", | |
| 832 | + t.dataset.audienceSet === aud ? "true" : "false", | |
| 833 | + ); | |
| 834 | + } | |
| 835 | + for (const p of panes) { | |
| 836 | + p.hidden = p.dataset.audiencePane !== aud; | |
| 837 | + } | |
| 838 | + }; | |
| 839 | + for (const t of tabs) { | |
| 840 | + t.addEventListener("click", () => | |
| 841 | + setAudience(t.dataset.audienceSet ?? "humans"), | |
| 842 | + ); | |
| 843 | + } | |
| 507 | 844 | } |
| 845 | +</script> | |
| 508 | 846 | |
| 509 | − // Desktop card "Download →" CTA jumps to the hero, switches to Local, | |
| 510 | − // and lets the auto-detect script there pick the right OS button. | |
| 511 | − document | |
| 512 | − .querySelectorAll<HTMLButtonElement>("button[data-jump-to-local]") | |
| 513 | − .forEach((btn) => { | |
| 514 | − btn.addEventListener("click", () => { | |
| 515 | − setActiveTab("local"); | |
| 516 | − heroToggle?.scrollIntoView({ behavior: "smooth", block: "center" }); | |
| 517 | − }); | |
| 518 | − }); | |
| 519 | − | |
| 847 | +<script> | |
| 520 | 848 | // ── Auto-detect download platform ────────────────────────────────── |
| 521 | − // Shows ONE primary button matching the visitor's OS in the "Try | |
| 522 | − // locally" pane; tucks the others behind an "Other platforms" | |
| 523 | − // popover. No-JS / unrecognised UA: all four buttons visible (still | |
| 524 | − // functional, just less polished). | |
| 849 | + // Shows ONE primary button matching the visitor's OS in the Desktop | |
| 850 | + // card; tucks the others behind an "Other platforms" popover. No-JS / | |
| 851 | + // unrecognised UA: all four buttons visible (still functional, just | |
| 852 | + // less polished). | |
| 525 | 853 | type DownloadOs = "mac-arm64" | "mac-x64" | "win-x64" | "linux-x64"; |
| 526 | 854 | |
| 527 | 855 | type UserAgentData = { |
| @@ -594,3 +922,31 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 66 unmodified lines | ||
| 594 | 922 | other.classList.remove("hidden"); |
| 595 | 923 | })(); |
| 596 | 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> | |
| @@ -66,7 +66,7 @@ body { · 65 unmodified lines | ||
| 66 | 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 | 70 | .pattern-fade-overlay { |
| 71 | 71 | position: absolute; |
| 72 | 72 | inset: 0; |
| @@ -75,7 +75,7 @@ body { · 2 unmodified lines | ||
| 75 | 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 | 79 | .serif-italic { |
| 80 | 80 | font-family: var(--font-serif); |
| 81 | 81 | font-style: italic; |
| @@ -111,7 +111,7 @@ body { · 29 unmodified lines | ||
| 111 | 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 | 115 | .surface-card { |
| 116 | 116 | background: white; |
| 117 | 117 | border: 1px solid var(--color-rule); |
| @@ -136,7 +136,7 @@ body { · 18 unmodified lines | ||
| 136 | 136 | background: #1f1f24; |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | −/* Light, bordered counterpart to .btn-primary — a real secondary CTA. Sized to | |
| 139 | +/* Light, bordered counterpart to .btn-primary, a real secondary CTA. Sized to | |
| 140 | 140 | match .btn-primary--hero so it balances under the hero's primary button. */ |
| 141 | 141 | .btn-secondary { |
| 142 | 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 | 213 | A sliding white pill animates between two equal-width tabs. |
| 214 | 214 | The active pane is revealed below; inactive panes hide. |
| 215 | 215 | ─────────────────────────────────────────────────────────── */ |
| @@ -271,7 +271,7 @@ body { · 55 unmodified lines | ||
| 271 | 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 | 275 | tight max-width so two lines wrap naturally on narrow widths. */ |
| 276 | 276 | .pane-desc { |
| 277 | 277 | font-size: 14.5px; |
| @@ -284,7 +284,7 @@ body { · 6 unmodified lines | ||
| 284 | 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 | 288 | .pane-meta { |
| 289 | 289 | display: inline-flex; |
| 290 | 290 | flex-wrap: wrap; |
| @@ -334,18 +334,13 @@ body { · 43 unmodified lines | ||
| 334 | 334 | user-select: none; |
| 335 | 335 | font-size: 13px; |
| 336 | 336 | color: var(--color-ink-3); |
| 337 | − padding: 0.4rem 0.6rem; | |
| 338 | − border-radius: 8px; | |
| 339 | − transition: | |
| 340 | − color 0.15s ease, | |
| 341 | − background 0.15s ease; | |
| 337 | + transition: color 0.15s ease; | |
| 342 | 338 | } |
| 343 | 339 | .download-other > summary::-webkit-details-marker { |
| 344 | 340 | display: none; |
| 345 | 341 | } |
| 346 | 342 | .download-other > summary:hover { |
| 347 | 343 | color: var(--color-ink-2); |
| 348 | − background: var(--color-surface-2); | |
| 349 | 344 | } |
| 350 | 345 | .download-other[open] > summary { |
| 351 | 346 | color: var(--color-ink); |
| @@ -384,14 +379,14 @@ body { · 32 unmodified lines | ||
| 384 | 379 | background: var(--color-surface-2); |
| 385 | 380 | } |
| 386 | 381 | |
| 387 | −/* Larger primary button for the hero pane — keeps btn-primary base sizing | |
| 382 | +/* Larger primary button for the hero pane: keeps btn-primary base sizing | |
| 388 | 383 | for nav/cards but bumps the hero's CTA to a more confident scale */ |
| 389 | 384 | .btn-primary--hero { |
| 390 | 385 | padding: 0.7rem 1.25rem; |
| 391 | 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 | 390 | .link-accent { |
| 396 | 391 | color: var(--color-accent); |
| 397 | 392 | font-weight: 500; |
| @@ -470,3 +465,1057 @@ body { · 72 unmodified lines | ||
| 470 | 465 | color: var(--color-ink); |
| 471 | 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 | +} | |