diff --git a/MEDIA_CAPTURE.md b/MEDIA_CAPTURE.md index d879f9d..adabe90 100644 --- a/MEDIA_CAPTURE.md +++ b/MEDIA_CAPTURE.md @@ -1,40 +1,40 @@ # GroundControl marketing media -The public site uses real product captures only. Do not add invented dashboards, fake metrics, synthetic terminal output, or decorative UI mockups presented as product state. +The public site uses real product evidence only. Do not add invented dashboards, fake metrics, synthetic terminal output, or decorative UI mockups presented as product state. -## Current capture set — September 2026 +## Visible proof set — September 2026 -The visible marketing page now uses captures synced directly from the current GroundControl product repository: +The live public page is deliberately using fresh evidence from the current agent-native rollout: -- `current-dashboard.png` -- `current-containers.png` -- `current-terminal.png` -- `current-topology.png` -- `current-infrastructure.png` +- `agent-access.webp` — GroundControl's current Agents workspace showing ChatGPT authorized through OAuth with the granted deployment scopes. +- `redeploy-proof.webp` — ChatGPT reporting a real GroundControl durable redeploy operation, operation ID and verification evidence. -These replace the August product screenshots in the visible page. Older files may remain in Git history, but should not be reintroduced unless they still match the current product. +These are used inside the animated product-proof sequence and the live-acceptance section. -## Recent product evidence worth capturing next +The previous `current-dashboard.png`, `current-containers.png`, `current-terminal.png`, `current-topology.png`, and `current-infrastructure.png` aliases were removed because they were captures of an older interface and should not be presented as current. -1. **ChatGPT + GroundControl MCP** - - connected custom app - - scoped actions visible - - live deployment health result +Older media may remain in Git history or as unreferenced archival assets, but must not be reintroduced into the visible page unless it still matches the live product. -2. **Durable redeploy** - - operation id returned from ChatGPT - - GroundControl verification evidence - - successful public HTTP verification +## Next proof worth capturing -3. **Connector capability health** - - GitHub source/read/webhook/GHCR capabilities - - Daytona sandbox lifecycle + exact-revision acceptance +1. **Connect Repo** + - current GitHub connector surface + - account/repository selection + - explicit deployment ↔ repository identity -4. **Agent-assisted installation** - - structured `--json` installer output +2. **Connector capability health** + - GitHub source/webhook/GHCR capability states + - Daytona exact-revision acceptance evidence + +3. **Agent-assisted installation** + - structured installer `--json` output - one-time claim page - - post-claim private operator login - - eventual HTTPS/domain setup + - publish/domain step + - final HTTPS + MCP verification + +4. **Native operator PTY** + - only after a current capture is taken from the post-PTY redesign + - harmless command output, no secrets ## Editing rules @@ -42,5 +42,5 @@ These replace the August product screenshots in the visible page. Older files ma - Crop for focus, never to manufacture a different product state. - Redact secrets rather than replacing them with fictional values. - Never fabricate success states, counts or metrics. -- Keep typography and UI untouched inside screenshots. -- Replace visible captures when the product surface materially changes. +- Keep typography and product UI untouched inside screenshots. +- Replace visible captures whenever the product surface materially changes. diff --git a/app/globals.css b/app/globals.css index 3bd4f1a..c5bdb67 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,215 +1,517 @@ @import "tailwindcss"; :root { - --ink: #f5f7f5; - --muted: #9aa29b; - --dim: #6d756e; - --line: #252b27; - --line-strong: #343c36; - --black: #090b0a; - --deep: #0d100e; - --panel: #111512; - --panel-soft: #151a16; - --accent: #6d7df0; - --accent-bright: #8794ff; - --success: #77d39c; + --bg: #202427; + --dark: #141618; + --darker: #0d0e10; + --deep: #101214; + --text: #f5f6f7; + --muted: rgba(245, 246, 247, 0.48); + --dim: rgba(245, 246, 247, 0.25); + --line: rgba(245, 246, 247, 0.085); + --line-strong: rgba(245, 246, 247, 0.16); + --accent: #4e5fd5; + --accent-bright: #7181ed; + --success: #94d85a; } * { box-sizing: border-box; } -html { scroll-behavior: smooth; background: var(--black); } +html { scroll-behavior: smooth; background: var(--darker); } body { margin: 0; - background: var(--black); - color: var(--ink); - font-family: var(--font-geist-sans), Arial, sans-serif; + background: var(--bg); + color: var(--text); + font-family: var(--font-geist-sans), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -webkit-font-smoothing: antialiased; + overflow-x: hidden; } a { color: inherit; text-decoration: none; } -button, a { -webkit-tap-highlight-color: transparent; } button { font: inherit; } +button, a { -webkit-tap-highlight-color: transparent; } -.section-shell { width: min(1220px, calc(100% - 48px)); margin: 0 auto; } -.eyebrow { margin: 0; color: var(--dim); font: 600 10px/1.4 var(--font-geist-mono); letter-spacing: .14em; } +.section-shell { width: min(1180px, calc(100% - 48px)); margin-inline: auto; } +.eyebrow { + margin: 0; + color: var(--dim); + font: 600 10px/1.4 var(--font-geist-mono); + letter-spacing: .16em; + text-transform: uppercase; +} .site-header { - position: sticky; - z-index: 30; - top: 0; - height: 70px; + position: fixed; + inset: 0 0 auto; + z-index: 50; + height: 72px; display: flex; align-items: center; justify-content: space-between; - padding: 0 clamp(24px, 4vw, 64px); - background: rgba(9, 11, 10, .9); - border-bottom: 1px solid var(--line); - backdrop-filter: blur(18px); + padding: 0 clamp(20px, 4vw, 60px); + border-bottom: 1px solid rgba(245,246,247,.06); + background: linear-gradient(180deg, rgba(13,14,16,.88), rgba(13,14,16,.38), transparent); + backdrop-filter: blur(14px); +} +.wordmark { display: inline-flex; align-items: center; gap: 11px; font: 580 12px/1 var(--font-geist-mono); } +.orbit-mark { + position: relative; + width: 28px; + height: 28px; + display: block; + border: 2px solid rgba(245,246,247,.72); + border-right-color: transparent; + border-radius: 50%; + transform: rotate(28deg); +} +.orbit-mark::before { + position: absolute; + width: 7px; + height: 7px; + top: -4px; + right: 1px; + border-radius: 50%; + background: var(--accent-bright); + content: ""; +} +.orbit-mark i { + position: absolute; + inset: 6px; + border: 1px solid rgba(245,246,247,.28); + border-left-color: transparent; + border-radius: 50%; } -.wordmark { display: inline-flex; align-items: center; gap: 10px; font: 650 12px/1 var(--font-geist-mono); letter-spacing: .04em; } -.brand-mark { width: 28px; height: 28px; display: inline-grid; place-items: center; border: 1px solid var(--line-strong); color: var(--accent-bright); font: 700 8px/1 var(--font-geist-mono); } .site-header nav { display: flex; align-items: center; gap: clamp(16px, 3vw, 34px); } -.site-header nav a, footer a { color: #aeb5af; font: 550 10px/1 var(--font-geist-mono); letter-spacing: .04em; transition: color .2s; } +.site-header nav a, footer a { + color: rgba(245,246,247,.52); + font: 520 10px/1 var(--font-geist-mono); + transition: color .2s ease; +} .site-header nav a:hover, footer a:hover { color: white; } -.hero { - min-height: calc(100svh - 70px); - padding-top: clamp(72px, 10vw, 140px); - padding-bottom: 90px; - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(420px, .88fr); - gap: clamp(42px, 7vw, 96px); +.hero-private { + position: relative; + min-height: 100svh; + display: flex; + align-items: center; + overflow: hidden; + background: var(--dark); +} +.ambient-mesh { position: absolute; inset: 0; overflow: hidden; } +.ambient-mesh::before { + position: absolute; + inset: -15%; + background: + radial-gradient(ellipse 62% 56% at 21% 35%, rgba(78,95,213,.38), transparent 62%), + radial-gradient(ellipse 54% 48% at 80% 72%, rgba(78,95,213,.2), transparent 60%), + radial-gradient(ellipse 42% 38% at 54% 106%, rgba(245,246,247,.07), transparent 56%), + linear-gradient(155deg, #0d0e10 0%, #141618 46%, #202427 100%); + filter: saturate(.88); + animation: meshDrift 16s ease-in-out infinite alternate; + content: ""; +} +.ambient-orb { + position: absolute; + width: min(58vw, 760px); + aspect-ratio: 1; + border-radius: 50%; + filter: blur(90px); + opacity: .16; + background: var(--accent); +} +.ambient-orb--one { left: -12%; top: -30%; animation: orbOne 12s ease-in-out infinite alternate; } +.ambient-orb--two { right: -18%; bottom: -40%; animation: orbTwo 14s ease-in-out infinite alternate; } +.ambient-grain { + position: absolute; + inset: 0; + opacity: .055; + mix-blend-mode: overlay; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 220 220' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.88' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.8'/%3E%3C/svg%3E"); +} +.hero-shade { + position: absolute; + inset: 0; + background: + linear-gradient(90deg, rgba(13,14,16,.9) 0%, rgba(13,14,16,.64) 42%, rgba(13,14,16,.22) 72%, transparent 100%), + linear-gradient(0deg, rgba(13,14,16,.7), transparent 35%); +} +.hero-private-inner { + position: relative; + z-index: 3; + width: min(1180px, calc(100% - 48px)); + margin: 0 auto; + padding: 130px 0 110px; +} +.hero-kicker { margin-bottom: 28px; animation: fadeRise .8s .25s both; } +.hero-private h1 { + max-width: 880px; + margin: 0; + font-size: clamp(52px, 7.2vw, 108px); + font-weight: 500; + line-height: .96; + letter-spacing: -.055em; +} +.line-mask { display: block; overflow: hidden; padding-bottom: .04em; } +.line-inner { display: block; transform: translateY(105%); animation: lineReveal 1.35s .32s cubic-bezier(.7,0,.2,1) forwards; } +.line-mask:nth-child(2) .line-inner { animation-delay: .48s; } +.line-inner--accent { color: var(--accent-bright); } +.hero-private-copy { + max-width: 620px; + margin: 34px 0 0; + color: var(--muted); + font-size: clamp(16px, 1.55vw, 20px); + line-height: 1.7; +} +.fade-in-seq { opacity: 0; transform: translateY(18px); animation: fadeRise .75s .76s ease forwards; } +.hero-actions { margin-top: 34px; display: flex; flex-wrap: wrap; gap: 11px; } +.hero-actions.fade-in-seq { animation-delay: .87s; } +.hero-notes { margin-top: 28px; display: flex; flex-wrap: wrap; gap: 8px; } +.hero-notes.fade-in-seq { animation-delay: .96s; } +.hero-notes span { + padding: 7px 10px; + border: 1px solid var(--line); + color: var(--dim); + font: 540 8px/1 var(--font-geist-mono); + letter-spacing: .06em; + text-transform: uppercase; +} +.button { + min-height: 48px; + display: inline-flex; align-items: center; + justify-content: center; + gap: 16px; + padding: 0 20px; + border: 1px solid var(--line-strong); + border-radius: 8px; + font: 590 10px/1 var(--font-geist-mono); + cursor: pointer; + transition: transform .2s ease, background .2s ease, border-color .2s ease; } -.hero-copy-block { max-width: 720px; } -.hero h1 { margin: 23px 0 28px; font-size: clamp(48px, 6.1vw, 88px); line-height: .97; letter-spacing: -.06em; font-weight: 520; } -.hero-copy { max-width: 680px; margin: 0; color: var(--muted); font-size: clamp(17px, 1.6vw, 21px); line-height: 1.62; } -.hero-actions { margin-top: 36px; display: flex; flex-wrap: wrap; gap: 10px; } -.button { min-height: 50px; display: inline-flex; align-items: center; justify-content: center; gap: 16px; padding: 0 20px; border: 1px solid var(--line-strong); font: 650 10px/1 var(--font-geist-mono); letter-spacing: .045em; transition: transform .2s, background .2s, border-color .2s; } .button:hover { transform: translateY(-2px); } .button--primary { background: var(--accent); border-color: var(--accent); color: white; } .button--primary:hover { background: var(--accent-bright); border-color: var(--accent-bright); } -.button--secondary { background: transparent; color: #c9cfca; } -.button--secondary:hover { border-color: #59625b; background: rgba(255,255,255,.025); } -.hero-notes { margin-top: 34px; display: flex; flex-wrap: wrap; gap: 8px; } -.hero-notes span { padding: 7px 9px; border: 1px solid var(--line); color: var(--dim); font: 550 8px/1 var(--font-geist-mono); letter-spacing: .06em; text-transform: uppercase; } - -.hero-media { margin: 0; border: 1px solid var(--line-strong); background: var(--deep); overflow: hidden; box-shadow: 0 28px 80px rgba(0,0,0,.35); } -.hero-media img { width: 100%; height: auto; display: block; } -.hero-media figcaption { padding: 18px 20px 20px; border-top: 1px solid var(--line); color: var(--muted); font-size: 13px; line-height: 1.55; } -.hero-media figcaption strong { color: var(--ink); font-weight: 600; } - -.proof-band { padding: 100px 0; background: var(--panel-soft); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } -.proof-grid { display: grid; grid-template-columns: minmax(160px, .55fr) minmax(0, 1.45fr); gap: 48px; } -.proof-grid h2, .section-heading h2, .ownership-grid h2, .final-cta h2 { margin: 0; font-size: clamp(38px, 5vw, 68px); line-height: 1.01; letter-spacing: -.052em; font-weight: 500; } -.proof-grid p:not(.eyebrow), .section-heading > p, .section-heading--split > p, .workflow-copy > p:not(.eyebrow), .ownership-copy > p:not(.eyebrow) { color: var(--muted); font-size: 16px; line-height: 1.72; } -.proof-grid h2 + p { max-width: 800px; margin: 24px 0 0; } - -.features, .templates, .install-section, .evidence-section { padding: 130px 0; } -.section-heading { max-width: 900px; margin-bottom: 58px; } -.section-heading h2 { margin-top: 18px; } -.section-heading--split { max-width: none; display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(280px, .75fr); gap: 64px; align-items: end; } -.section-heading--split > p { margin: 0; max-width: 520px; } +.button--secondary { background: rgba(13,14,16,.2); color: rgba(245,246,247,.78); } +.button--secondary:hover { background: rgba(245,246,247,.04); border-color: rgba(245,246,247,.26); } +.scroll-cue { + position: absolute; + z-index: 4; + bottom: 27px; + left: 50%; + display: inline-flex; + flex-direction: column; + align-items: center; + gap: 9px; + color: var(--dim); + font: 500 8px/1 var(--font-geist-mono); + letter-spacing: .1em; + text-transform: uppercase; + transform: translateX(-50%); +} +.scroll-cue span { width: 1px; height: 35px; background: linear-gradient(var(--accent-bright), transparent); animation: cue 1.8s ease-in-out infinite; } -.evidence-feature, .evidence-card, .workflow-media, .template-capture, .terminal-capture { +.statement { + min-height: 70svh; + display: grid; + grid-template-columns: minmax(150px,.42fr) minmax(0,1.58fr); + gap: 58px; + align-items: center; + padding-block: 120px; +} +.statement h2, .section-heading h2, .fresh-proof h2, .tools-layout h2, .final-cta h2 { margin: 0; - border: 1px solid var(--line-strong); - background: var(--deep); + font-size: clamp(38px, 5.6vw, 76px); + font-weight: 360; + line-height: 1.04; + letter-spacing: -.05em; +} +.statement p:not(.eyebrow), .section-heading > p, .fresh-proof-copy > p:not(.eyebrow), .tools-layout > div > p:not(.eyebrow), .final-cta > p:not(.eyebrow) { + max-width: 760px; + color: var(--muted); + font-size: 16px; + line-height: 1.75; +} +.statement h2 + p { margin-top: 28px; } + +[data-reveal] { + opacity: 0; + transform: translateY(28px); + transition: opacity .9s ease, transform .9s cubic-bezier(.2,.75,.25,1); +} +[data-reveal].is-visible { opacity: 1; transform: translateY(0); } + +.motion-grid-section { + position: relative; + height: 310vh; + background: #0f1112; +} +.motion-grid-pin { + position: sticky; + top: 0; + height: 100svh; + overflow: hidden; + background: #0f1112; +} +.motion-grid-lines { + position: absolute; + inset: 0; + opacity: .5; + background-image: + linear-gradient(rgba(245,246,247,.075) 1px, transparent 1px), + linear-gradient(90deg, rgba(245,246,247,.075) 1px, transparent 1px); + background-size: 17vw 16.66vh; +} +.motion-grid-lines::after { + position: absolute; + inset: 0; + background: radial-gradient(circle at 74% 54%, rgba(78,95,213,.14), transparent 35%); + content: ""; +} +.motion-grid-copy { + position: absolute; + left: 8%; + top: 19%; + z-index: 3; + width: min(560px, 40vw); +} +.motion-grid-copy h2 { + margin: 18px 0 0; + font-size: clamp(36px, 4.5vw, 66px); + font-weight: 360; + line-height: 1.02; + letter-spacing: -.05em; +} +.motion-grid-copy p:last-child { margin-top: 22px; color: var(--muted); font-size: 15px; line-height: 1.7; } +.motion-image-shell { + position: absolute; + z-index: 4; + border: 1px solid rgba(245,246,247,.22); + background: #090b0a; overflow: hidden; + box-shadow: 0 28px 90px rgba(0,0,0,.42); + will-change: left, top, width, height; +} +.motion-image-stack { position: absolute; inset: 0; display: grid; place-items: center; overflow: hidden; } +.motion-shot { + position: absolute; + height: 100%; + width: 100%; + object-fit: contain; + transition: opacity .12s linear; +} +.motion-shot--second { opacity: 0; } +.motion-vignette { + position: absolute; + inset: 0; + background: linear-gradient(90deg, rgba(9,11,10,.68), transparent 36%, transparent 64%, rgba(9,11,10,.62)); + pointer-events: none; +} +.motion-grid-final { + position: absolute; + z-index: 6; + left: 10%; + right: 10%; + bottom: 8%; + text-align: center; + pointer-events: none; +} +.motion-grid-final h3 { + max-width: 900px; + margin: 14px auto 0; + font-size: clamp(30px, 4.4vw, 62px); + font-weight: 380; + line-height: 1.04; + letter-spacing: -.045em; + text-shadow: 0 3px 24px #000; +} +.motion-metrics { margin-top: 25px; display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; } +.motion-metrics span { + padding: 10px 12px; + border: 1px solid rgba(245,246,247,.14); + background: rgba(13,14,16,.76); + color: var(--muted); + font: 500 9px/1.4 var(--font-geist-mono); +} +.motion-metrics strong { color: white; font-weight: 600; } + +.fresh-proof { + min-height: 100svh; + display: grid; + grid-template-columns: minmax(0,1.1fr) minmax(300px,.55fr); + gap: clamp(48px,8vw,120px); + align-items: center; + padding-block: 130px; +} +.fresh-proof-copy h2 { margin-top: 18px; } +.fresh-proof-copy > p:not(.eyebrow) { margin-top: 28px; } +.proof-facts { margin-top: 34px; display: grid; gap: 10px; } +.proof-facts span { color: var(--muted); font: 500 10px/1.5 var(--font-geist-mono); } +.proof-facts strong { display: inline-block; min-width: 90px; color: var(--success); } +.phone-proof { + margin: 0; + justify-self: center; + width: min(100%, 360px); + padding: 12px; + border: 1px solid var(--line-strong); + border-radius: 28px; + background: #080a09; + box-shadow: 0 32px 100px rgba(0,0,0,.38); +} +.phone-proof img { display: block; width: 100%; height: auto; border-radius: 18px; } +.phone-proof figcaption { padding: 13px 6px 4px; color: var(--dim); font: 500 8px/1.5 var(--font-geist-mono); text-align: center; } + +.capabilities, .connectors, .install-section { padding-block: 130px; } +.section-heading { max-width: 900px; margin-bottom: 55px; } +.section-heading h2 { margin-top: 18px; } +.section-heading > p { margin-top: 26px; } +.section-heading--split { max-width: none; display: grid; grid-template-columns: 1.2fr .8fr; gap: 70px; align-items: end; } +.section-heading--split > p { margin: 0; } +.capability-grid { + display: grid; + grid-template-columns: repeat(3,1fr); + border-left: 1px solid var(--line); + border-top: 1px solid var(--line); +} +.capability-grid article { + min-height: 270px; + display: flex; + flex-direction: column; + padding: 30px; + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); + background: var(--darker); } -.evidence-feature img, .evidence-card img, .workflow-media img, .template-capture img, .terminal-capture img { width: 100%; height: auto; display: block; } -.evidence-feature figcaption, .evidence-card figcaption, .workflow-media figcaption, .template-capture figcaption, .terminal-capture figcaption { border-top: 1px solid var(--line); } -.evidence-feature figcaption { - min-height: 112px; +.capability-index { color: var(--dim); font: 500 9px/1 var(--font-geist-mono); } +.capability-grid h3 { margin: 40px 0 14px; font-size: 23px; font-weight: 420; letter-spacing: -.03em; } +.capability-grid p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.7; } +.capability-grid small { margin-top: auto; padding-top: 28px; color: var(--dim); font: 500 8px/1.4 var(--font-geist-mono); text-transform: uppercase; letter-spacing: .06em; } + +.tools-band { padding: 120px 0; background: var(--darker); border-block: 1px solid var(--line); } +.tools-layout { display: grid; grid-template-columns: .85fr 1.15fr; gap: 90px; align-items: start; } +.tools-layout h2 { margin-top: 18px; } +.tools-layout > div > p:not(.eyebrow) { margin-top: 24px; } +.tool-list { border-top: 1px solid var(--line-strong); } +.tool-list > div { display: grid; grid-template-columns: 190px 1fr; gap: 28px; padding: 18px 0; border-bottom: 1px solid var(--line); } +.tool-list code { color: var(--accent-bright); font: 550 10px/1.5 var(--font-geist-mono); } +.tool-list span { color: var(--muted); font-size: 13px; line-height: 1.55; } + +.connector-flow { display: grid; - grid-template-columns: 180px minmax(0, 1fr) auto; + grid-template-columns: repeat(7, auto); align-items: center; - gap: 28px; - padding: 24px 26px; -} -.evidence-feature figcaption span, .evidence-card figcaption span { color: var(--accent-bright); font: 600 9px/1.5 var(--font-geist-mono); letter-spacing: .05em; text-transform: uppercase; } -.evidence-feature figcaption strong, .evidence-card figcaption strong { font-size: 16px; line-height: 1.45; font-weight: 520; } -.evidence-feature figcaption small, .evidence-card figcaption small { color: var(--dim); font: 500 9px/1.55 var(--font-geist-mono); } -.evidence-grid { margin-top: 18px; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; } -.evidence-card figcaption { min-height: 160px; display: grid; grid-template-columns: 1fr; align-content: start; gap: 9px; padding: 22px 24px 24px; } - -.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--line-strong); border-left: 1px solid var(--line-strong); } -.feature-grid article { min-height: 260px; padding: 30px; display: flex; flex-direction: column; border-right: 1px solid var(--line-strong); border-bottom: 1px solid var(--line-strong); background: var(--deep); } -.feature-grid h3 { margin: 0 0 16px; font-size: 25px; letter-spacing: -.035em; font-weight: 520; } -.feature-grid p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.68; } -.feature-grid small { margin-top: auto; padding-top: 28px; color: var(--dim); font: 550 8px/1.45 var(--font-geist-mono); text-transform: uppercase; letter-spacing: .065em; } - -.workflow-section { padding: 120px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: #0b0e0c; } -.workflow-grid { display: grid; grid-template-columns: minmax(0, .9fr) minmax(420px, 1.1fr); gap: clamp(56px, 8vw, 110px); align-items: start; } -.workflow-copy { margin: 0; position: sticky; top: 112px; } -.workflow-copy > p:not(.eyebrow) { max-width: 600px; margin: 24px 0 0; } -.workflow-media { margin-top: 36px; } -.workflow-media figcaption { padding: 15px 17px; color: var(--muted); font: 500 9px/1.5 var(--font-geist-mono); } -.workflow-steps { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line-strong); } -.workflow-steps li { min-height: 150px; display: grid; grid-template-columns: 54px 1fr; gap: 24px; padding: 28px 0; border-bottom: 1px solid var(--line); } -.workflow-steps li > span { color: var(--accent-bright); font: 600 9px/1.4 var(--font-geist-mono); } -.workflow-steps strong { display: block; margin-bottom: 9px; font-size: 22px; font-weight: 520; letter-spacing: -.025em; } -.workflow-steps p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.65; } - -.template-grid { display: grid; grid-template-columns: 1fr 1fr; border-top: 1px solid var(--line-strong); } -.template-capture { margin-bottom: 58px; } -.template-capture figcaption, .terminal-capture figcaption { padding: 17px 20px 19px; color: var(--muted); font-size: 13px; line-height: 1.55; } -.template-capture figcaption strong, .terminal-capture figcaption strong { color: var(--ink); font-weight: 600; } -.template-grid article { padding: 30px 0; border-bottom: 1px solid var(--line); } -.template-grid article:nth-child(odd) { padding-right: 32px; border-right: 1px solid var(--line); } -.template-grid article:nth-child(even) { padding-left: 32px; } -.template-topline { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; } -.template-grid h3 { margin: 0; font-size: 22px; font-weight: 520; letter-spacing: -.03em; } -.template-grid code { color: var(--accent-bright); font: 500 8px/1.4 var(--font-geist-mono); text-align: right; } -.template-grid p { margin: 18px 0 0; color: var(--muted); font-size: 14px; line-height: 1.68; max-width: 530px; } - -.ownership-section { padding: 120px 0; background: var(--panel-soft); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } -.ownership-grid { display: grid; grid-template-columns: minmax(320px, .78fr) minmax(0, 1.22fr); gap: clamp(48px, 7vw, 96px); align-items: center; } -.ownership-copy { min-width: 0; } -.ownership-grid h2 { max-width: 920px; } -.ownership-copy h2 { margin-top: 18px; } -.ownership-copy > p:last-child { max-width: 620px; margin: 28px 0 0; } -.terminal-capture code { color: var(--success); font: 500 .95em/1 var(--font-geist-mono); } - -.install-console { margin-top: 58px; border: 1px solid var(--line-strong); background: #080a09; } -.install-tabs { display: grid; grid-template-columns: repeat(3, 1fr); border-bottom: 1px solid var(--line); } -.install-tab { min-height: 78px; padding: 17px 20px; border: 0; border-right: 1px solid var(--line); background: transparent; color: var(--dim); text-align: left; cursor: pointer; } + justify-content: start; + gap: 16px; + overflow-x: auto; + padding-bottom: 8px; +} +.connector-flow span { + white-space: nowrap; + padding: 16px 20px; + border: 1px solid var(--line-strong); + background: var(--darker); + font: 520 11px/1 var(--font-geist-mono); +} +.connector-flow i { color: var(--dim); font-style: normal; } + +.install-console { border: 1px solid var(--line-strong); background: #080a09; } +.install-tabs { display: grid; grid-template-columns: repeat(3,1fr); border-bottom: 1px solid var(--line); } +.install-tab { + min-height: 78px; + padding: 17px 20px; + border: 0; + border-right: 1px solid var(--line); + background: transparent; + color: var(--dim); + text-align: left; + cursor: pointer; +} .install-tab:last-child { border-right: 0; } -.install-tab span { display: block; color: inherit; font: 600 10px/1 var(--font-geist-mono); } +.install-tab span { display: block; font: 600 10px/1 var(--font-geist-mono); } .install-tab small { display: block; margin-top: 8px; font: 500 9px/1 var(--font-geist-mono); } -.install-tab--active { background: var(--panel); color: var(--ink); box-shadow: inset 0 2px 0 var(--accent-bright); } -.command-shell { min-height: 110px; padding: 24px; display: grid; grid-template-columns: auto 1fr auto; gap: 14px; align-items: start; } -.prompt { color: var(--success); font: 600 12px/1.6 var(--font-geist-mono); } -.command-shell code { min-width: 0; color: #c8cec9; font: 500 12px/1.65 var(--font-geist-mono); overflow-wrap: anywhere; } -.command-shell button { border: 1px solid var(--line-strong); background: transparent; color: var(--muted); padding: 8px 10px; cursor: pointer; font: 600 8px/1 var(--font-geist-mono); } -.command-shell button:hover { color: white; border-color: #59625b; } - -.final-cta { padding: 110px 0 125px; text-align: center; } -.final-cta h2 { max-width: 900px; margin: 20px auto 0; } -.final-cta-copy { max-width: 700px; margin: 24px auto 0; color: var(--muted); font-size: 16px; line-height: 1.72; } +.install-tab--active { background: var(--deep); color: var(--text); box-shadow: inset 0 2px 0 var(--accent-bright); } +.command-shell { + min-height: 118px; + display: grid; + grid-template-columns: auto 1fr auto; + gap: 14px; + align-items: start; + padding: 26px; +} +.prompt { color: var(--success); font: 600 12px/1.65 var(--font-geist-mono); } +.command-shell code { min-width: 0; color: rgba(245,246,247,.77); font: 500 12px/1.65 var(--font-geist-mono); overflow-wrap: anywhere; } +.command-shell button { padding: 8px 10px; border: 1px solid var(--line-strong); background: transparent; color: var(--muted); font: 600 8px/1 var(--font-geist-mono); cursor: pointer; } +.install-steps { margin-top: 20px; display: grid; grid-template-columns: repeat(4,1fr); border-left: 1px solid var(--line); border-top: 1px solid var(--line); } +.install-steps article { min-height: 155px; padding: 22px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--darker); } +.install-steps article > span { color: var(--accent-bright); font: 550 9px/1 var(--font-geist-mono); } +.install-steps strong { display: block; margin-top: 24px; font-size: 16px; font-weight: 500; } +.install-steps p { margin: 8px 0 0; color: var(--muted); font-size: 12px; line-height: 1.55; } + +.final-cta { padding-block: 130px; text-align: center; } +.final-cta h2 { max-width: 930px; margin: 18px auto 0; } +.final-cta > p:not(.eyebrow) { margin: 26px auto 0; } .final-cta .hero-actions { justify-content: center; } -footer { min-height: 110px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 30px; border-top: 1px solid var(--line); } +footer { + min-height: 110px; + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 30px; + align-items: center; + border-top: 1px solid var(--line); +} footer p { margin: 0; color: var(--dim); font-size: 11px; text-align: center; } -footer > div { justify-self: end; display: flex; gap: 24px; } +footer > div { justify-self: end; display: flex; gap: 22px; } + +@keyframes lineReveal { to { transform: translateY(0); } } +@keyframes fadeRise { to { opacity: 1; transform: translateY(0); } } +@keyframes meshDrift { from { transform: translate3d(-1%, -1%, 0) scale(1.02); } to { transform: translate3d(2%, 1%, 0) scale(1.08); } } +@keyframes orbOne { to { transform: translate3d(18%, 14%, 0) scale(1.16); } } +@keyframes orbTwo { to { transform: translate3d(-18%, -10%, 0) scale(.92); } } +@keyframes cue { 0%,100% { opacity: .3; transform: scaleY(.6); transform-origin: top; } 50% { opacity: 1; transform: scaleY(1); } } -@media (max-width: 980px) { - .hero { grid-template-columns: 1fr; min-height: auto; padding-top: 90px; } - .hero-copy-block { max-width: 820px; } - .hero-media { max-width: 820px; } - .evidence-feature figcaption { grid-template-columns: 130px minmax(0, 1fr); } - .evidence-feature figcaption small { grid-column: 2; } - .feature-grid { grid-template-columns: 1fr 1fr; } - .workflow-grid { grid-template-columns: 1fr; } - .workflow-copy { position: static; } - .ownership-grid { grid-template-columns: 1fr; } +@media (max-width: 900px) { + .statement, .fresh-proof, .tools-layout, .section-heading--split { grid-template-columns: 1fr; } + .statement { gap: 25px; min-height: auto; } + .fresh-proof { min-height: auto; } + .capability-grid { grid-template-columns: 1fr 1fr; } + .install-steps { grid-template-columns: 1fr 1fr; } + .tools-layout { gap: 50px; } } @media (max-width: 720px) { - .section-shell { width: min(100% - 32px, 1220px); } + .section-shell, .hero-private-inner { width: min(100% - 32px, 1180px); } .site-header { height: 64px; padding: 0 16px; } .site-header nav a:nth-child(1), .site-header nav a:nth-child(2) { display: none; } - .hero { padding-top: 70px; padding-bottom: 70px; } - .hero h1 { font-size: clamp(44px, 13vw, 62px); } - .proof-grid, .section-heading--split { grid-template-columns: 1fr; gap: 24px; } - .features, .templates, .install-section, .evidence-section { padding: 90px 0; } - .feature-grid, .template-grid, .evidence-grid { grid-template-columns: 1fr; } - .evidence-feature figcaption { min-height: 0; grid-template-columns: 1fr; gap: 9px; padding: 20px; } - .evidence-feature figcaption small { grid-column: 1; } - .evidence-card figcaption { min-height: 0; } - .feature-grid article { min-height: 230px; } - .template-grid article:nth-child(odd), .template-grid article:nth-child(even) { padding: 26px 0; border-right: 0; } - .template-topline { display: block; } - .template-grid code { display: block; margin-top: 9px; text-align: left; } - .workflow-section, .ownership-section, .proof-band { padding: 86px 0; } - .template-capture { margin-bottom: 40px; } + .hero-private-inner { padding-top: 110px; padding-bottom: 100px; } + .hero-private h1 { font-size: clamp(48px, 14vw, 72px); } + .hero-shade { background: linear-gradient(0deg, rgba(13,14,16,.75), rgba(13,14,16,.2)); } + .statement, .fresh-proof, .capabilities, .connectors, .install-section { padding-block: 90px; } + .motion-grid-section { height: auto; padding: 84px 0; } + .motion-grid-pin { position: relative; height: auto; min-height: 820px; overflow: hidden; } + .motion-grid-copy { left: 7%; top: 6%; width: 86%; opacity: 1 !important; transform: none !important; } + .motion-grid-copy h2 { font-size: 38px; } + .motion-image-shell { + left: 9% !important; + top: 35% !important; + width: 82% !important; + height: 50% !important; + } + .motion-shot:first-child { opacity: 0 !important; } + .motion-shot--second { opacity: 1 !important; } + .motion-grid-final { left: 7%; right: 7%; bottom: 5%; opacity: 1 !important; } + .motion-grid-final h3 { display: none; } + .motion-metrics { gap: 6px; } + .capability-grid, .install-steps { grid-template-columns: 1fr; } + .tool-list > div { grid-template-columns: 1fr; gap: 7px; } .install-tabs { grid-template-columns: 1fr; } - .install-tab, .install-tab:last-child { border-right: 0; border-bottom: 1px solid var(--line); } + .install-tab { border-right: 0; border-bottom: 1px solid var(--line); } .command-shell { grid-template-columns: auto 1fr; } .command-shell button { grid-column: 2; justify-self: start; } - footer { grid-template-columns: 1fr; padding: 34px 0; text-align: center; } + footer { grid-template-columns: 1fr; padding-block: 34px; text-align: center; } footer .wordmark, footer > div { justify-self: center; } } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - *, *::before, *::after { transition-duration: .01ms !important; } + *, *::before, *::after { animation: none !important; transition-duration: .01ms !important; } + .line-inner, .fade-in-seq { opacity: 1 !important; transform: none !important; } + [data-reveal] { opacity: 1 !important; transform: none !important; } } diff --git a/app/page.test.tsx b/app/page.test.tsx index cd2a3d9..60692b7 100644 --- a/app/page.test.tsx +++ b/app/page.test.tsx @@ -14,19 +14,28 @@ describe("Home page", () => { expect(screen.getByText(/single tenant/i)).toBeInTheDocument(); }); - it("does not send visitors into the private operator login", () => { + it("keeps the public site separate from the private operator login", () => { render(); const links = screen.getAllByRole("link"); - expect(links.some((link) => link.getAttribute("href") === "https://groundcontrol.serendepify.com")).toBe(false); + expect( + links.some((link) => link.getAttribute("href") === "https://groundcontrol.serendepify.com"), + ).toBe(false); }); - it("uses current GroundControl product captures", () => { + it("uses the fresh September MCP and durable-operation proof", () => { render(); - expect(screen.getByAltText(/current GroundControl dashboard/i)).toBeInTheDocument(); - expect(screen.getByAltText(/current GroundControl infrastructure/i)).toBeInTheDocument(); - expect(screen.getByAltText(/current GroundControl topology/i)).toBeInTheDocument(); - expect(screen.getByAltText(/current GroundControl runtime containers/i)).toBeInTheDocument(); - expect(screen.getByAltText(/current GroundControl native PTY terminal/i)).toBeInTheDocument(); + expect( + screen.getByAltText(/active ChatGPT OAuth grant with scoped deployment capabilities/i), + ).toBeInTheDocument(); + expect( + screen.getByAltText(/successful GroundControl redeploy with an operation ID/i), + ).toBeInTheDocument(); + expect( + screen.getByAltText(/successful GroundControl redeploy evidence returned inside ChatGPT/i), + ).toBeInTheDocument(); + + expect(screen.queryByAltText(/current GroundControl dashboard/i)).not.toBeInTheDocument(); + expect(screen.queryByAltText(/current GroundControl topology/i)).not.toBeInTheDocument(); }); it("shows the scoped MCP tool surface", () => { @@ -67,7 +76,10 @@ describe("Home page", () => { it("copies the active install command", async () => { const writeText = vi.fn().mockResolvedValue(undefined); - Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true }); + Object.defineProperty(navigator, "clipboard", { + value: { writeText }, + configurable: true, + }); render(); fireEvent.click(screen.getByRole("button", { name: "Copy installation command" })); diff --git a/app/page.tsx b/app/page.tsx index 80f93c1..da1ba62 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,7 @@ "use client"; import Image from "next/image"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; const GITHUB_URL = "https://github.com/teckedd-code2save/groundcontrol"; const INSTALL_URL = @@ -28,46 +28,22 @@ const commands = [ }, ] as const; -const features = [ - { - title: "Agent-native operations", - copy: "Connect ChatGPT or another MCP client through OAuth. Agents receive scoped deployment capabilities, not your SSH keys or provider secrets.", - meta: "MCP · OAuth · resource scopes", - }, - { - title: "Durable deployment work", - copy: "Redeployments return operation IDs, continue after the chat disconnects, verify the result, and preserve evidence for the next agent session.", - meta: "Idempotent · resumable · verifiable", - }, - { - title: "Connector capability health", - copy: "GitHub, GHCR and Daytona are checked capability by capability so configured never masquerades as healthy.", - meta: "Healthy · degraded · missing scope", - }, - { - title: "Native terminal", - copy: "Operators get a real xterm + PTY with Tab, Ctrl+C, history, ANSI output and persistent shell state when direct access is appropriate.", - meta: "PTY · SSH · host bridge", - }, - { - title: "Deployment evidence", - copy: "Inspect runtime containers, routes, health, releases, source identity and public verification from one operational record.", - meta: "Runtime · source · verification", - }, - { - title: "Single-tenant by design", - copy: "Every installation is your own control plane. The public site never doubles as a shared dashboard and your instance keeps its credentials locally.", - meta: "Self-hosted · private · open source", - }, -] as const; - const agentTools = [ ["deployment.list", "See only the workloads the grant allows."], ["deployment.inspect", "Read source, target, release and runtime identity."], ["deployment.health", "Check containers and the public endpoint."], - ["deployment.config.check", "Confirm named config keys without returning secret values."], - ["deployment.redeploy", "Start an idempotent durable redeploy operation."], - ["operation.get", "Return later and read final status + evidence."], + ["deployment.config.check", "Confirm named config keys without returning values."], + ["deployment.redeploy", "Start an idempotent durable redeploy."], + ["operation.get", "Return later for final status and evidence."], +] as const; + +const capabilities = [ + ["Agent-native operations", "ChatGPT and MCP clients receive scoped capabilities, never your SSH keys.", "MCP · OAuth"], + ["Durable operations", "Deployments continue after the calling chat disappears and retain verification evidence.", "operation id · evidence"], + ["Connector health", "GitHub, GHCR and Daytona are verified capability by capability.", "healthy · degraded"], + ["Native operator PTY", "Humans still get a real terminal with Tab, Ctrl+C, history and persistent state.", "xterm · PTY"], + ["Secret-safe config checks", "Agents can confirm named configuration exists without seeing its value.", "metadata only"], + ["Single tenant", "Every install is your own control plane, not an account in a shared operations SaaS.", "self-hosted"], ] as const; function Arrow() { @@ -86,19 +62,9 @@ function InstallConsole() { async function copyCommand() { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(active.command); - } else { - const textArea = document.createElement("textarea"); - textArea.value = active.command; - textArea.setAttribute("readonly", ""); - textArea.style.position = "fixed"; - textArea.style.opacity = "0"; - document.body.appendChild(textArea); - textArea.select(); - document.execCommand("copy"); - textArea.remove(); } setCopied(true); - window.setTimeout(() => setCopied(false), 1800); + window.setTimeout(() => setCopied(false), 1600); } return ( @@ -132,12 +98,132 @@ function InstallConsole() { ); } +function MotionGrid() { + const sectionRef = useRef(null); + const [progress, setProgress] = useState(0); + + useEffect(() => { + const section = sectionRef.current; + if (!section) return; + + let frame = 0; + const update = () => { + frame = 0; + const rect = section.getBoundingClientRect(); + const travel = Math.max(1, section.offsetHeight - window.innerHeight); + const value = Math.min(1, Math.max(0, -rect.top / travel)); + setProgress(value); + }; + const onScroll = () => { + if (!frame) frame = window.requestAnimationFrame(update); + }; + update(); + window.addEventListener("scroll", onScroll, { passive: true }); + window.addEventListener("resize", onScroll); + return () => { + window.removeEventListener("scroll", onScroll); + window.removeEventListener("resize", onScroll); + if (frame) window.cancelAnimationFrame(frame); + }; + }, []); + + const eased = progress * progress * (3 - 2 * progress); + const compact = { left: 55, top: 23, width: 26, height: 62 }; + const expanded = { left: 7, top: 7, width: 86, height: 86 }; + const frame = { + left: compact.left + (expanded.left - compact.left) * eased, + top: compact.top + (expanded.top - compact.top) * eased, + width: compact.width + (expanded.width - compact.width) * eased, + height: compact.height + (expanded.height - compact.height) * eased, + }; + + return ( +
+
+
+ ); +} + export default function Home() { + useEffect(() => { + const elements = Array.from(document.querySelectorAll("[data-reveal]")); + if (!("IntersectionObserver" in window)) { + elements.forEach((element) => element.classList.add("is-visible")); + return; + } + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + (entry.target as HTMLElement).classList.add("is-visible"); + observer.unobserve(entry.target); + } + }); + }, + { threshold: 0.18 } + ); + elements.forEach((element) => observer.observe(element)); + return () => observer.disconnect(); + }, []); + return (
- + GroundControl
-
-
-

SELF-HOSTED CONTROL PLANE FOR SOFTWARE AGENTS

-

Give your agents infrastructure arms.

-

- GroundControl lets ChatGPT and other approved agents inspect, deploy, verify and recover - applications on infrastructure you own without handing the conversation your SSH keys, - provider credentials or an unrestricted shell. +

+
- -
-
-

THE PRODUCT THESIS

-
-

Agents should ask for outcomes, not learn your server.

-

- GroundControl turns repositories, containers, domains, deployment evidence and provider connections - into typed operational capabilities. The agent asks to inspect or redeploy. GroundControl handles - the host mechanics, verifies the result and returns evidence. -

-
-
+ + + Scroll +
-
-
-
-

CHATGPT + YOUR INFRASTRUCTURE

-

Connect once. Grant only what the agent needs.

-
+
+

THE PRODUCT THESIS

+
+

Agents should ask for outcomes, not learn your server.

- GroundControl exposes a remote MCP endpoint with OAuth. Each client receives exact deployment and - capability scopes. Human approval is reserved for the boundaries you choose, not every harmless operation. + GroundControl turns repositories, containers, domains, health and deployment evidence into bounded + operational capabilities. The agent says what should happen. GroundControl handles the host mechanics + and returns proof.

- -
- {[ - ["01", "Connect", "Add the GroundControl MCP URL to ChatGPT or another compatible agent."], - ["02", "Authorize", "Sign into your private instance and choose exact workloads + capabilities."], - ["03", "Operate", "The agent can inspect, redeploy and verify within that autonomy envelope."], - ].map(([step, title, copy]) => ( -
- {step} -

{title}

-

{copy}

-
- ))} -
- -
- {agentTools.map(([name, copy]) => ( -
- {name} -

{copy}

-
- ))} -
-
-
-
-

REAL OPERATING SURFACE

-

GroundControl knows what is actually running.

-

- The control plane keeps deployment identity, runtime state and public verification close enough that - agents can make decisions without scraping a pile of shell output. -

-
- Current GroundControl infrastructure view -
Current infrastructure surface · host and operational state.
-
-
- -
    -
  1. - 01 -
    - Connect GitHub -

    Use an operator-owned GitHub App, then choose the exact account and repository GroundControl may see.

    -
    -
  2. -
  3. - 02 -
    - Connect Repo -

    Link an installation-backed repository to the deployment. Private repositories are first-class, not a public-API fallback.

    -
    -
  4. -
  5. - 03 -
    - Deploy and verify -

    Operations keep their own durable state and evidence, even when the calling chat disappears.

    -
    -
  6. -
  7. - 04 -
    - Investigate safely -

    Connector health distinguishes configured, healthy, degraded, missing-scope and revoked capabilities.

    -
    -
  8. -
-
-
+ -
-
-

CURRENT PRODUCT

-

The interface changed. The proof should change with it.

+
+
+

LIVE ACCEPTANCE · SEPTEMBER 2026

+

This is not a mockup. ChatGPT redeployed a real workload through GroundControl.

+

+ The agent received a durable operation ID. GroundControl reconciled the deployment, checked Postgres, + Redis, migrations, API and web, verified the running images, then hit the public endpoint and returned + the evidence to ChatGPT. +

+
+ success first attempt + HTTP 200 endpoint verified + no shell typed MCP capability +
- -
+
Current GroundControl topology view -
- 01 · Topology - Understand services and their relationships before changing them. - Current product capture. -
+
Real ChatGPT + GroundControl acceptance run.
- -
-
- Current GroundControl runtime containers view -
- 02 · Runtime - See containers, health and what is actually alive. - Runtime evidence stays separate from source assumptions. -
-
- -
- Current GroundControl native PTY terminal -
- 03 · Terminal - A real PTY for the operator, not an agent escape hatch. - Tab, Ctrl+C, history, ANSI and persistent cwd. -
-
-
-
-
-

WHY AGENTS PREFER THE CONTROL PLANE

+
+
+

WHAT GROUNDCONTROL GIVES THE AGENT

More reach. Less infrastructure-specific reasoning.

-
- {features.map((feature) => ( -
-

{feature.title}

-

{feature.copy}

- {feature.meta} +
+ {capabilities.map(([title, copy, meta], index) => ( +
+ 0{index + 1} +

{title}

+

{copy}

+ {meta}
))}
-
-
-
-

CONNECTORS AS CAPABILITIES

-

“Configured” is not the same thing as healthy.

+
+
+
+

NARROW TOOL SURFACE

+

Capabilities, not a bag of shell commands.

- GitHub repository reads, signed webhooks, repair PR permissions, GHCR pulls and Daytona sandbox - lifecycle are verified independently. GroundControl tells agents what works now and exactly what needs repair. + External agents do not get the human terminal. They get intent-level operations with resource scopes, + idempotency and evidence.

-
-

healthy · repository read verified

-

unverified · configured but not yet proven

-

missing_scope · reconnect with the capability required

-
+
+ {agentTools.map(([name, copy]) => ( +
+ {name} + {copy} +
+ ))} +
+
+
-
- Current GroundControl operator dashboard -
- The public website is only the front door. Every operator gets their own GroundControl instance and private login plane. -
-
+
+
+

CONNECTORS AS CAPABILITIES

+

Connect GitHub. Connect Repo. Prove what actually works.

+

+ Repository access, signed events, repair PR permissions, private GHCR pulls and Daytona sandbox + reproduction are verified independently. “Configured” does not get to cosplay as healthy. +

+
+
+ Connect GitHub→Connect Repo→Deploy→Verify
-
+

AGENT-ASSISTED INSTALL

Install mechanically. Claim ownership personally.

- The installer runs on the VPS, generates secrets there, starts GroundControl on loopback and returns a - short-lived one-time claim. The installing agent never becomes the permanent administrator. + The installer runs on the authorized VPS, generates secrets there, starts GroundControl on loopback and + returns a short-lived one-time claim. The installing agent never becomes the permanent administrator.

- - -
+
{[ - ["1", "Install", "Agent or operator runs the canonical installer on the authorized VPS."], - ["2", "Claim", "Human opens the short-lived claim URL and creates the first administrator."], - ["3", "Publish", "Attach your domain/reverse proxy or keep the instance private."], - ["4", "Connect agent", "Add the instance MCP URL to ChatGPT and approve the exact scope."], - ].map(([step, title, copy]) => ( -
- {step} - {title} -

{copy}

+ ["01", "Install", "Run the canonical installer on the VPS."], + ["02", "Claim", "Human creates the first administrator."], + ["03", "Publish", "Attach HTTPS/domain or keep it private."], + ["04", "Connect agent", "Approve the exact MCP scope."], + ].map(([step, title, copy], index) => ( +
+ {step} + {title} +

{copy}

))}
- -

- A fresh install binds locally first. GroundControl should be published through HTTPS before you use the - operator login over the public internet. Domain/bridge automation is the next distribution step. -

-
+

YOUR INFRASTRUCTURE, NOW AGENT-ADDRESSABLE

Build anywhere. Let GroundControl operate what you ship.

-

- The agent gets typed capabilities and evidence. You keep ownership, credentials and the private control plane. +

+ Your agent gets bounded reach and operational evidence. You keep ownership, credentials and the private + control plane.

- View source + GitHub