site(landing): demo-first CTAs, light theme, honest copy; restore positioning map

Now that demo.pcbjam.com is live, the landing page leads with it instead
of "coming soon":

- Hero: "Open the live demo" is the primary CTA; the coming-soon panel is
  replaced by a DemoLaunchCard (screenshot + play overlay, new tab — the
  editor needs COOP/COEP so it can never be iframed here); waitlist demoted
  to a ghost button under a "Want early access to multiplayer?" lead-in.
- Removed the Gerber demo and positioning-map sections, then restored the
  positioning map per review — recolored for light theme and without the
  multiplayer badge/legend ("Everyone picks one. We do both.").
- Light theme: flipped the :root ink tokens, darkened accent/signal for
  contrast on white; fixed two dark-assumption bugs it exposed (header CTA
  text losing to .nav>a specificity, DemoVideo play scrim derived from --bg).
- Copy: positioned around what's live (real KiCad in a tab) with
  multiplayer explicitly "coming — join the waitlist"; cut untrue claims
  (GitHub import, self-host, offline PWA -> roadmap); cut the jam metaphor
  outside the product name; TrustBar fake-stat grid -> plain claims strip;
  footer tagline + fallback meta description aligned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REwWZGkAz1AZUJw8yqxkCN
This commit is contained in:
Viktor Vaczi 2026-07-04 21:04:37 +02:00
commit f9696ac503
19 changed files with 292 additions and 125 deletions

View file

@ -1,15 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 820 600" width="820" height="600" role="img" aria-labelledby="pq-title pq-desc" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 820 600" width="820" height="600" role="img" aria-labelledby="pq-title pq-desc" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif">
<title id="pq-title">PCB tool positioning map</title> <title id="pq-title">PCB tool positioning map</title>
<desc id="pq-desc">A quadrant chart. The horizontal axis runs from desktop-install on the left to browser-native on the right; the vertical axis from proprietary at the bottom to open at the top. LibrePCB and KiCad sit top-left (open but desktop). KiCanvas sits top-right but is viewer-only. Flux and EasyEDA sit bottom-right (browser but proprietary). Altium 365 and OrCAD X sit bottom-left (desktop and proprietary). PCBJam sits alone in the top-right as the only browser-native, open, real-time-multiplayer tool.</desc> <desc id="pq-desc">A quadrant chart. The horizontal axis runs from desktop-install on the left to browser-native on the right; the vertical axis from proprietary at the bottom to open at the top. LibrePCB and KiCad sit top-left (open but desktop). KiCanvas sits top-right but is viewer-only. Flux and EasyEDA sit bottom-right (browser but proprietary). Altium 365 and OrCAD X sit bottom-left (desktop and proprietary). PCBJam sits alone in the top-right as the only full editor that is both browser-native and open.</desc>
<!-- panel --> <!-- panel -->
<rect x="8" y="8" width="804" height="584" rx="16" fill="#11182e" stroke="#233052" stroke-width="1"/> <rect x="8" y="8" width="804" height="584" rx="16" fill="#f4f7fc" stroke="#d9e1f0" stroke-width="1"/>
<!-- top-right "whitespace" tint --> <!-- top-right "whitespace" tint -->
<rect x="410" y="64" width="338" height="232" fill="#4f7cff" fill-opacity="0.09"/> <rect x="410" y="64" width="338" height="232" fill="#4f7cff" fill-opacity="0.09"/>
<!-- axes --> <!-- axes -->
<g stroke="#34436b" stroke-width="2" fill="#34436b"> <g stroke="#aebad2" stroke-width="2" fill="#aebad2">
<line x1="80" y1="296" x2="748" y2="296"/> <line x1="80" y1="296" x2="748" y2="296"/>
<line x1="410" y1="64" x2="410" y2="528"/> <line x1="410" y1="64" x2="410" y2="528"/>
<polygon points="748,296 734,289 734,303" stroke="none"/> <polygon points="748,296 734,289 734,303" stroke="none"/>
@ -19,7 +19,7 @@
</g> </g>
<!-- axis labels --> <!-- axis labels -->
<g fill="#9aa6c0" font-size="14" font-weight="600"> <g fill="#5b6784" font-size="14" font-weight="600">
<text x="410" y="50" text-anchor="middle">Open &amp; yours</text> <text x="410" y="50" text-anchor="middle">Open &amp; yours</text>
<text x="410" y="554" text-anchor="middle">Proprietary, locked-in</text> <text x="410" y="554" text-anchor="middle">Proprietary, locked-in</text>
<text x="92" y="316" text-anchor="start">Desktop install</text> <text x="92" y="316" text-anchor="start">Desktop install</text>
@ -27,7 +27,7 @@
</g> </g>
<!-- competitors --> <!-- competitors -->
<g fill="#8794ad"> <g fill="#8b98b3">
<circle cx="210" cy="150" r="5"/> <circle cx="210" cy="150" r="5"/>
<circle cx="250" cy="205" r="5"/> <circle cx="250" cy="205" r="5"/>
<circle cx="540" cy="200" r="5"/> <circle cx="540" cy="200" r="5"/>
@ -36,7 +36,7 @@
<circle cx="210" cy="388" r="5"/> <circle cx="210" cy="388" r="5"/>
<circle cx="250" cy="448" r="5"/> <circle cx="250" cy="448" r="5"/>
</g> </g>
<g fill="#b4bfd6" font-size="15"> <g fill="#3d4a68" font-size="15">
<text x="224" y="155">LibrePCB</text> <text x="224" y="155">LibrePCB</text>
<text x="264" y="210">KiCad</text> <text x="264" y="210">KiCad</text>
<text x="554" y="205">KiCanvas</text> <text x="554" y="205">KiCanvas</text>
@ -45,16 +45,10 @@
<text x="224" y="393">Altium 365</text> <text x="224" y="393">Altium 365</text>
<text x="264" y="453">OrCAD X</text> <text x="264" y="453">OrCAD X</text>
</g> </g>
<text x="554" y="222" font-size="11" fill="#6b7796">viewer only</text> <text x="554" y="222" font-size="11" fill="#8b98b3">viewer only</text>
<!-- PCBJam marker (the only "all three") --> <!-- PCBJam marker (the only browser-native + open editor) -->
<circle cx="590" cy="120" r="30" fill="#4f7cff" fill-opacity="0.12"/> <circle cx="590" cy="120" r="30" fill="#4f7cff" fill-opacity="0.12"/>
<text x="590" y="130" text-anchor="middle" font-size="30" fill="#32d583">★</text> <text x="590" y="130" text-anchor="middle" font-size="30" fill="#2f57f5">★</text>
<text x="614" y="128" text-anchor="start" font-size="19" font-weight="700" fill="#e7ecf5">PCBJam</text> <text x="614" y="128" text-anchor="start" font-size="19" font-weight="700" fill="#101729">PCBJam</text>
<rect x="560" y="140" width="170" height="24" rx="12" fill="#32d583" fill-opacity="0.15" stroke="#32d583" stroke-opacity="0.6"/>
<text x="645" y="156" text-anchor="middle" font-size="12" fill="#32d583" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">real-time multiplayer</text>
<!-- legend -->
<text x="34" y="580" font-size="14" fill="#32d583">★</text>
<text x="50" y="580" font-size="12" fill="#9aa6c0">= true real-time multiplayer (PCBJam only)</text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

