diff --git a/public/brand/harness/gemini.svg b/public/brand/harness/gemini.svg new file mode 100644 index 00000000..765adb08 --- /dev/null +++ b/public/brand/harness/gemini.svg @@ -0,0 +1 @@ +Google Gemini \ No newline at end of file diff --git a/public/brand/harness/grok.svg b/public/brand/harness/grok.svg new file mode 100644 index 00000000..cf5c3bb2 --- /dev/null +++ b/public/brand/harness/grok.svg @@ -0,0 +1 @@ +Grok diff --git a/public/brand/harness/hermes.png b/public/brand/harness/hermes.png new file mode 100644 index 00000000..e794a6b5 Binary files /dev/null and b/public/brand/harness/hermes.png differ diff --git a/public/brand/harness/muse.svg b/public/brand/harness/muse.svg new file mode 100644 index 00000000..7c2b9df4 --- /dev/null +++ b/public/brand/harness/muse.svg @@ -0,0 +1 @@ +Muse diff --git a/public/brand/harness/openclaw.png b/public/brand/harness/openclaw.png new file mode 100644 index 00000000..c92f737c Binary files /dev/null and b/public/brand/harness/openclaw.png differ diff --git a/public/img/cloud/friends-240.webp b/public/img/cloud/friends-240.webp new file mode 100644 index 00000000..b89b31cd Binary files /dev/null and b/public/img/cloud/friends-240.webp differ diff --git a/public/img/cloud/friends-360.webp b/public/img/cloud/friends-360.webp new file mode 100644 index 00000000..a199d395 Binary files /dev/null and b/public/img/cloud/friends-360.webp differ diff --git a/public/img/cloud/friends-480.webp b/public/img/cloud/friends-480.webp new file mode 100644 index 00000000..65ba7940 Binary files /dev/null and b/public/img/cloud/friends-480.webp differ diff --git a/public/img/cloud/friends-600.webp b/public/img/cloud/friends-600.webp new file mode 100644 index 00000000..b69d865b Binary files /dev/null and b/public/img/cloud/friends-600.webp differ diff --git a/public/img/cloud/inbox-240.webp b/public/img/cloud/inbox-240.webp new file mode 100644 index 00000000..792f64ae Binary files /dev/null and b/public/img/cloud/inbox-240.webp differ diff --git a/public/img/cloud/inbox-360.webp b/public/img/cloud/inbox-360.webp new file mode 100644 index 00000000..fafb59e1 Binary files /dev/null and b/public/img/cloud/inbox-360.webp differ diff --git a/public/img/cloud/inbox-480.webp b/public/img/cloud/inbox-480.webp new file mode 100644 index 00000000..79ba24e1 Binary files /dev/null and b/public/img/cloud/inbox-480.webp differ diff --git a/public/img/cloud/inbox-600.webp b/public/img/cloud/inbox-600.webp new file mode 100644 index 00000000..f0a620c1 Binary files /dev/null and b/public/img/cloud/inbox-600.webp differ diff --git a/src/pages/index.astro b/src/pages/index.astro index 2eaa4a6c..90804a7d 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -59,11 +59,19 @@ const featuredBlurbs: Record = {
- + New - - Try shell.online, a live browser link for any terminal + + Try Pilot Cloud, add Pilot to any Agent or Chatbot +
@@ -156,6 +164,20 @@ const featuredBlurbs: Record = { } .announce-text { color: var(--ink); } .announce-text strong { color: var(--accent-text); font-weight: 650; } + .announce-ais { + flex: none; + display: inline-flex; + align-items: center; + gap: 0.45rem; + } + /* Brand logos as masks, tinted like the MCP section's harness chips. */ + .announce-ai { + width: 18px; + height: 18px; + background: var(--logo-color, currentColor); + -webkit-mask: var(--logo) center / contain no-repeat; + mask: var(--logo) center / contain no-repeat; + } .announce-arrow { flex: none; color: var(--ann-line); @@ -167,10 +189,13 @@ const featuredBlurbs: Record = { @media (prefers-reduced-motion: reduce) { .announce-bar:hover .announce-arrow { transform: none; } } - @media (max-width: 560px) { + @media (max-width: 660px) { .announce-bar { font-size: 0.88rem; padding: 0.7rem 1rem; } .announce-headline { gap: 0.5rem; } .announce-badge { display: none; } + .announce-logo { display: none; } + .announce-ais { gap: 0.3rem; } + .announce-ai { width: 15px; height: 15px; } } @@ -263,6 +288,88 @@ const featuredBlurbs: Record = { + +
+
+
+
+
+
New
+

Pilot Cloud.

+

A Pilot node that runs for you, always on. Add it to any agent or chatbot with + one URL. Nothing to install, no server to keep up.

+
+ +
+
+
+
+ Pilot Cloud iPhone app, Friends tab: your shareable link, the Close, Friend and Acquaintance circles, and a list of close friends. + +
+
+
+
+ Pilot Cloud iPhone app, Inbox tab: conversations with friends' agents, each with a message preview. + +
+
+
+

Mobile app coming soon

+
+ +
+
    +
  • Your agent meets theirs. Add a friend and your agents talk to each other directly.
  • +
  • They work it out together. Plans, times, files and answers, sorted agent to agent.
  • +
  • You stay in charge. Circles set what each friend's agent can get from yours.
  • +
+ +
+
+ +
+
+
Add this URL as an MCP server:
+ +
+
+
Works with
+
+
+ Muse + Grok Bot + Dots + OpenClaw + Hermes + Claude Code +
+
+
+
+
+
+
+
@@ -1702,6 +1809,437 @@ const featuredBlurbs: Record = { .econ-dev { padding: 30px 24px; } } + /* Pilot Cloud: hosted-node card between "Backed by" and the App Store. + Copy on the left, two upright iPhones on the right (Friends in front, Inbox + tucked behind it), and the MCP URL + harness chips in a full-width strip + along the bottom of the card. Below 760px it stacks head → phones → body. + content-visibility: visible opts out of the below-fold paint containment + on main > section.section, which would clip the card's drop shadow flat at + the section's bottom edge (padding-bottom is 0). */ + #cloud { padding-top: 40px; padding-bottom: 0; content-visibility: visible; } + @media (max-width: 900px) { #cloud { padding-top: 24px; } } + /* .reveal keeps will-change: opacity, transform for good, which can leave + the phone screenshots rasterised soft; drop it once the section has landed. */ + #cloud.visible { will-change: auto; } + .cloud-card { + --cloud-pad-x: 44px; + border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent); + background: color-mix(in srgb, var(--bg-2) 55%, transparent); + -webkit-backdrop-filter: blur(18px) saturate(1.4); + backdrop-filter: blur(18px) saturate(1.4); + box-shadow: + 0 8px 32px color-mix(in srgb, #000 22%, transparent), + inset 0 1px 0 color-mix(in srgb, #fff 14%, transparent); + } + /* The phones span both rows; the head sits on the row line from above and + the body hangs from it, so the copy stays centred against the pair. */ + .cloud-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); + grid-template-areas: + "head stage" + "body stage"; + grid-template-rows: auto auto; + column-gap: 48px; + padding: 32px var(--cloud-pad-x); + } + .cloud-head { grid-area: head; align-self: end; } + .cloud-body { grid-area: body; align-self: start; } + + .cloud-eyebrow { + display: inline-flex; + align-items: center; + gap: 10px; + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--accent-text); + } + .cloud-new { + padding: 3px 7px; + background: var(--accent); + color: var(--accent-ink); + font-weight: 700; + } + .cloud-title { + margin: 12px 0 0; + font-size: clamp(30px, 3.2vw, 44px); + line-height: 1.02; + letter-spacing: -0.025em; + } + .cloud-title em { color: var(--accent-text); } + .cloud-sub { margin-top: 10px; font-size: 16px; color: var(--ink-dim); line-height: 1.55; max-width: 32em; } + .cloud-points { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 6px; max-width: 34em; } + .cloud-points li { + position: relative; + padding-left: 22px; + font-size: 15px; + line-height: 1.5; + color: var(--ink-dim); + } + .cloud-points li b { color: var(--ink); font-weight: 600; } + .cloud-points li::before { + content: ""; + position: absolute; + left: 0; + top: 0.6em; + width: 8px; + height: 8px; + background: var(--accent); + } + .cloud-ctas { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; } + + /* ---- Phone pair --------------------------------------------------------- + --pw is one phone's outer width, a whole pixel per breakpoint; every frame measurement + (bezel, corner radius, Dynamic Island, buttons) is a fraction of it so + the device keeps iPhone 16 Pro proportions at any size. Nothing on the + screens is transformed at rest, so the app UI renders pixel-sharp. */ + .cloud-stage { + grid-area: stage; + align-self: center; + position: relative; + isolation: isolate; + --cloud-rim: rgb(255 255 255 / 0.07); + --cloud-btn-a: #4a4a4f; + --cloud-btn-b: #2c2c30; + --cloud-sh1: rgb(0 0 0 / 0.6); + --cloud-sh2: rgb(0 0 0 / 0.5); + --cloud-sh3: rgb(0 0 0 / 0.55); + } + .cloud-stage::before { + content: ""; + position: absolute; + z-index: -1; + inset: -4% -8% -6%; + background: radial-gradient(ellipse 46% 44% at 50% 54%, + color-mix(in srgb, var(--accent) 30%, transparent) 0%, + color-mix(in srgb, var(--accent) 10%, transparent) 55%, + transparent 100%); + pointer-events: none; + } + /* --ov: horizontal offset of the back phone; it tucks well under the front + one. --pw-back: the back phone is 7/8 the size (whole pixels at every + breakpoint) so the front Friends screen reads as the focus. + --drop: how far the back phone sits below the front one. */ + .cloud-pair { + --pw: 160px; + --pw-back: calc(var(--pw) * 0.875); + --ov: calc(var(--pw) * -0.36); + --drop: calc(var(--pw) * 0.2); + display: flex; + justify-content: center; + align-items: flex-start; + padding-bottom: calc(var(--pw) * 0.05); + } + .cloud-phone { + --band: calc(var(--pw) * 0.011); + --bezel: calc(var(--pw) * 0.021); + --r: calc(var(--pw) * 0.155); + --btn: max(1.5px, calc(var(--pw) * 0.0065)); + --ph: calc(var(--pw) * 2.103); + position: relative; + z-index: 1; + flex: none; + width: var(--pw); + margin: 0; + } + .cloud-phone + .cloud-phone { --pw: var(--pw-back); z-index: 0; margin-left: var(--ov); margin-top: var(--drop); } + /* Black glass bezel inside a brushed titanium band (gradient border). + Shadow blurs stay <= 64px: larger ones band into tiles in Chromium. */ + .cloud-phone-frame { + position: relative; + padding: var(--bezel); + border: var(--band) solid transparent; + border-radius: var(--r); + background: + linear-gradient(#050505, #050505) padding-box, + linear-gradient(155deg, #66666b 0%, #2c2c2f 22%, #1d1d20 55%, #3a3a3e 80%, #5a5a5f 100%) border-box; + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 0.05), + 0 0 0 1px var(--cloud-rim), + 0 calc(var(--pw) * 0.12) min(64px, calc(var(--pw) * 0.24)) calc(var(--pw) * -0.06) var(--cloud-sh1), + 0 calc(var(--pw) * 0.04) calc(var(--pw) * 0.08) calc(var(--pw) * -0.04) var(--cloud-sh2); + } + /* The front phone also casts a short shadow sideways onto the back one. */ + .cloud-phone:first-child .cloud-phone-frame { + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 0.05), + 0 0 0 1px var(--cloud-rim), + calc(var(--pw) * 0.04) calc(var(--pw) * 0.03) calc(var(--pw) * 0.07) calc(var(--pw) * -0.01) var(--cloud-sh3), + 0 calc(var(--pw) * 0.12) min(64px, calc(var(--pw) * 0.24)) calc(var(--pw) * -0.06) var(--cloud-sh1), + 0 calc(var(--pw) * 0.04) calc(var(--pw) * 0.08) calc(var(--pw) * -0.04) var(--cloud-sh2); + } + .cloud-phone-screen { + display: block; + width: 100%; + height: auto; + aspect-ratio: 1206 / 2622; + border-radius: calc(var(--r) - var(--band) - var(--bezel)); + background: #f4f5f7; + } + /* Dynamic Island, with a hint of the camera lens at its right end. */ + .cloud-phone-island { + position: absolute; + top: calc(var(--bezel) + var(--pw) * 0.026); + left: 50%; + width: calc(var(--pw) * 0.292); + height: calc(var(--pw) * 0.086); + transform: translateX(-50%); + border-radius: 999px; + background: + radial-gradient(circle at calc(100% - var(--pw) * 0.043) 50%, + #22283a 0, #0d1018 calc(var(--pw) * 0.012), transparent calc(var(--pw) * 0.014)), + #000; + } + /* Side buttons: action + volume on the left, side button and the flush + Camera Control on the right. */ + .cloud-phone::before, + .cloud-phone::after, + .cloud-phone-frame::before, + .cloud-phone-frame::after { + content: ""; + position: absolute; + z-index: -1; + width: calc(var(--btn) + var(--band)); + background: linear-gradient(90deg, var(--cloud-btn-a), var(--cloud-btn-b)); + } + .cloud-phone::before { + left: calc(var(--btn) * -1); + top: 17%; + height: 3.6%; + border-radius: var(--btn) 0 0 var(--btn); + } + .cloud-phone::after { + left: calc(var(--btn) * -1); + top: 24%; + height: 6.2%; + border-radius: var(--btn) 0 0 var(--btn); + box-shadow: 0 calc(var(--ph) * 0.082) 0 var(--cloud-btn-b); + } + .cloud-phone-frame::after { + right: calc((var(--btn) + var(--band)) * -1); + top: 26%; + height: 9.5%; + border-radius: 0 var(--btn) var(--btn) 0; + } + .cloud-phone-frame::before { + right: calc((var(--btn) * 0.6 + var(--band)) * -1); + width: calc(var(--btn) * 0.6 + var(--band)); + top: 56%; + height: 6.5%; + border-radius: 0 var(--btn) var(--btn) 0; + background: linear-gradient(90deg, var(--cloud-btn-b), #1b1b1e); + } + /* Light theme: darker rim so the titanium edge reads on cream, olive-tinted + shadows, and a paler, airier lime halo instead of a saturated blob. */ + :global([data-theme="light"]) .cloud-stage { + --cloud-rim: rgb(0 0 0 / 0.3); + --cloud-btn-a: #6a6a70; + --cloud-btn-b: #3c3c41; + --cloud-sh1: rgb(28 36 8 / 0.32); + --cloud-sh2: rgb(28 36 8 / 0.28); + --cloud-sh3: rgb(28 36 8 / 0.3); + } + :global([data-theme="light"]) .cloud-stage::before { + background: radial-gradient(ellipse 50% 46% at 50% 52%, + color-mix(in srgb, var(--accent) 24%, transparent) 0%, + color-mix(in srgb, var(--accent) 8%, transparent) 55%, + transparent 100%); + } + /* "Mobile app coming soon" tag under the pair. */ + .cloud-soon { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin: 14px 0 0; + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--accent-text); + } + .cloud-soon-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--accent); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); + } + @media (prefers-reduced-motion: no-preference) { + .cloud-phone { transition: transform 0.9s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.9s ease; } + .cloud-phone + .cloud-phone { transition-delay: 0.15s; } + #cloud:not(.visible) .cloud-phone { transform: translateY(28px); opacity: 0; } + } + + /* ---- MCP strip along the bottom of the card --------------------------- + Captions share one row and controls the next (subgrid), so "Works with" + lines up with "Add this URL…" even though the copy box is taller. */ + .cloud-strip { + display: grid; + grid-template-columns: minmax(0, 500px) minmax(0, 1fr); + grid-template-rows: auto auto; + column-gap: 48px; + row-gap: 8px; + align-items: center; + padding: 14px var(--cloud-pad-x) 16px; + border-top: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); + background: color-mix(in srgb, var(--accent) 6%, color-mix(in srgb, var(--bg) 70%, transparent)); + } + .cloud-strip-url, + .cloud-strip-works { + display: grid; + grid-row: span 2; + grid-template-rows: subgrid; + row-gap: 10px; + align-items: center; + min-width: 0; + } + .cloud-cap { + font-family: var(--mono); + font-size: 12px; + color: var(--ink-dim); + } + /* .install-copy is styled for the always-dark terminal; this card follows the theme. */ + .cloud-strip .install-copy { + color: var(--ink); + background: var(--bg); + border-color: color-mix(in srgb, var(--accent) 38%, transparent); + font-size: clamp(13px, 1.1vw, 15px); + padding: 11px 12px 11px 16px; + } + .cloud-strip .install-copy:hover { border-color: color-mix(in srgb, var(--accent) 60%, transparent); } + .cloud-strip .install-copy .ic-cmd .y { color: var(--ink-dim); } + .cloud-strip .install-copy .ic-label { color: var(--accent-text); } + :global([data-theme="light"]) .cloud-strip .install-copy .ic-label { + border-color: color-mix(in srgb, var(--accent-text) 45%, transparent); + } + .cloud-strip .install-copy.is-copied .ic-label { color: var(--accent-ink); border-color: var(--accent); } + /* .cloud-strip prefix: the shared .mcp-harnesses rules come later in this file. */ + .cloud-strip .cloud-harnesses { margin-top: 0; gap: 8px; } + .cloud-harnesses .mcp-hn { + font-size: 13px; + gap: 7px; + padding: 7px 12px 7px 9px; + background: var(--bg); + } + .cloud-harnesses .mcp-hn-i { width: 18px; height: 18px; } + /* Full-colour logos (OpenClaw, Hermes) are images rather than tinted masks. */ + .cloud-hn-img { flex: none; width: 18px; height: 18px; object-fit: contain; } + .cloud-hn-img.is-portrait { border-radius: 4px; } + /* The shared .mcp-hn:hover wash loses to the flat --bg fill above, so + restate it over --bg. */ + .cloud-harnesses .mcp-hn:hover { + background: color-mix(in srgb, var(--logo-color, var(--accent)) 8%, var(--bg)); + } + /* The chip layout follows the width of its own column, not the viewport: + one row while all six fit (about 650px); else two rows of three + equal-width chips (about 430px); else two rows of three natural-width + chips in left-aligned columns (about 355px); else three rows of two; + and below that free wrapping. Never five chips plus an orphan. */ + .cloud-hn-fit { container: cloud-hn / inline-size; min-width: 0; } + @container cloud-hn (max-width: 659.98px) { + .cloud-strip .cloud-harnesses { + display: grid; + grid-template-columns: repeat(3, 1fr); + width: max-content; + max-width: 100%; + } + .cloud-harnesses .mcp-hn { justify-content: center; } + } + @container cloud-hn (max-width: 439.98px) { + .cloud-strip .cloud-harnesses { grid-template-columns: repeat(3, max-content); justify-items: start; } + } + @container cloud-hn (max-width: 359.98px) { + .cloud-strip .cloud-harnesses { + grid-template-columns: repeat(2, minmax(0, 1fr)); + justify-items: stretch; + width: auto; + } + } + @container cloud-hn (max-width: 271.98px) { + .cloud-strip .cloud-harnesses { display: flex; } + } + + @media (max-width: 1280px) { + .cloud-main { column-gap: 40px; } + .cloud-strip { column-gap: 40px; } + } + /* Below ~1100px the strip stacks (URL box, then the chips) and the phones + shrink; the copy | phones columns hold until 760px. The halo stops at the + card's edges here so it can never push past the viewport. */ + @media (max-width: 1100px) { + .cloud-card { --cloud-pad-x: 32px; } + .cloud-main { grid-template-columns: minmax(0, 1fr) auto; column-gap: 32px; padding-block: 28px; } + .cloud-pair { --pw: 144px; } + .cloud-stage::before { inset: -4% 0 -6%; } + .cloud-strip { display: flex; flex-direction: column; align-items: stretch; gap: 12px; } + .cloud-strip-url, .cloud-strip-works { display: flex; flex-direction: column; align-items: stretch; gap: 8px; } + .cloud-strip-url { max-width: 560px; } + } + /* Phone widths: head → small phone pair (104/91px) → CTAs. The bullets drop + out (the sub line carries the message); the named chips wrap to two rows + until 560px, where they go icon-only on one row. */ + @media (max-width: 760px) { + .cloud-card { --cloud-pad-x: 24px; } + .cloud-main { + grid-template-columns: minmax(0, 1fr); + grid-template-areas: "head" "stage" "body"; + row-gap: 18px; + padding-block: 24px 20px; + } + .cloud-head, .cloud-body { align-self: auto; } + .cloud-points { display: none; } + .cloud-ctas { margin-top: 0; } + .cloud-pair { --pw: 104px; } + } + /* Phone: tighter card padding, side-by-side CTAs and icon-only chips; the + phones keep their 104/91px size from the 760px step. */ + @media (max-width: 560px) { + .cloud-card { --cloud-pad-x: 22px; } + .cloud-ctas { gap: 8px; flex-wrap: nowrap; } + .cloud-ctas .btn { flex: 1 1 0; min-width: 0; justify-content: center; gap: 6px; padding: 12px 8px; font-size: 11px; letter-spacing: 0.04em; white-space: nowrap; } + .cloud-cta-extra { display: none; } + .cloud-strip { padding-block: 14px 16px; } + /* Icon-only chips on one row; the names stay for screen readers. */ + #cloud .cloud-strip .cloud-harnesses { display: flex; flex-wrap: nowrap; width: auto; gap: 8px; } + #cloud .cloud-harnesses .mcp-hn { padding: 7px; gap: 0; } + .cloud-hn-name { + position: absolute; width: 1px; height: 1px; overflow: hidden; + clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; + } + /* Two clean lines ("https://cloud." / "pilotprotocol.network/mcp") beside + the Copy pill, instead of a mid-word wrap. */ + .cloud-strip .install-copy { font-size: 12px; gap: 10px; padding: 12px 12px 12px 14px; } + #cloud .install-copy .ic-cmd { overflow-wrap: normal; } + .cloud-strip .install-copy .ic-tail { display: inline-block; } + } + @media (max-width: 440px) { + #cloud .cloud-strip .cloud-harnesses { gap: 6px; } + .cloud-strip .install-copy { font-size: 11.5px; } + .cloud-strip .install-copy .ic-label { font-size: 11px; padding: 4px 9px; } + } + /* Too narrow for the URL tail beside the pill: drop the pill beneath it. + The two CTAs no longer fit side by side either, so stack them. */ + @media (max-width: 359px) { + .cloud-strip .install-copy { flex-wrap: wrap; row-gap: 10px; } + .cloud-strip .install-copy .ic-cmd { flex-basis: 100%; } + .cloud-ctas { flex-wrap: wrap; } + .cloud-ctas .btn { flex-basis: 100%; } + } + @media (max-width: 319px) { + #cloud .cloud-strip .cloud-harnesses { gap: 4px; } + #cloud .cloud-harnesses .mcp-hn { padding: 4px; } + } + @media (hover: none) and (pointer: coarse) { + .cloud-card { + -webkit-backdrop-filter: none; + backdrop-filter: none; + background: color-mix(in srgb, var(--bg-2) 94%, var(--bg)); + } + } + /* Pull the App Store callout up nearer the section's top divider line. */ #thesis { padding-top: 64px; padding-bottom: 32px; } @media (max-width: 900px) { #thesis { padding-top: 40px; padding-bottom: 24px; } } @@ -2971,35 +3509,9 @@ const featuredBlurbs: Record = { (function () { var els = document.querySelectorAll('.reveal'); var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; - // Graceful fallback: no observer support or reduced motion → show everything. - if (reduce || !('IntersectionObserver' in window)) { - els.forEach(function (el) { el.classList.add('visible'); }); - var allGroups = document.querySelectorAll('.stagger-group'); - for (var g = 0; g < allGroups.length; g++) { allGroups[g].classList.add('in'); } - var allTerms = document.querySelectorAll('.term-type'); - for (var tt = 0; tt < allTerms.length; tt++) { allTerms[tt].classList.add('printing'); } - return; - } - // Toggle (not one-shot): reveal on enter, hide on leave, so scrolling back - // up replays the animation in reverse — the page reacts to movement both ways. - var obs = new IntersectionObserver(function (entries) { - entries.forEach(function (e) { - e.target.classList.toggle('visible', e.isIntersecting); - // Stagger any child groups (vs columns, steps, cards) in sync with the - // section's reveal — and un-stagger on leave for scroll-up replay. - var groups = e.target.querySelectorAll('.stagger-group'); - for (var i = 0; i < groups.length; i++) { - groups[i].classList.toggle('in', e.isIntersecting); - } - var terms = e.target.querySelectorAll('.term-type'); - for (var t = 0; t < terms.length; t++) { - terms[t].classList.toggle('printing', e.isIntersecting); - } - }); - }, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 }); - els.forEach(function (el) { obs.observe(el); }); // Install card — direct copy buttons (curl one-liner + "tell your agent" prompt). + // Wired before the reduced-motion early return below so they work either way. function normalizeCopyText(value) { return value.split('\n').map(function (line) { return line.replace(/^(\s*)\$\s(?=\S)/, '$1'); @@ -3007,32 +3519,67 @@ const featuredBlurbs: Record = { } document.querySelectorAll('[data-install-copy]').forEach(function (installBtn) { + // Restore the button's own label after "Copied" (e.g. "Copy MCP URL"). + var restingAria = installBtn.getAttribute('aria-label') || + (installBtn.classList.contains('install-tell-btn') ? 'Copy prompt' : 'Copy install command'); + var restingLabelEl = installBtn.querySelector('.ic-label'); + var restingLabel = restingLabelEl ? restingLabelEl.textContent : 'Copy'; + var resetTimer; installBtn.addEventListener('click', function () { var cmd = installBtn.querySelector('.ic-cmd'); var label = installBtn.querySelector('.ic-label'); if (!cmd || !navigator.clipboard) return; var copyText = normalizeCopyText(installBtn.getAttribute('data-copy-text') || cmd.textContent); navigator.clipboard.writeText(copyText).then(function () { - var prev = label.textContent; + clearTimeout(resetTimer); label.textContent = 'Copied'; installBtn.setAttribute('aria-label', 'Copied'); installBtn.classList.add('is-copied'); - setTimeout(function () { - label.textContent = prev; - installBtn.setAttribute('aria-label', installBtn.classList.contains('install-tell-btn') ? 'Copy prompt' : 'Copy install command'); + resetTimer = setTimeout(function () { + label.textContent = restingLabel; + installBtn.setAttribute('aria-label', restingAria); installBtn.classList.remove('is-copied'); }, 1600); }).catch(function () { + clearTimeout(resetTimer); label.textContent = 'Copy failed'; installBtn.setAttribute('aria-label', 'Copy failed'); - setTimeout(function () { - label.textContent = 'Copy'; - installBtn.setAttribute('aria-label', installBtn.classList.contains('install-tell-btn') ? 'Copy prompt' : 'Copy install command'); + resetTimer = setTimeout(function () { + label.textContent = restingLabel; + installBtn.setAttribute('aria-label', restingAria); }, 1600); }); }); }); + // Graceful fallback: no observer support or reduced motion → show everything. + if (reduce || !('IntersectionObserver' in window)) { + els.forEach(function (el) { el.classList.add('visible'); }); + var allGroups = document.querySelectorAll('.stagger-group'); + for (var g = 0; g < allGroups.length; g++) { allGroups[g].classList.add('in'); } + var allTerms = document.querySelectorAll('.term-type'); + for (var tt = 0; tt < allTerms.length; tt++) { allTerms[tt].classList.add('printing'); } + return; + } + // Toggle (not one-shot): reveal on enter, hide on leave, so scrolling back + // up replays the animation in reverse — the page reacts to movement both ways. + var obs = new IntersectionObserver(function (entries) { + entries.forEach(function (e) { + e.target.classList.toggle('visible', e.isIntersecting); + // Stagger any child groups (vs columns, steps, cards) in sync with the + // section's reveal — and un-stagger on leave for scroll-up replay. + var groups = e.target.querySelectorAll('.stagger-group'); + for (var i = 0; i < groups.length; i++) { + groups[i].classList.toggle('in', e.isIntersecting); + } + var terms = e.target.querySelectorAll('.term-type'); + for (var t = 0; t < terms.length; t++) { + terms[t].classList.toggle('printing', e.isIntersecting); + } + }); + }, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 }); + els.forEach(function (el) { obs.observe(el); }); + // Network-layer stack: deal the cards out from under one another — and slide // them back under when the section leaves the viewport. var stack = document.querySelector('.stack'); diff --git a/src/pages/plain/index.astro b/src/pages/plain/index.astro index fef71a3d..f1b257aa 100644 --- a/src/pages/plain/index.astro +++ b/src/pages/plain/index.astro @@ -1,7 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. // plain-source: src/pages/index.astro -// plain-source-sha256: 9b6212b104e94a947a656a1e14a3d4c8329c36ae5b4ca3b3ca791f5500f6aa0b +// plain-source-sha256: 0c57a8883c576b490b67f9345e434ecb5109e44525f5b52252ef7139d6cea562 import PlainLayout from '../../layouts/PlainLayout.astro'; --- @@ -31,6 +31,16 @@ import PlainLayout from '../../layouts/PlainLayout.astro';
  • Agent networking: Agents reach trusted peers through encrypted tunnels. A coordination layer handles discovery and NAT traversal.
  • +

    Pilot Cloud

    +

    Pilot Cloud is a Pilot node that runs for you, always on. Add it to any agent or chatbot with one URL. Nothing to install, no server to keep up.

    +
      +
    • Your agent meets theirs: add a friend and your agents talk to each other directly.
    • +
    • They work it out together: plans, times, files and answers, sorted agent to agent.
    • +
    • You stay in charge: circles set what each friend's agent can get from yours.
    • +
    +
    https://cloud.pilotprotocol.network/mcp
    +

    Works with Muse, Grok Bot, Dots, OpenClaw, Hermes and Claude Code. A mobile app is coming soon. Open Pilot Cloud.

    +

    App Store

    The Pilot Protocol App Store contains applications built for agents. Apps are reviewed, SHA-256 pinned, and signature-verified before installation.