Measured from this repo's benchmark report, not a marketing placeholder.
reports/benchmarks.md
diff --git a/CHANGELOG.md b/CHANGELOG.md index fea065d..3207ff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed + +- **Landing + status pages: token-driven fluid type scale and spacing scale.** + `src/brand.js` now computes a fluid `clamp()` type scale (`--fs-n2`…`--fs-7`) and a + 4px-base spacing scale (`--sp-1`…`--sp-24`) from a formula, the same way it already + derived the color palette from `brand.json`. `landing/index.html` and the generated + status page (`scripts/build-pages.mjs`) now consume these tokens for every + font-size/margin/padding/gap instead of hand-picked pixel values; `test/pages.test.js` + enforces that both surfaces stay in lockstep with the formula. `forge uicheck design` + now reports the landing page's spacing values 100% on-grid (previously 96% on an + inconsistent 2px base). ## [0.24.0] - 2026-07-20 ### Added diff --git a/landing/index.html b/landing/index.html index 333970f..b45b2f1 100644 --- a/landing/index.html +++ b/landing/index.html @@ -103,6 +103,45 @@ ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace; --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --ease-out: cubic-bezier(0.22, 1, 0.36, 1); + /* Fluid type scale — one formula (src/brand.js: typeScaleCss), not per-element + magic numbers. Every font-size below is one of these ten steps. + test/pages.test.js checks this block is exactly what the formula emits. */ + --fs-n2: clamp(12.5px, 0.79rem - 0.02vw, 12.64px); + --fs-n1: clamp(14.22px, 0.87rem + 0.09vw, 15px); + --fs-0: clamp(16px, 0.94rem + 0.23vw, 18px); + --fs-1: clamp(18px, 1.02rem + 0.41vw, 21.6px); + --fs-2: clamp(20.25px, 1.1rem + 0.64vw, 25.92px); + --fs-3: clamp(22.78px, 1.19rem + 0.95vw, 31.1px); + --fs-4: clamp(25.63px, 1.27rem + 1.33vw, 37.32px); + --fs-5: clamp(28.83px, 1.35rem + 1.81vw, 44.79px); + --fs-6: clamp(32.44px, 1.42rem + 2.42vw, 53.75px); + --fs-7: clamp(36.49px, 1.49rem + 3.18vw, 64.5px); + /* Spacing scale — one base unit (4px) × an integer step (src/brand.js: + spaceScaleCss). Every margin/padding/gap below is one of these steps. */ + --sp-1: 4px; + --sp-2: 8px; + --sp-3: 12px; + --sp-4: 16px; + --sp-5: 20px; + --sp-6: 24px; + --sp-7: 28px; + --sp-8: 32px; + --sp-9: 36px; + --sp-10: 40px; + --sp-12: 48px; + --sp-14: 56px; + --sp-16: 64px; + --sp-20: 80px; + --sp-24: 96px; + /* Page-scoped structural tokens — like --radius / --ease-out above, these + are landing-local (NOT part of the brand.json color-parity set). Shadows + and tints are derived from the theme tokens so they flip with the scheme. */ + --radius-lg: 26px; + --ease-spring: cubic-bezier(0.34, 1.4, 0.5, 1); + --tint: color-mix(in srgb, var(--brand) 12%, transparent); + --tint-soft: color-mix(in srgb, var(--brand) 7%, transparent); + --hairline: color-mix(in srgb, var(--line-strong) 55%, transparent); + --bezel: inset 0 1px 0 color-mix(in srgb, var(--text) 7%, transparent); } @media (prefers-color-scheme: light) { :root { @@ -136,11 +175,14 @@ scroll-behavior: smooth; background: var(--bg); } + /* Motion is opt-out: every transition/animation collapses to ~0 so a + reduced-motion visitor sees the final, fully-legible layout instantly. */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; + animation-delay: 0ms !important; scroll-behavior: auto !important; transition-duration: 0.001ms !important; } @@ -148,14 +190,22 @@ body { margin: 0; color: var(--text); - font: 16px/1.65 var(--sans); + font: var(--fs-0) / 1.65 var(--sans); + /* Two ember washes — a warm forge-glow top-right, a fainter counter-light + bottom-left — both derived from the accent tokens so they flip per theme. */ background: radial-gradient( - 960px 460px at 85% -10%, - rgba(242, 100, 48, 0.16), - transparent 70% + 1120px 560px at 90% -12%, + color-mix(in srgb, var(--brand) 15%, transparent), + transparent 66% + ), + radial-gradient( + 820px 520px at -6% 6%, + color-mix(in srgb, var(--brand-2) 8%, transparent), + transparent 60% ), var(--bg); + background-attachment: fixed; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } @@ -169,8 +219,12 @@ outline-offset: 3px; border-radius: var(--radius-sm); } + ::selection { + background: var(--brand); + color: var(--bg); + } .wrap { - width: min(var(--max), calc(100% - 40px)); + width: min(var(--max), calc(100% - 48px)); margin: 0 auto; } .mono { @@ -178,16 +232,16 @@ } .skip { position: absolute; - left: 16px; + left: var(--sp-4); top: -80px; background: var(--text); color: var(--bg); - padding: 10px 14px; + padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-sm); z-index: 30; } .skip:focus { - top: 16px; + top: var(--sp-4); } /* ---- Nav (N1b canonical) ---------------------------------------- */ @@ -196,36 +250,40 @@ top: 0; z-index: 20; border-bottom: 1px solid var(--line); - background: color-mix(in srgb, var(--bg) 82%, transparent); + background: color-mix(in srgb, var(--bg) 78%, transparent); /* Gate: compositor-friendly blur — kept ≤8px so the sticky nav doesn't trigger a heavy repaint on every scroll frame. */ backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); } .nav-inner { - min-height: 68px; + min-height: 66px; display: flex; align-items: center; justify-content: space-between; - gap: 24px; + gap: var(--sp-6); } .brand { display: inline-flex; - gap: 10px; + gap: var(--sp-3); align-items: center; text-decoration: none; - font: 750 16px var(--mono); - letter-spacing: -0.02em; + font: 700 var(--fs-0) var(--mono); + letter-spacing: -0.03em; } .logo { - width: 32px; - height: 32px; - border-radius: var(--radius-sm); + width: 30px; + height: 30px; + border-radius: 9px; display: grid; place-items: center; - background: linear-gradient(135deg, var(--brand), var(--brand-2)); + background: linear-gradient(150deg, var(--brand), var(--brand-2)); color: var(--bg); font-weight: 800; - box-shadow: 0 12px 34px rgba(242, 100, 48, 0.26); + font-size: var(--fs-n1); + box-shadow: + 0 6px 18px color-mix(in srgb, var(--brand) 40%, transparent), + var(--bezel); } .brand em { color: var(--brand); @@ -234,8 +292,9 @@ .nav-links { display: flex; align-items: center; - gap: 22px; - font-size: 14px; + gap: var(--sp-6); + font: 500 var(--fs-n1) var(--mono); + letter-spacing: -0.01em; } .nav-links a { color: var(--muted); @@ -249,36 +308,42 @@ color: var(--text); border: 1px solid var(--line-strong); border-radius: 999px; - padding: 8px 15px; + padding: var(--sp-2) var(--sp-4); transition: - border-color 0.16s var(--ease-out), - background 0.16s var(--ease-out); + border-color 0.18s var(--ease-out), + background 0.18s var(--ease-out), + transform 0.18s var(--ease-spring); } .nav-links .pill:hover { border-color: var(--brand); - background: color-mix(in srgb, var(--brand) 12%, transparent); + background: var(--tint); + transform: translateY(-1px); } @media (max-width: 760px) { .nav-links a:not(.pill) { display: none; } .wrap { - width: min(var(--max), calc(100% - 28px)); + width: min(var(--max), calc(100% - 32px)); } } /* ---- Hero (Stat-Led) -------------------------------------------- */ .hero { - padding: 84px 0 40px; + padding: var(--sp-24) 0 var(--sp-12); } .eyebrow { display: inline-flex; - gap: 10px; + gap: var(--sp-3); align-items: center; - margin: 0 0 20px; + margin: 0 0 var(--sp-6); + padding: var(--sp-2) var(--sp-4) var(--sp-2) var(--sp-3); + border: 1px solid var(--line); + border-radius: 999px; + background: color-mix(in srgb, var(--panel) 60%, transparent); color: var(--brand-2); - font: 700 12px var(--mono); - letter-spacing: 0.14em; + font: 600 var(--fs-n2) var(--mono); + letter-spacing: 0.12em; text-transform: uppercase; } /* Honest status dot — a plain marker, not a faux-live pulse. No glow ring, @@ -288,14 +353,16 @@ height: 7px; border-radius: 999px; background: var(--ok); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent); } h1 { margin: 0; - max-width: 15ch; - font-size: clamp(40px, 6vw, 66px); - line-height: 1.02; - letter-spacing: -0.03em; - font-weight: 700; + max-width: 17ch; + /* The display voice is monospace — the product compiles config, so its + headline is set in the same family as the code it emits. Tight tracking + and leading keep the mono forms from reading as a terminal dump. */ + font: 700 var(--fs-7) / 1.02 var(--mono); + letter-spacing: -0.045em; overflow-wrap: anywhere; min-width: 0; } @@ -303,38 +370,44 @@ color: var(--brand); } .lede { - margin: 24px 0 0; - max-width: 62ch; + margin: var(--sp-7) 0 0; + max-width: 60ch; color: var(--muted); - font-size: clamp(18px, 2vw, 21px); + font-size: var(--fs-1); + line-height: 1.6; text-wrap: pretty; } .actions { display: flex; flex-wrap: wrap; - gap: 14px; - margin-top: 32px; + gap: var(--sp-3); + margin-top: var(--sp-8); } .btn { display: inline-flex; min-height: 48px; align-items: center; justify-content: center; - gap: 10px; + gap: var(--sp-2); border-radius: 999px; border: 1px solid var(--line-strong); - padding: 12px 22px; + padding: var(--sp-3) var(--sp-6); text-decoration: none; - font-weight: 750; + font: 650 var(--fs-n1) var(--mono); + letter-spacing: -0.01em; white-space: nowrap; transition: - background 0.16s var(--ease-out), - border-color 0.16s var(--ease-out), - color 0.16s var(--ease-out), - transform 0.12s var(--ease-out); + background 0.18s var(--ease-out), + border-color 0.18s var(--ease-out), + color 0.18s var(--ease-out), + box-shadow 0.18s var(--ease-out), + transform 0.16s var(--ease-spring); + } + .btn:hover { + transform: translateY(-2px); } .btn:active { - transform: translateY(1px); + transform: translateY(0); } .btn-primary { background: var(--text); @@ -342,63 +415,110 @@ border-color: transparent; } .btn-primary:hover { - background: var(--brand-2); + background: var(--brand); + color: var(--bg); + box-shadow: 0 12px 30px + color-mix(in srgb, var(--brand) 34%, transparent); + } + .btn-secondary { + background: color-mix(in srgb, var(--panel) 55%, transparent); } .btn-secondary:hover { border-color: var(--brand); color: var(--brand-2); + background: var(--tint-soft); } + /* The install affordance — the page's one crafted "instrument". A framed + prompt with an ember caret, sized in the mono display voice. */ .command { - margin-top: 20px; + margin-top: var(--sp-6); display: flex; - max-width: 100%; + max-width: 640px; align-items: center; - gap: 12px; - border: 1px solid var(--line); + gap: var(--sp-3); + border: 1px solid var(--line-strong); border-radius: var(--radius-sm); - padding: 12px 14px; + padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4); background: var(--panel); - box-shadow: var(--shadow); + box-shadow: var(--shadow), var(--bezel); overflow-x: auto; - font: 700 13px var(--mono); + font: 600 var(--fs-n1) var(--mono); + transition: border-color 0.18s var(--ease-out); + } + .command:hover { + border-color: color-mix(in srgb, var(--brand) 55%, var(--line-strong)); } .command .prompt { color: var(--brand); flex: none; + font-weight: 700; } .command code { flex: 1; white-space: nowrap; + color: var(--text); } .command button { flex: none; border: 1px solid var(--line-strong); background: var(--panel-2); color: var(--muted); - border-radius: var(--radius-sm); - padding: 6px 12px; + border-radius: 8px; + padding: var(--sp-2) var(--sp-4); cursor: pointer; - font: 700 12px var(--mono); + font: 650 var(--fs-n2) var(--mono); + letter-spacing: 0.02em; transition: color 0.16s var(--ease-out), - border-color 0.16s var(--ease-out); + border-color 0.16s var(--ease-out), + background 0.16s var(--ease-out); } .command button:hover { color: var(--text); border-color: var(--brand); + background: var(--tint); } /* ---- Hero diagram (the real, shipped SVG) ----------------------- */ .diagram { - margin: 56px 0 0; + margin: var(--sp-16) 0 0; } .diagram figure { + position: relative; margin: 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--panel); - box-shadow: var(--shadow); + box-shadow: var(--shadow), var(--bezel); + } + /* Schematic corner ticks — the signature detail. Two opposed L-brackets read + as an instrument bezel / blueprint crop, quietly reinforcing "engineered, + measured infrastructure" without adding chrome. */ + .diagram figure::before, + .diagram figure::after { + content: ""; + position: absolute; + width: 14px; + height: 14px; + border: 1.5px solid var(--brand); + opacity: 0.55; + pointer-events: none; + z-index: 1; + } + .diagram figure::before { + top: 12px; + left: 12px; + border-right: 0; + border-bottom: 0; + border-top-left-radius: 4px; + } + .diagram figure::after { + bottom: 12px; + right: 12px; + border-left: 0; + border-top: 0; + border-bottom-right-radius: 4px; } /* The real shipped hero art, inlined so it resolves in local preview AND on the deployed site root (the deploy copies only index.html, not docs/assets). @@ -421,9 +541,12 @@ } .diagram figcaption { color: var(--faint); - font: 500 13px var(--mono); - margin-top: 14px; + font: 500 var(--fs-n1) var(--mono); + line-height: 1.6; + margin-top: var(--sp-5); text-align: center; + max-width: 68ch; + margin-inline: auto; } /* ---- Stat band -------------------------------------------------- */ @@ -431,36 +554,49 @@ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; - margin: 56px 0 0; + margin: var(--sp-16) 0 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--panel); + box-shadow: var(--bezel); } .stat { - padding: 26px 24px; + position: relative; + padding: var(--sp-8) var(--sp-6) var(--sp-7); border-right: 1px solid var(--line); } + /* Each readout wears a short ember gauge-tick above the figure. */ + .stat::before { + content: ""; + position: absolute; + top: 0; + left: var(--sp-6); + width: 26px; + height: 2px; + background: var(--brand); + } .stat:last-child { border-right: 0; } .stat b { display: block; - font: 850 clamp(26px, 4vw, 38px) var(--mono); + font: 700 var(--fs-5) var(--mono); letter-spacing: -0.04em; - color: var(--brand-2); + font-variant-numeric: tabular-nums; + color: var(--text); } .stat span { display: block; - margin-top: 6px; + margin-top: var(--sp-3); color: var(--muted); - font-size: 14px; + font-size: var(--fs-n1); } .stat small { display: block; - margin-top: 8px; + margin-top: var(--sp-2); color: var(--faint); - font: 400 11px var(--mono); + font: 400 var(--fs-n2) var(--mono); } @media (max-width: 860px) { .stats { @@ -489,67 +625,106 @@ /* ---- Sections --------------------------------------------------- */ section { - padding: 84px 0; + padding: var(--sp-24) 0; border-top: 1px solid var(--line); } + /* Section reference tag — a schematic label, not decoration: an ember index + mark keys the eye to each stage of the story. */ .kicker { - margin: 0 0 12px; + display: inline-flex; + align-items: center; + gap: var(--sp-3); + margin: 0 0 var(--sp-5); color: var(--brand); - font: 800 12px var(--mono); - letter-spacing: 0.14em; + font: 700 var(--fs-n2) var(--mono); + letter-spacing: 0.2em; text-transform: uppercase; } + .kicker::before { + content: ""; + width: 22px; + height: 2px; + background: var(--brand); + } h2 { margin: 0; - max-width: 20ch; - font-size: clamp(30px, 5vw, 50px); - line-height: 1.05; - letter-spacing: -0.04em; + max-width: 22ch; + font: 700 var(--fs-5) / 1.08 var(--mono); + letter-spacing: -0.035em; overflow-wrap: anywhere; min-width: 0; } .sub { color: var(--muted); - margin: 16px 0 0; - font-size: 18px; - max-width: 60ch; + margin: var(--sp-5) 0 0; + font-size: var(--fs-1); + line-height: 1.6; + max-width: 62ch; text-wrap: pretty; } /* Capability list — icon + copy rows divided by hairlines, NOT a grid of identical bordered icon-tiles (breaks the card-in-card / icon-grid tell). */ .caps { - margin: 48px 0 0; + margin: var(--sp-12) 0 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0 56px; + gap: 0 var(--sp-16); } .cap { + position: relative; display: grid; - grid-template-columns: 40px 1fr; - gap: 18px; + grid-template-columns: 42px 1fr; + gap: var(--sp-4); align-items: start; - padding: 26px 0; + padding: var(--sp-7) 0; border-top: 1px solid var(--line); } + /* Hairline lights up on hover — a low-key affordance that keeps the eye moving. */ + .cap::before { + content: ""; + position: absolute; + top: -1px; + left: 0; + width: 0; + height: 2px; + background: var(--brand); + transition: width 0.32s var(--ease-out); + } + .cap:hover::before { + width: 42px; + } .cap-ico { - width: 40px; - height: 40px; + width: 42px; + height: 42px; + display: grid; + place-items: center; + border: 1px solid var(--line); + border-radius: 11px; + background: var(--tint-soft); color: var(--brand-2); + transition: + border-color 0.2s var(--ease-out), + background 0.2s var(--ease-out); + } + .cap:hover .cap-ico { + border-color: color-mix(in srgb, var(--brand) 45%, var(--line)); + background: var(--tint); } .cap-ico svg { - width: 100%; - height: 100%; + width: 22px; + height: 22px; } .cap h3 { - margin: 2px 0 8px; - font-size: 19px; + margin: var(--sp-1) 0 var(--sp-2); + font: 600 var(--fs-2) var(--sans); letter-spacing: -0.01em; } .cap p { margin: 0; color: var(--muted); - font-size: 15px; + font-size: var(--fs-n1); + line-height: 1.6; } @media (max-width: 760px) { .caps { @@ -561,67 +736,83 @@ terminal window: no traffic-light dots, no mock title bar). */ .split { display: grid; - grid-template-columns: minmax(280px, 0.92fr) minmax(0, 1.08fr); - gap: 40px; + grid-template-columns: minmax(280px, 0.9fr) minmax(0, 1.1fr); + gap: var(--sp-12); align-items: start; } .steps { display: grid; - gap: 12px; - margin-top: 32px; + gap: var(--sp-3); + margin-top: var(--sp-8); counter-reset: step; } .step { display: grid; - grid-template-columns: 34px 1fr; - gap: 16px; + grid-template-columns: 36px 1fr; + gap: var(--sp-5); align-items: start; - padding: 18px 0; + padding: var(--sp-5) 0; border-top: 1px solid var(--line); } + /* A real, ordered sequence — so the numeric markers carry information. */ .step-num { counter-increment: step; - width: 34px; - height: 34px; - border-radius: var(--radius-sm); + width: 36px; + height: 36px; + border-radius: 10px; display: grid; place-items: center; - background: var(--panel-2); - border: 1px solid var(--line); + background: var(--tint); + border: 1px solid color-mix(in srgb, var(--brand) 32%, var(--line)); color: var(--brand-2); - font: 800 14px var(--mono); + font: 700 var(--fs-n1) var(--mono); + font-variant-numeric: tabular-nums; } .step-num::before { - content: counter(step); + content: counter(step, decimal-leading-zero); } .step h3 { - margin: 4px 0 4px; - font-size: 17px; + margin: var(--sp-1) 0 var(--sp-2); + font: 600 var(--fs-2) var(--sans); + letter-spacing: -0.01em; } .step p { margin: 0; color: var(--muted); + line-height: 1.6; } figure.listing { - margin: 32px 0 0; + margin: 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel-2); - box-shadow: var(--shadow); + box-shadow: var(--shadow), var(--bezel); overflow: hidden; } figure.listing figcaption { - padding: 12px 20px; + display: flex; + align-items: center; + gap: var(--sp-3); + padding: var(--sp-3) var(--sp-5); border-bottom: 1px solid var(--line); color: var(--faint); - font: 600 12px var(--mono); - letter-spacing: 0.04em; + font: 600 var(--fs-n2) var(--mono); + letter-spacing: 0.06em; + text-transform: uppercase; + } + figure.listing figcaption::before { + content: ""; + width: 7px; + height: 7px; + border-radius: 999px; + background: var(--brand); + flex: none; } pre { margin: 0; - padding: 20px; + padding: var(--sp-6) var(--sp-5); overflow-x: auto; - font: 13px/1.7 var(--mono); + font: var(--fs-n1) / 1.75 var(--mono); color: var(--text); } .c-dim { @@ -638,73 +829,82 @@ .honesty { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 40px; - margin-top: 44px; + gap: var(--sp-10); + margin-top: var(--sp-12); } .honest { display: grid; - grid-template-columns: 40px 1fr; - gap: 18px; + grid-template-columns: 42px 1fr; + gap: var(--sp-4); align-items: start; - padding-top: 26px; + padding-top: var(--sp-7); border-top: 2px solid var(--brand); } .honest .cap-ico { color: var(--brand); + border-color: color-mix(in srgb, var(--brand) 30%, var(--line)); } .honest h3 { - margin: 2px 0 8px; - font-size: 19px; + margin: var(--sp-1) 0 var(--sp-2); + font: 600 var(--fs-2) var(--sans); + letter-spacing: -0.01em; } .honest p { margin: 0; color: var(--muted); + line-height: 1.6; } @media (max-width: 640px) { .honesty { grid-template-columns: 1fr; - gap: 8px; + gap: var(--sp-6); } } /* Data sources */ .sources { display: grid; - gap: 10px; + gap: var(--sp-3); padding: 0; - margin: 32px 0 0; + margin: var(--sp-8) 0 0; list-style: none; } .sources li { overflow-wrap: anywhere; border-left: 3px solid var(--brand); - padding: 8px 0 8px 14px; + padding: var(--sp-2) 0 var(--sp-2) var(--sp-4); color: var(--muted); - font: 500 14px var(--mono); + font: 500 var(--fs-n1) var(--mono); } footer { border-top: 1px solid var(--line); - padding: 34px 0; + padding: var(--sp-10) 0; color: var(--muted); } .foot { display: flex; justify-content: space-between; - gap: 18px; + gap: var(--sp-5); flex-wrap: wrap; } + .foot .mono { + font-size: var(--fs-n1); + color: var(--faint); + } .foot a { color: var(--muted); text-decoration: none; - margin-left: 16px; + margin-left: var(--sp-5); white-space: nowrap; + font: 500 var(--fs-n1) var(--mono); + transition: color 0.16s var(--ease-out); } .foot a:first-child { margin-left: 0; } .foot a:hover { - color: var(--text); + color: var(--brand-2); } @media (max-width: 900px) { @@ -714,14 +914,56 @@ } @media (max-width: 560px) { .hero { - padding-top: 52px; + padding-top: var(--sp-16); } section { - padding: 60px 0; + padding: var(--sp-16) 0; } .foot a { margin-left: 0; - margin-right: 14px; + margin-right: var(--sp-5); + } + } + + /* Orchestrated hero entrance — one calm, staggered rise on load. Gated behind + no-preference so reduced-motion visitors never see a hidden/animated state. */ + @media (prefers-reduced-motion: no-preference) { + .hero > .wrap > .eyebrow, + .hero > .wrap > h1, + .hero > .wrap > .lede, + .hero > .wrap > .actions, + .hero > .wrap > .command, + .hero > .wrap > .diagram, + .hero > .wrap > .stats { + animation: rise 0.72s var(--ease-out) both; + } + .hero > .wrap > h1 { + animation-delay: 0.06s; + } + .hero > .wrap > .lede { + animation-delay: 0.14s; + } + .hero > .wrap > .actions { + animation-delay: 0.2s; + } + .hero > .wrap > .command { + animation-delay: 0.26s; + } + .hero > .wrap > .diagram { + animation-delay: 0.34s; + } + .hero > .wrap > .stats { + animation-delay: 0.4s; + } + @keyframes rise { + from { + opacity: 0; + transform: translateY(14px); + } + to { + opacity: 1; + transform: translateY(0); + } } } diff --git a/scripts/build-pages.mjs b/scripts/build-pages.mjs index 253ef54..0bca304 100644 --- a/scripts/build-pages.mjs +++ b/scripts/build-pages.mjs @@ -195,43 +195,43 @@ export function render(d) { ${rootTokensCss()} :root{--r-s:6px;--r-m:12px;--r-pill:999px} *{box-sizing:border-box} -body{margin:0;background:radial-gradient(960px 400px at 85% -10%,rgba(242,100,48,.16),transparent 70%),var(--bg);color:var(--text);font:16px/1.65 var(--sans);-webkit-font-smoothing:antialiased} +body{margin:0;background:radial-gradient(960px 400px at 85% -10%,rgba(242,100,48,.16),transparent 70%),var(--bg);color:var(--text);font:var(--fs-0)/1.65 var(--sans);-webkit-font-smoothing:antialiased} ::selection{background:var(--brand);color:var(--bg)} -p{margin:16px 0} +p{margin:var(--sp-4) 0} a{color:inherit} -a:focus-visible,button:focus-visible{outline:2px solid var(--brand);outline-offset:4px;border-radius:var(--r-s)} +a:focus-visible,button:focus-visible{outline:2px solid var(--brand);outline-offset:var(--sp-1);border-radius:var(--r-s)} .wrap{width:min(1080px,calc(100% - 48px));margin:0 auto} -.nav{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)} -.brand{font:600 16px var(--mono);text-decoration:none} +.nav{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);padding:var(--sp-4) 0;border-bottom:1px solid var(--line)} +.brand{font:600 var(--fs-0) var(--mono);text-decoration:none} .brand em{font-style:normal;color:var(--brand)} -.links{display:flex;gap:24px;flex-wrap:wrap;font-size:14px} +.links{display:flex;gap:var(--sp-6);flex-wrap:wrap;font-size:var(--fs-n1)} .links a{color:var(--muted);text-decoration:none} .links a:hover{color:var(--text)} -.hero{padding:64px 0 48px} -.eyebrow{font:500 13px var(--mono);color:var(--brand);letter-spacing:.12em;text-transform:uppercase;margin:0 0 16px} -h1{font-size:clamp(32px,5vw,56px);line-height:1.05;letter-spacing:-.02em;margin:0 0 16px;max-width:800px;font-weight:700} -.lead{font-size:18px;color:var(--muted);max-width:680px;margin:0 0 32px} -.btn{display:inline-block;text-decoration:none;font:600 15px var(--sans);border-radius:var(--r-pill);padding:12px 28px;border:1px solid var(--line)} +.hero{padding:var(--sp-16) 0 var(--sp-12)} +.eyebrow{font:500 var(--fs-n1) var(--mono);color:var(--brand);letter-spacing:.12em;text-transform:uppercase;margin:0 0 var(--sp-4)} +h1{font-size:var(--fs-6);line-height:1.05;letter-spacing:-.02em;margin:0 0 var(--sp-4);max-width:800px;font-weight:700} +.lead{font-size:var(--fs-1);color:var(--muted);max-width:680px;margin:0 0 var(--sp-8)} +.btn{display:inline-block;text-decoration:none;font:600 var(--fs-n1) var(--sans);border-radius:var(--r-pill);padding:var(--sp-3) var(--sp-7);border:1px solid var(--line)} .btn.primary{background:var(--brand);border-color:var(--brand);color:var(--bg)} .btn.primary:hover{background:var(--text);border-color:var(--text)} .btn:not(.primary):hover{border-color:var(--faint)} -.meta{display:flex;gap:12px;flex-wrap:wrap;margin-top:32px} -.chip{font:500 13px var(--mono);color:var(--muted);border:1px solid var(--line);border-radius:var(--r-pill);padding:8px 16px} -.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:48px 0} -.cell{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-m);padding:32px 24px} -.metric{font:600 32px var(--mono);letter-spacing:-.02em} +.meta{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin-top:var(--sp-8)} +.chip{font:500 var(--fs-n1) var(--mono);color:var(--muted);border:1px solid var(--line);border-radius:var(--r-pill);padding:var(--sp-2) var(--sp-4)} +.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4);margin:var(--sp-12) 0} +.cell{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-m);padding:var(--sp-8) var(--sp-6)} +.metric{font:600 var(--fs-4) var(--mono);letter-spacing:-.02em} .metric em{font-style:normal;color:var(--brand)} -.cell strong{display:block;margin-top:8px;font-size:14px} -.muted{color:var(--muted);font-size:14px} -.src{font:400 12px var(--mono);color:var(--faint);margin-top:12px} -section{padding:48px 0;border-bottom:1px solid var(--line)} -h2{font-size:24px;letter-spacing:-.01em;margin:0 0 16px} -.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-m);padding:32px} -.terminal{font:400 14px/1.7 var(--mono);overflow-x:auto;white-space:pre;background:var(--panel-2);color:var(--text);border-radius:var(--r-m);padding:24px;border:1px solid var(--line);box-shadow:var(--shadow)} -.list{display:grid;gap:12px;padding:0;list-style:none;margin:0} -.list li{border-left:2px solid var(--brand);padding-left:16px;color:var(--muted);font-size:15px} -code{font:500 13px var(--mono);background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r-s);padding:0 8px} -footer{padding:32px 0;color:var(--faint);font-size:14px} +.cell strong{display:block;margin-top:var(--sp-2);font-size:var(--fs-n1)} +.muted{color:var(--muted);font-size:var(--fs-n1)} +.src{font:400 var(--fs-n2) var(--mono);color:var(--faint);margin-top:var(--sp-3)} +section{padding:var(--sp-12) 0;border-bottom:1px solid var(--line)} +h2{font-size:var(--fs-2);letter-spacing:-.01em;margin:0 0 var(--sp-4)} +.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-m);padding:var(--sp-8)} +.terminal{font:400 var(--fs-n1)/1.7 var(--mono);overflow-x:auto;white-space:pre;background:var(--panel-2);color:var(--text);border-radius:var(--r-m);padding:var(--sp-6);border:1px solid var(--line);box-shadow:var(--shadow)} +.list{display:grid;gap:var(--sp-3);padding:0;list-style:none;margin:0} +.list li{border-left:2px solid var(--brand);padding-left:var(--sp-4);color:var(--muted);font-size:var(--fs-n1)} +code{font:500 var(--fs-n1) var(--mono);background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r-s);padding:0 var(--sp-2)} +footer{padding:var(--sp-8) 0;color:var(--faint);font-size:var(--fs-n1)} @media(max-width:800px){.grid{grid-template-columns:1fr}}
${esc(d.name)} · v${esc(d.version)} · Node ${esc(d.node)}
${esc(d.description)}
Measured from this repo's benchmark report, not a marketing placeholder.
reports/benchmarks.md
Assumptions, routing, reuse, context, impact, scope, and anchoring.
reports/benchmarks.md
Documented from the white-paper prototype and exposed by Forge cost reports.
whitepaper prototype