@ -0,0 +1,119 @@
---
/**
* DemoLaunchCard — a clickable "open the live demo" card: a real product
* screenshot dimmed under a play button + CTA, opening the editor in a new tab.
*
* New tab is load-bearing, not a style choice: the editor needs COOP/COEP
* (SharedArrayBuffer) and the landing page deliberately isn't cross-origin
* isolated (it would break the no-COEP YouTube/iframe embeds), so the demo
* can never be iframed here. Same mechanic as the Gerber viewer card.
*/
import { Picture } from 'astro:assets';
import type { ImageMetadata } from 'astro';
interface Props {
href: string;
image: ImageMetadata;
alt: string;
cta?: string;
sub?: string;
class?: string;
}
const {
href,
image,
alt,
cta = 'Open the live demo',
sub = 'Opens in a new tab · no install, no account',
class: cls = '',
} = Astro.props;
---
<a class={`launch-card ${cls}`} href={href} target="_blank" rel="noopener">
<Picture
src={image}
formats={['avif', 'webp']}
alt={alt}
widths={[480, 768, 1280]}
sizes="(max-width: 880px) 92vw, 540px"
loading="eager"
decoding="async"
/>
<span class="launch-card__scrim" aria-hidden="true"></span>
<span class="launch-card__play" aria-hidden="true">▶</span>
<span class="launch-card__cta">{cta}</span>
<span class="launch-card__sub">{sub}</span>
</a>
<style>
.launch-card {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.6rem;
width: 100%;
aspect-ratio: 16 / 9;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
background: var(--bg-soft);
color: var(--fg);
text-decoration: none;
transition: filter var(--dur) var(--ease);
}
.launch-card :global(img) {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center top;
}
/* Dim the screenshot so the CTA stays legible. */
.launch-card__scrim {
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(11, 16, 32, 0.45), rgba(11, 16, 32, 0.72));
}
.launch-card:hover {
filter: brightness(1.06);
text-decoration: none;
}
.launch-card__play,
.launch-card__cta,
.launch-card__sub {
position: relative;
}
.launch-card__play {
display: grid;
place-items: center;
width: 64px;
height: 64px;
border-radius: 999px;
background: var(--brand-600);
color: #fff;
font-size: 1.4rem;
padding-left: 4px;
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
}
.launch-card__cta {
font-weight: 600;
font-size: 1.1rem;
color: #fff;
}
.launch-card__sub {
font-family: var(--font-mono);
font-size: 0.78rem;
color: rgba(255, 255, 255, 0.75);
text-align: center;
padding: 0 1rem;
}
@media (prefers-reduced-motion: reduce) {
.launch-card {
transition: none;
}
}
</style>

