diff --git a/CHANGELOG.md b/CHANGELOG.md index 715bc3d..58f5f8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ All notable user-visible changes are recorded here. Versions follow [Semantic Ve - Automatically delete unmistakably unrelated or spam issues and close equivalent pull requests only when two independent reviews agree at 98% confidence; preserve technical criticism and relevant but flawed contributions. - Use GitHub's current Copilot inference path rather than the retired GitHub Models endpoint. +### Changed + +- Frame the landing page's three phone captures as a realistic device: metal rim, bezel, status bar, Dynamic Island, and side buttons, sharing one status-bar template and scaling with the phone's width. +- Add a Contact button beside Copy install in the landing page header that opens a prefilled email to the team, and show the cloud terminal mark beside the wordmark. + ## [0.8.1] โ€” 2026-09-04 ### Fixed diff --git a/web/landing.css b/web/landing.css index 326dd37..2965b4b 100644 --- a/web/landing.css +++ b/web/landing.css @@ -318,9 +318,20 @@ } .marketing .wordmark { + display: inline-flex; color: #191b18; font-size: 23px; font-weight: 590; + align-items: center; +} + +.marketing-nav .wordmark-mark { + display: block; + width: 28px; + height: 28px; + margin-right: 9px; + border-radius: 8px; + box-shadow: 0 1px 2px rgb(25 27 24 / 12%); } .marketing .wordmark span { @@ -349,18 +360,22 @@ color: #11130f; } -.nav-install { - min-width: 108px; +.nav-install, +.marketing-nav .marketing-links > a.nav-contact { height: 38px; - padding: 0 15px; border: 1px solid #1d1f1b; border-radius: 999px; + font-size: 12px; + font-weight: 590; + transition: transform 150ms ease, background 150ms ease, color 150ms ease; +} + +.nav-install { + min-width: 108px; + padding: 0 15px; background: #1d1f1b; color: #f8f7f1 !important; cursor: pointer; - font-size: 12px; - font-weight: 590; - transition: transform 150ms ease, background 150ms ease; } .nav-install:hover { @@ -368,6 +383,30 @@ transform: translateY(-1px); } +.marketing-nav .marketing-links > a.nav-contact { + display: inline-flex; + margin-right: -18px; + padding: 0 14px 0 12px; + color: #1d1f1b; + align-items: center; + gap: 8px; +} + +.marketing-nav .marketing-links > a.nav-contact:hover { + background: #1d1f1b; + color: #f8f7f1; + transform: translateY(-1px); +} + +.marketing-links > a.nav-contact svg { + width: 16px; + height: 13px; + fill: none; + stroke: currentColor; + stroke-width: 1.6; + stroke-linejoin: round; +} + .marketing-hero { display: grid; width: min(1240px, calc(100% - 64px)); @@ -598,45 +637,142 @@ } .phone-device { + --phone-scale: 1; + --phone-shadow-far: 0 56px 110px rgb(30 39 24 / 26%); + --phone-shadow-near: 0 14px 30px rgb(30 39 24 / 18%); position: relative; - padding: 9px; - border: 1px solid #30343d; - border-radius: 46px; - background: #111319; + padding: calc(11px * var(--phone-scale)); + border-radius: calc(58px * var(--phone-scale)); + background: + linear-gradient(160deg, #3d414b 0%, #1c1f26 28%, #15171d 60%, #2a2e37 100%); box-shadow: - 0 48px 100px rgb(30 39 24 / 24%), - 0 10px 26px rgb(30 39 24 / 17%), - inset 0 0 0 1px rgb(255 255 255 / 7%); + var(--phone-shadow-far), + var(--phone-shadow-near), + 0 0 0 1px #0a0b0e, + inset 0 0 0 1.5px rgb(255 255 255 / 16%), + inset 0 0 0 4px #0c0d11, + inset 0 0 0 5px rgb(255 255 255 / 4%); } +/* Physical buttons: action + volume on the left, power on the right. */ .phone-device::before, -.phone-device::after { +.phone-device::after, +.phone-volume, +.phone-volume::after { position: absolute; - width: 3px; - border-radius: 3px 0 0 3px; - background: #292d35; + width: 4px; + background: linear-gradient(90deg, #2c3038, #464b55 45%, #23262d); + box-shadow: 0 1px 2px rgb(0 0 0 / 45%); +} + +.phone-device::before, +.phone-device::after, +.phone-volume::after { content: ""; } .phone-device::before { - height: 70px; - top: 118px; + height: calc(30px * var(--phone-scale)); + top: calc(118px * var(--phone-scale)); left: -4px; + border-radius: 3px 0 0 3px; +} + +.phone-volume { + height: calc(56px * var(--phone-scale)); + top: calc(168px * var(--phone-scale)); + left: -4px; + border-radius: 3px 0 0 3px; +} + +.phone-volume::after { + top: calc(68px * var(--phone-scale)); + left: 0; + height: 100%; + border-radius: inherit; } .phone-device::after { - height: 92px; - top: 145px; + height: calc(92px * var(--phone-scale)); + top: calc(200px * var(--phone-scale)); right: -4px; border-radius: 0 3px 3px 0; + background: linear-gradient(270deg, #2c3038, #464b55 45%, #23262d); +} + +.phone-screen { + position: relative; + overflow: hidden; + border-radius: calc(47px * var(--phone-scale)); + background: #11141b; + box-shadow: inset 0 0 0 1px rgb(255 255 255 / 5%); +} + +.phone-statusbar { + display: flex; + position: relative; + height: calc(50px * var(--phone-scale)); + padding: calc(12px * var(--phone-scale)) calc(30px * var(--phone-scale)) 0; + color: #f2f4f7; + font: 600 calc(15px * var(--phone-scale))/1 -apple-system, "SF Pro Text", "Uncut Sans", system-ui, sans-serif; + letter-spacing: -0.01em; + align-items: center; + justify-content: space-between; } +.phone-time { + min-width: calc(44px * var(--phone-scale)); + font-variant-numeric: tabular-nums; +} + +.phone-island { + position: absolute; + top: calc(16px * var(--phone-scale)); + left: 50%; + width: calc(92px * var(--phone-scale)); + height: calc(30px * var(--phone-scale)); + border-radius: 18px; + background: #000; + box-shadow: + inset 0 0 0 1px rgb(255 255 255 / 7%), + 0 0 0 1px rgb(0 0 0 / 60%); + transform: translateX(-50%); +} + +.phone-island::after { + position: absolute; + top: calc(9px * var(--phone-scale)); + right: calc(12px * var(--phone-scale)); + width: calc(12px * var(--phone-scale)); + height: calc(12px * var(--phone-scale)); + border-radius: 50%; + background: radial-gradient(circle at 40% 40%, #1d2433 0 30%, #05070b 70%); + content: ""; +} + +.phone-status-icons { + display: flex; + align-items: center; + gap: calc(6px * var(--phone-scale)); + color: #f2f4f7; +} + +.phone-status-icons svg { display: block; fill: currentColor; } +.phone-signal { width: calc(18px * var(--phone-scale)); height: calc(12px * var(--phone-scale)); } +.phone-wifi { width: calc(16px * var(--phone-scale)); height: calc(12px * var(--phone-scale)); } +.phone-battery { width: calc(27px * var(--phone-scale)); height: calc(12px * var(--phone-scale)); } + .phone-device img { display: block; width: 100%; height: auto; - border-radius: 37px; - background: #0c0f14; + background: #0b0d12; +} + +.phone-device-proof { + --phone-scale: 0.82; + --phone-shadow-far: 0 30px 70px rgb(30 39 24 / 20%); + --phone-shadow-near: 0 10px 22px rgb(30 39 24 / 14%); } .real-phone-demo figcaption { @@ -2058,50 +2194,31 @@ } .demo-proof figure { + display: flex; min-width: 0; margin: 0; - overflow: hidden; - border: 1px solid #30343d; - border-radius: 34px; - background: #111319; - box-shadow: - 0 24px 65px rgb(43 49 37 / 13%), - inset 0 0 0 1px rgb(255 255 255 / 6%); -} - -.phone-proof-screen { - padding: 7px 7px 0; -} - -.demo-proof figure img { - display: block; - width: 100%; - height: auto; - border-radius: 27px 27px 0 0; - background: #0c0f14; + flex-direction: column; + gap: 18px; } .demo-proof figcaption { display: flex; - min-height: 64px; - padding: 12px 15px; - border-top: 1px solid #30343d; - background: #171a20; + padding: 0 6px; flex-direction: column; - justify-content: center; - gap: 4px; + gap: 5px; } .demo-proof figcaption b { - color: #e2e5eb; - font-size: 10px; - font-weight: 580; + color: #20231e; + font-size: 12px; + font-weight: 600; + letter-spacing: -0.01em; } .demo-proof figcaption span { - color: #959ca8; - font-size: 8px; - line-height: 1.4; + color: #6b7066; + font-size: 11px; + line-height: 1.5; } @media (max-width: 1040px) { @@ -2201,11 +2318,25 @@ display: none; } - .marketing-links > a:last-of-type { + .marketing-links > a.nav-github { display: block; font-size: 11px; } + .marketing-nav .wordmark-mark { + width: 24px; + height: 24px; + margin-right: 7px; + border-radius: 7px; + } + + .marketing-nav .marketing-links > a.nav-contact { + height: 35px; + margin-right: -4px; + padding: 0 12px 0 10px; + font-size: 11px; + } + .nav-install { min-width: 96px; height: 35px; @@ -2227,7 +2358,7 @@ font-size: clamp(50px, 15.2vw, 78px); } - .hero-dek { + .marketing .hero-dek { margin-top: 25px; font-size: 16px; } @@ -2261,6 +2392,10 @@ width: min(310px, 88%); } + .real-phone-demo .phone-device { + --phone-scale: 0.92; + } + .browser-window { min-height: 0; border-radius: 13px; @@ -2346,9 +2481,8 @@ gap: 12px; } - .demo-proof figcaption { - min-height: 54px; - padding: 10px; + .phone-device-proof { + --phone-scale: 0.55; } .numbers { @@ -2525,15 +2659,36 @@ } } +@media (max-width: 560px) { + .marketing-nav .wordmark-mark { + display: none; + } + + .marketing-nav .marketing-links > a.nav-contact { + width: 35px; + padding: 0; + justify-content: center; + gap: 0; + } + + .marketing-nav .marketing-links > a.nav-contact span { + display: none; + } +} + @media (max-width: 420px) { .marketing .wordmark { font-size: 20px; } - .marketing-links > a:last-of-type { + .marketing-links > a.nav-github { display: none; } + .real-phone-demo .phone-device { + --phone-scale: 0.75; + } + .hero-copy h1 { font-size: clamp(47px, 14.8vw, 62px); } @@ -2585,6 +2740,10 @@ width: min(255px, 78%); justify-self: center; } + + .phone-device-proof { + --phone-scale: 0.66; + } } @media (prefers-reduced-motion: reduce) { diff --git a/web/main.ts b/web/main.ts index 1b06262..0b0bbb9 100644 --- a/web/main.ts +++ b/web/main.ts @@ -56,6 +56,8 @@ if (!app) throw new Error("Missing app root"); type TerminalColorMode = "dark" | "light"; const TYPING_LEASE_MS = 1_800; const PILOT_PROTOCOL_URL = "https://pilotprotocol.network/"; +const CONTACT_EMAIL = "founders@pilotprotocol.network"; +const CONTACT_MAILTO = `mailto:${CONTACT_EMAIL}?subject=${encodeURIComponent("shell.online")}`; const GITHUB_MARK_PATH = "M8 0C3.58 0 0 3.64 0 8.13c0 3.59 2.29 6.64 5.47 7.71.4.08.55-.18.55-.39 0-.19-.01-.82-.01-1.49-2.01.44-2.53-.5-2.69-.96-.09-.23-.48-.96-.82-1.16-.28-.15-.68-.52-.01-.53.63-.01 1.08.59 1.23.83.72 1.23 1.87.88 2.33.67.07-.53.28-.88.51-1.08-1.78-.21-3.65-.91-3.65-4.02 0-.89.31-1.62.82-2.19-.08-.2-.36-1.04.08-2.16 0 0 .67-.22 2.2.84A7.52 7.52 0 018 3.84a7.5 7.5 0 012 .28c1.53-1.06 2.2-.84 2.2-.84.44 1.12.16 1.96.08 2.16.51.57.82 1.3.82 2.19 0 3.12-1.88 3.81-3.66 4.01.29.25.54.74.54 1.5 0 1.08-.01 1.95-.01 2.22 0 .22.15.47.55.39A8.16 8.16 0 0016 8.13C16 3.64 12.42 0 8 0z"; const OPENAI_MARK_PATH = "M196.4 185.8l0-48.6c0-4.1 1.5-7.2 5.1-9.2l97.8-56.3c13.3-7.7 29.2-11.3 45.6-11.3 61.4 0 100.4 47.6 100.4 98.3 0 3.6 0 7.7-.5 11.8L343.3 111.1c-6.1-3.6-12.3-3.6-18.4 0L196.4 185.8zM424.7 375.2l0-116.2c0-7.2-3.1-12.3-9.2-15.9L287 168.4 329 144.3c3.6-2 6.7-2 10.2 0L437 200.7c28.2 16.4 47.1 51.2 47.1 85 0 38.9-23 74.8-59.4 89.6l0 0zM166.2 272.8l-42-24.6c-3.6-2-5.1-5.1-5.1-9.2l0-112.6c0-54.8 42-96.3 98.8-96.3 21.5 0 41.5 7.2 58.4 20L175.4 108.5c-6.1 3.6-9.2 8.7-9.2 15.9l0 148.5 0 0zm90.4 52.2l-60.2-33.8 0-71.7 60.2-33.8 60.2 33.8 0 71.7-60.2 33.8zm38.7 155.7c-21.5 0-41.5-7.2-58.4-20l100.9-58.4c6.1-3.6 9.2-8.7 9.2-15.9l0-148.5 42.5 24.6c3.6 2 5.1 5.1 5.1 9.2l0 112.6c0 54.8-42.5 96.3-99.3 96.3l0 0zM173.8 366.5L76.1 310.2c-28.2-16.4-47.1-51.2-47.1-85 0-39.4 23.6-74.8 59.9-89.6l0 116.7c0 7.2 3.1 12.3 9.2 15.9l128 74.2-42 24.1c-3.6 2-6.7 2-10.2 0zm-5.6 84c-57.9 0-100.4-43.5-100.4-97.3 0-4.1 .5-8.2 1-12.3l100.9 58.4c6.1 3.6 12.3 3.6 18.4 0l128.5-74.2 0 48.6c0 4.1-1.5 7.2-5.1 9.2l-97.8 56.3c-13.3 7.7-29.2 11.3-45.6 11.3l0 0zm127 60.9c62 0 113.7-44 125.4-102.4 57.3-14.9 94.2-68.6 94.2-123.4 0-35.8-15.4-70.7-43-95.7 2.6-10.8 4.1-21.5 4.1-32.3 0-73.2-59.4-128-128-128-13.8 0-27.1 2-40.4 6.7-23-22.5-54.8-36.9-89.6-36.9-62 0-113.7 44-125.4 102.4-57.3 14.8-94.2 68.6-94.2 123.4 0 35.8 15.4 70.7 43 95.7-2.6 10.8-4.1 21.5-4.1 32.3 0 73.2 59.4 128 128 128 13.8 0 27.1-2 40.4-6.7 23 22.5 54.8 36.9 89.6 36.9z"; @@ -394,6 +396,37 @@ function wireDocumentationControls(kind: DocumentationKind, version: string, con }); } +type PhoneFrameOptions = { + src: string; + alt: string; + proof?: boolean; + loading?: "lazy"; + fetchPriority?: "high"; +}; + +function renderPhoneFrame({ src, alt, proof = false, loading, fetchPriority }: PhoneFrameOptions): string { + const attributes = [ + loading ? `loading="${loading}"` : "", + fetchPriority ? `fetchpriority="${fetchPriority}"` : "", + ].filter(Boolean).join(" "); + return ` +
+ +
+ + ${alt} +
+
`; +} + function renderLanding(): void { const windowsVisitor = /Windows/i.test(navigator.userAgent); const installCommand = windowsVisitor @@ -406,12 +439,16 @@ function renderLanding(): void { app!.innerHTML = `
- shell.online + shell.online