View file

@ -18,9 +18,11 @@ interface Props {
poster?: string; poster?: string;
caption?: string; caption?: string;
ratio?: string; // default 16/9 ratio?: string; // default 16/9
/** When true and no video yet, show a big "Demo coming soon" panel instead of /** When true and no video yet, show a big "coming soon" panel instead of
* the internal PRODUCE placeholder. */ * the internal PRODUCE placeholder. */
comingSoon?: boolean; comingSoon?: boolean;
/** Panel text for the comingSoon state. */
soonLabel?: string;
class?: string; class?: string;
} }
@ -31,6 +33,7 @@ const {
caption, caption,
ratio = '16 / 9', ratio = '16 / 9',
comingSoon = false, comingSoon = false,
soonLabel = 'Demo coming soon',
class: cls = '', class: cls = '',
} = Astro.props; } = Astro.props;
const hasVideo = Boolean(mp4 || webm); const hasVideo = Boolean(mp4 || webm);
@ -58,7 +61,7 @@ const hasVideo = Boolean(mp4 || webm);
</div> </div>
) : comingSoon ? ( ) : comingSoon ? (
<div class="demo-soon bp-grid" style={`aspect-ratio:${ratio};`}> <div class="demo-soon bp-grid" style={`aspect-ratio:${ratio};`}>
<span class="demo-soon-text">Demo coming soon</span> <span class="demo-soon-text">{soonLabel}</span>
</div> </div>
) : ( ) : (
<Placeholder <Placeholder
@ -95,7 +98,9 @@ const hasVideo = Boolean(mp4 || webm);
display: grid; display: grid;
place-items: center; place-items: center;
border: 0; border: 0;
background: color-mix(in srgb, var(--bg) 35%, transparent); /* Fixed dark scrim (not --bg-derived): white glyph over video needs a dark
veil in both themes. */
background: rgba(11, 16, 32, 0.35);
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
} }
@ -113,8 +118,8 @@ const hasVideo = Boolean(mp4 || webm);
background-color: var(--bg-soft); background-color: var(--bg-soft);
} }
.demo-soon-text { .demo-soon-text {
font-size: clamp(1.6rem, 5.5vw, 3rem); font-size: clamp(1.1rem, 3vw, 1.75rem);
font-weight: 800; font-weight: 700;
letter-spacing: -0.01em; letter-spacing: -0.01em;
color: var(--fg); color: var(--fg);
} }

View file

@ -21,7 +21,7 @@ const BUILD_COMMIT_URL = `https://github.com/emergence-engineering/pcbjam/commit
<img src="/favicon.svg" alt="" width="18" height="25" /> <img src="/favicon.svg" alt="" width="18" height="25" />
<span>PCBJam</span> <span>PCBJam</span>
</a> </a>
<p class="tagline muted">KiCad, in your browser. Now multiplayer.</p> <p class="tagline muted">Real KiCad, in your browser. No install.</p>
<nav class="links" aria-label="PCBJam"> <nav class="links" aria-label="PCBJam">
<a href={GITHUB_URL} rel="noopener" target="_blank"> <a href={GITHUB_URL} rel="noopener" target="_blank">
<Icon name="icons/github.svg" size="0.9rem" alt="" /> GitHub <Icon name="icons/github.svg" size="0.9rem" alt="" /> GitHub
@ -35,7 +35,7 @@ const BUILD_COMMIT_URL = `https://github.com/emergence-engineering/pcbjam/commit
<nav class="links stack" aria-label="Product"> <nav class="links stack" aria-label="Product">
<a href="/#how-it-works">How it works</a> <a href="/#how-it-works">How it works</a>
<a href="/#open">Open &amp; yours</a> <a href="/#open">Open &amp; yours</a>
<a href="/#demo">Live demo</a> <a href="https://demo.pcbjam.com" target="_blank" rel="noopener">Live demo</a>
<a href="/#waitlist">Join the waitlist</a> <a href="/#waitlist">Join the waitlist</a>
</nav> </nav>
</div> </div>

View file

@ -118,6 +118,12 @@ const isActive = (href: string, external?: boolean) =>
padding-block: 0.5rem; padding-block: 0.5rem;
font-size: 0.95rem; font-size: 0.95rem;
} }
/* `.nav > a` (higher specificity than .btn-primary) sets the muted link color;
re-assert the button's white text — dark-on-blue otherwise. */
.nav > a.cta-join,
.nav > a.cta-join:hover {
color: #fff;
}
/* Mobile menu (CSS-only via checkbox toggle). */ /* Mobile menu (CSS-only via checkbox toggle). */
.hamburger { .hamburger {

View file

@ -1,5 +1,5 @@
--- ---
/** Step — one numbered "how a jam works" step (§11). Body via default slot. */ /** Step — one numbered how-it-works step (§11). Body via default slot. */
import Icon from './Icon.astro'; import Icon from './Icon.astro';
interface Props { interface Props {
n: number; n: number;

View file

@ -18,7 +18,7 @@ interface Props {
const { const {
title, title,
description = 'PCBJam — open-source PCB design in your browser, with real-time multiplayer. Built on KiCad.', description = 'PCBJam — open-source PCB design in your browser. Real KiCad, no install. Built on KiCad.',
wide = false, wide = false,
ogImage = '/og/og-home.png', ogImage = '/og/og-home.png',
} = Astro.props; } = Astro.props;

View file

@ -5,7 +5,6 @@ import TrustBar from '../sections/TrustBar.astro';
import Pillars from '../sections/Pillars.astro'; import Pillars from '../sections/Pillars.astro';
import HowItWorks from '../sections/HowItWorks.astro'; import HowItWorks from '../sections/HowItWorks.astro';
import CapabilityProof from '../sections/CapabilityProof.astro'; import CapabilityProof from '../sections/CapabilityProof.astro';
import GerberDemoSection from '../sections/GerberDemoSection.astro';
import OpenYours from '../sections/OpenYours.astro'; import OpenYours from '../sections/OpenYours.astro';
import MigrationStrip from '../sections/MigrationStrip.astro'; import MigrationStrip from '../sections/MigrationStrip.astro';
import PositioningMap from '../sections/PositioningMap.astro'; import PositioningMap from '../sections/PositioningMap.astro';
@ -14,11 +13,8 @@ import MakeMoney from '../sections/MakeMoney.astro';
import AiNote from '../sections/AiNote.astro'; import AiNote from '../sections/AiNote.astro';
import FinalCta from '../sections/FinalCta.astro'; import FinalCta from '../sections/FinalCta.astro';
// Toggle the optional positioning-map section (§15).
const SHOW_MAP = true;
const description = const description =
'Open-source PCB design in your browser with real-time collaboration. Built on KiCad. No install, open format, free to start. Join the waitlist.'; 'Real KiCad in your browser — schematic capture, PCB layout, DRC, 3D, Gerber export. No install, open format, free. Multiplayer collaboration coming. Try the live demo.';
const jsonLd = { const jsonLd = {
'@context': 'https://schema.org', '@context': 'https://schema.org',
@ -37,7 +33,7 @@ const jsonLd = {
--- ---
<BaseLayout <BaseLayout
title="PCBJam — KiCad in your browser, now multiplayer" title="PCBJam — real KiCad in your browser, no install"
description={description} description={description}
wide wide
> >
@ -50,13 +46,12 @@ const jsonLd = {
<Hero /> <Hero />
<TrustBar /> <TrustBar />
<GerberDemoSection />
<Pillars /> <Pillars />
<HowItWorks /> <HowItWorks />
<CapabilityProof /> <CapabilityProof />
<OpenYours /> <OpenYours />
<MigrationStrip /> <MigrationStrip />
{SHOW_MAP && <PositioningMap />} <PositioningMap />
<BuiltByEmergence /> <BuiltByEmergence />
<MakeMoney /> <MakeMoney />
<AiNote /> <AiNote />

View file

@ -5,9 +5,10 @@ import WaitlistForm from '../components/WaitlistForm.astro';
<SectionBand grid class="final" labelledby="final-h2"> <SectionBand grid class="final" labelledby="final-h2">
<div class="final-inner"> <div class="final-inner">
<h2 id="final-h2">Be there when the first jam opens.</h2> <h2 id="final-h2">Be first in when multiplayer opens.</h2>
<p class="lead"> <p class="lead">
Early access rolls out in waves. Join the waitlist and we’ll save you a seat. Multiplayer early access rolls out in waves. Join the waitlist and we’ll save you a
seat.
</p> </p>
<WaitlistForm <WaitlistForm

View file

@ -1,37 +1,41 @@
--- ---
import WaitlistForm from '../components/WaitlistForm.astro'; import WaitlistForm from '../components/WaitlistForm.astro';
import DemoVideo from '../components/DemoVideo.astro'; import DemoLaunchCard from '../components/DemoLaunchCard.astro';
import Icon from '../components/Icon.astro'; import pcbnew from '../assets/screens/pcbnew.png';
--- ---
<section class="hero bp-grid"> <section class="hero bp-grid">
<div class="container hero-grid"> <div class="container hero-grid">
<div class="hero-copy"> <div class="hero-copy">
<span class="eyebrow">PCBJam · built on KiCad</span> <span class="eyebrow">PCBJam · built on KiCad</span>
<h1>KiCad, in your browser — now multiplayer.</h1> <h1>Real KiCad. In your browser. No install.</h1>
<p class="lead"> <p class="lead">
The PCB design tool millions trust — now zero-install and collaborative. Open a The full KiCad suite, compiled to WebAssembly and running in a tab. Open your own
board in a tab, invite your team, and edit the same schematic and layout together board, route, run DRC, spin it in 3D, export Gerbers — the real engine, not a
in real time. Open format. No install. Free to start. re-implementation. Multiplayer co-editing is coming: join the waitlist for early
access.
</p> </p>
<WaitlistForm source="hero" id="waitlist" class="hero-form" /> <p class="hero-cta">
<p class="hero-secondary">
<a <a
class="btn btn-primary btn-demo"
href="https://demo.pcbjam.com" href="https://demo.pcbjam.com"
class="try"
target="_blank" target="_blank"
rel="noopener" rel="noopener"
> >
Open the live editor → ▶&nbsp; Open the live demo
</a>
<span class="dot" aria-hidden="true">·</span>
<a href="#multiplayer" class="watch">
<Icon name="icons/play.svg" size="1.1rem" alt="" /> Watch the 90-second demo →
</a> </a>
<span class="hero-cta-sub">Free · no install, no account</span>
</p> </p>
<p class="hero-wl-lead">Want early access to multiplayer?</p>
<WaitlistForm
source="hero"
id="waitlist"
class="hero-form"
microcopy="Collab rolls out in waves — get your invite."
/>
<ul class="hero-trust"> <ul class="hero-trust">
<li>Open source</li> <li>Open source</li>
<li>Your files stay in KiCad format</li> <li>Your files stay in KiCad format</li>
@ -40,11 +44,13 @@ import Icon from '../components/Icon.astro';
</div> </div>
<div class="hero-visual"> <div class="hero-visual">
{/* TODO(assets): swap for hero board still w/ presence cursors (§4b.3) or the {/* TODO(assets): swap the poster for a hero board still w/ presence cursors
multiplayer demo loop (§4b.2) once produced. */} (§4b.3) or the multiplayer demo loop (§4b.2) once produced. */}
<DemoVideo <DemoLaunchCard
comingSoon href="https://demo.pcbjam.com"
caption="2 editors · live cursors · one board.kicad_pcb · no merge step" image={pcbnew}
alt="KiCad Pcbnew editing the pic_programmer demo board in a browser tab — open the live demo"
sub="Real KiCad in your browser · opens in a new tab"
/> />
</div> </div>
</div> </div>
@ -64,32 +70,43 @@ import Icon from '../components/Icon.astro';
font-size: clamp(2rem, 5vw, 3.25rem); font-size: clamp(2rem, 5vw, 3.25rem);
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.hero-form { .hero-cta {
margin-top: 1.5rem; margin: 1.5rem 0 0;
max-width: 30rem;
}
.hero-secondary {
margin: 1rem 0 0;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
gap: 0.4rem 0.75rem; gap: 0.5rem 1rem;
} }
.try { .btn-demo {
display: inline-flex; padding: 0.85rem 1.6rem;
align-items: center; font-size: 1.05rem;
font-weight: 600;
color: var(--accent);
} }
.dot { .hero-cta-sub {
font-size: 0.85rem;
color: var(--fg-muted); color: var(--fg-muted);
} }
.watch { .hero-wl-lead {
display: inline-flex; margin: 1.75rem 0 0.5rem;
align-items: center; font-weight: 600;
gap: 0.4rem; font-size: 0.95rem;
}
.hero-form {
max-width: 30rem;
}
/* One primary action per hero: the demo button. Demote the waitlist submit
to ghost weight (it stays full-primary in the final CTA and footer).
.hero-copy anchors the scope — .hero-form itself sits on the WaitlistForm
root, which doesn't carry this component's scope attribute. */
.hero-copy :global(.hero-form .btn-primary) {
background: transparent;
border-color: var(--border);
color: var(--fg); color: var(--fg);
} }
.hero-copy :global(.hero-form .btn-primary:hover) {
border-color: var(--accent);
color: var(--accent);
filter: none;
}
.hero-trust { .hero-trust {
list-style: none; list-style: none;
display: flex; display: flex;

View file

@ -5,20 +5,19 @@ import Step from '../components/Step.astro';
<SectionBand id="how-it-works" labelledby="hiw-h2"> <SectionBand id="how-it-works" labelledby="hiw-h2">
<div class="section-head"> <div class="section-head">
<h2 id="hiw-h2">From link to layout in three moves.</h2> <h2 id="hiw-h2">From file to fab in three moves.</h2>
</div> </div>
<div class="steps"> <div class="steps">
<Step n={1} title="Open a board."> <Step n={1} title="Open a board.">
Paste a link, drag in a <code>.kicad_pcb</code>, or pull from GitHub. It opens in seconds — Drag in a <code>.kicad_pcb</code> or start from the demo board. It opens in seconds —
no install, no account wall to <em>look</em>. no install, no account.
</Step> </Step>
<Step n={2} title="Start a jam."> <Step n={2} title="Design and route.">
Share the URL. Teammates join with live cursors and presence. Everyone sees the same Schematic capture, PCB layout, interactive routing, DRC/ERC, 3D view — the real KiCad
board update in real time. toolset.
</Step> </Step>
<Step n={3} title="Design, route & export — together."> <Step n={3} title="Export and ship.">
Schematic capture, PCB layout, DRC/ERC, 3D, BOM, and Gerbers — the full flow, then hand BOM and Gerbers straight from the browser — hand off to any fab. Your call.
off to any fab. Your call.
</Step> </Step>
</div> </div>
</SectionBand> </SectionBand>

View file

@ -18,8 +18,8 @@ const row = [
The editor and the open file format are <strong>free</strong> — design entirely in your The editor and the open file format are <strong>free</strong> — design entirely in your
browser, with your files stored locally on your own machine. When you want PCBJam’s browser, with your files stored locally on your own machine. When you want PCBJam’s
<strong>cloud</strong> — hosted storage, sync across your devices, and shared workspaces for <strong>cloud</strong> — hosted storage, sync across your devices, and shared workspaces for
your team — that’s the paid part. The open core stays free, and your local files are always your team — that will be the paid part. The open core stays free, and your local files are
yours. always yours.
</p> </p>
</div> </div>

View file

@ -14,8 +14,7 @@ const sources = ['EAGLE', 'Altium', 'CADSTAR', 'OrCAD'];
<h3 id="mig-h3">Coming from EAGLE, Altium, CADSTAR, or OrCAD?</h3> <h3 id="mig-h3">Coming from EAGLE, Altium, CADSTAR, or OrCAD?</h3>
<p class="muted"> <p class="muted">
KiCad is where designs are migrating — and PCBJam opens them in a browser tab, free, KiCad is where designs are migrating — and PCBJam opens them in a browser tab, free,
with nothing to install. <em>Bring your board. Invite your team. Pick up where you left with nothing to install. <em>Bring your board. Pick up where you left off.</em>
off.</em>
</p> </p>
<a class="btn btn-primary" href="#waitlist">Join the waitlist →</a> <a class="btn btn-primary" href="#waitlist">Join the waitlist →</a>
</div> </div>

View file

@ -34,9 +34,13 @@ const subpoints = [
</p> </p>
</div> </div>
{/* Slim band, not a hero-sized panel: the single-user demo is live (see the
hero), only the multiplayer demo video doesn't exist yet. */}
<DemoVideo <DemoVideo
class="mp-demo" class="mp-demo"
comingSoon comingSoon
soonLabel="Multiplayer demo video coming soon"
ratio="21 / 5"
caption="2 editors · live cursors · one board.kicad_pcb · no merge step" caption="2 editors · live cursors · one board.kicad_pcb · no merge step"
/> />

View file

@ -2,25 +2,19 @@
import SectionBand from '../components/SectionBand.astro'; import SectionBand from '../components/SectionBand.astro';
import Chip from '../components/Chip.astro'; import Chip from '../components/Chip.astro';
const chips = [ const chips = ['Open .kicad_pcb / .kicad_sch', 'Open source (GPL)'];
'Open .kicad_pcb / .kicad_sch',
'Self-hostable',
'Offline PWA',
'Open source (GPL)',
];
--- ---
<SectionBand id="open" labelledby="open-h2"> <SectionBand id="open" labelledby="open-h2">
<div class="section-head open-copy"> <div class="section-head open-copy">
<span class="eyebrow">No lock-in. No data hostage.</span> <span class="eyebrow">No lock-in. No data hostage.</span>
<h2 id="open-h2">Design <em>and</em> collaborate in the browser — without surrendering your files.</h2> <h2 id="open-h2">Design in the browser — without surrendering your files.</h2>
<p class="lead"> <p class="lead">
Cloud EDA tools trap your designs in proprietary formats and clouds you can’t leave. Cloud EDA tools trap your designs in proprietary formats and clouds you can’t leave.
Desktop open-source tools keep you free but strand you offline and solo. PCBJam refuses PCBJam refuses the trade-off: <strong>real KiCad format</strong> in and out, and it’s
the trade-off: <strong>real KiCad format</strong> in and out, <strong>self-host</strong> <strong>open source</strong> — the same <code>.kicad_pcb</code> your desktop KiCad opens,
it on your own server, run it <strong>offline as a PWA</strong>, and it’s so you can walk away with every byte whenever you want. Self-hosting and an offline PWA
<strong>open source</strong>. Collaborate in our cloud when it’s convenient — and walk are on the roadmap.
away with every byte whenever you want.
</p> </p>
<div class="cluster"> <div class="cluster">
{chips.map((c) => <Chip label={c} />)} {chips.map((c) => <Chip label={c} />)}

View file

@ -12,13 +12,13 @@ import Pillar from '../components/Pillar.astro';
Zero install, zero toolchain. Mac, Linux, Windows, Chromebook, iPad — if it has a Zero install, zero toolchain. Mac, Linux, Windows, Chromebook, iPad — if it has a
modern browser, it designs PCBs. modern browser, it designs PCBs.
</Pillar> </Pillar>
<Pillar title="Truly multiplayer."> <Pillar title="Multiplayer, next.">
Live cursors, presence, and conflict-free co-editing on the same board. Plus Live cursors, presence, and conflict-free co-editing on the same board — rolling out
pin-to-canvas comments for review. to the waitlist first.
</Pillar> </Pillar>
<Pillar title="Open & yours."> <Pillar title="Open & yours.">
Real KiCad format in, real KiCad format out. Self-host it, run it offline as a PWA, or Real KiCad format in, real KiCad format out — your files are never held hostage.
use our cloud — your files are never held hostage. Self-hosting and an offline PWA are on the roadmap.
</Pillar> </Pillar>
</div> </div>
</SectionBand> </SectionBand>

View file

@ -4,11 +4,11 @@ import SectionBand from '../components/SectionBand.astro';
<SectionBand labelledby="pos-h3"> <SectionBand labelledby="pos-h3">
<div class="section-head"> <div class="section-head">
<h3 id="pos-h3">Everyone picks two. We do all three.</h3> <h3 id="pos-h3">Everyone picks one. We do both.</h3>
<p class="lead"> <p class="lead">
Browser-native. Open &amp; yours. Truly multiplayer. Flux is browser + collab but closed. Browser-native. Open &amp; yours. Flux and EasyEDA are browser-based but closed.
KiCanvas is browser + open but read-only. LibrePCB is open + full but desktop-only. KiCanvas is browser + open but a viewer. KiCad and LibrePCB are open but desktop-only.
<strong>PCBJam is the only one in the middle.</strong> <strong>PCBJam is the only full editor that's both.</strong>
</p> </p>
</div> </div>
<img <img
@ -16,7 +16,7 @@ import SectionBand from '../components/SectionBand.astro';
src="/graphics/positioning-quadrant.svg" src="/graphics/positioning-quadrant.svg"
width="820" width="820"
height="600" height="600"
alt="Positioning map: PCBJam is the only PCB tool that is browser-native, open, and truly real-time multiplayer. Flux and EasyEDA are browser-based but proprietary; KiCanvas is browser and open but viewer-only; LibrePCB and KiCad are open but desktop-only; Altium 365 and OrCAD X are desktop and proprietary." alt="Positioning map: PCBJam is the only full PCB editor that is both browser-native and open. Flux and EasyEDA are browser-based but proprietary; KiCanvas is browser and open but viewer-only; LibrePCB and KiCad are open but desktop-only; Altium 365 and OrCAD X are desktop and proprietary."
loading="lazy" loading="lazy"
decoding="async" decoding="async"
/> />

View file

@ -1,23 +1,54 @@
--- ---
import SectionBand from '../components/SectionBand.astro'; import SectionBand from '../components/SectionBand.astro';
import StatBar from '../components/StatBar.astro';
// TODO(numbers): replace with defensible, current figures. Pull PCBJam-native // Plain claims, not stat headlines — no fake precision to source or defend.
// counts (boards opened in beta, GitHub stars) once they exist (§8, §31.6). // Swap in PCBJam-native numbers (boards opened, GitHub stars) once they exist (§8).
const stats = [ const claims = [
{ value: 'Millions', label: 'KiCad users worldwide' }, 'Built on KiCad — trusted by millions',
{ value: 'Largest', label: 'open component & footprint library in EDA' }, 'the largest open footprint library in EDA',
{ value: '100%', label: 'open file format — your .kicad_pcb / .kicad_sch' }, '100% open .kicad_pcb format',
{ value: 'Free', label: 'to start — no credit card' }, 'free to start',
]; ];
--- ---
<SectionBand tone="soft" flush class="trust"> <SectionBand tone="soft" flush class="trust">
<StatBar stats={stats} /> <p class="claims">
{
claims.map((c, i) => (
<>
{i > 0 && (
<span class="sep" aria-hidden="true">
·
</span>
)}
<span class="claim">{c}</span>
</>
))
}
</p>
</SectionBand> </SectionBand>
<style> <style>
.trust :global(.container) { .trust :global(.container) {
padding-block: 1.75rem; padding-block: 1.25rem;
}
.claims {
margin: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: baseline;
gap: 0.35rem 0.75rem;
text-align: center;
font-size: 0.95rem;
color: var(--fg-muted);
}
.claim:first-of-type {
color: var(--fg);
font-weight: 600;
}
.sep {
color: var(--fg-muted);
opacity: 0.5;
} }
</style> </style>

View file

@ -5,20 +5,23 @@
@import "tailwindcss/utilities.css" layer(utilities); @import "tailwindcss/utilities.css" layer(utilities);
:root { :root {
/* ── Ink neutrals (authoritative; existing names kept) ───────────────── */ color-scheme: light;
--bg: #0b1020; /* ink-950 */
--bg-soft: #11182e; /* ink-900 / surface */ /* ── Ink neutrals (authoritative; existing names kept — the *roles* stayed,
--fg: #e7ecf5; /* ink-100 */ the values flipped to the light theme) ────────────────────────────── */
--fg-muted: #9aa6c0; /* ink-400 */ --bg: #ffffff; /* page */
--border: #233052; /* ink-800 */ --bg-soft: #f4f7fc; /* surface / soft band */
--fg: #101729; /* ink-950 text */
--fg-muted: #5b6784; /* muted text */
--border: #d9e1f0; /* hairlines / bp-grid dots */
/* ── Primary (Circuit Blue) ─────────────────────────────────────────── */ /* ── Primary (Circuit Blue) ─────────────────────────────────────────── */
--accent: #4f7cff; /* brand-500 (links/accents) */ --accent: #2f57f5; /* links/accents (darker than the dark-theme 4f7cff for contrast on white) */
--brand-600: #2f57f5; /* primary button */ --brand-600: #2f57f5; /* primary button */
--brand-700: #1d4ed8; /* favicon / deep */ --brand-700: #1d4ed8; /* favicon / deep */
/* ── Signal (green / "live" presence) ───────────────────────────────── */ /* ── Signal (green / "live" presence) ───────────────────────────────── */
--signal-400: #32d583; /* online text */ --signal-400: #0a9b62; /* online text/icons (dark-theme value 32d583 is illegible on white) */
--signal-600: #039855; /* online fill / success */ --signal-600: #039855; /* online fill / success */
/* ── Presence palette (peer cursors) ────────────────────────────────── */ /* ── Presence palette (peer cursors) ────────────────────────────────── */