Skip to content

Commit eb2b7e1

Browse files
committed
docs(cover): re-render the hero cover on the slogan, with its template beside it
The README hero (docs/screenshots/hero-cover-dark.png, 2400x1200) and the docs site's og:image / video poster (apps/docs/public/hero-cover-dark.webp) now carry the headline "The ontology is the software.", the descriptor and the chips Executable · AI-writable · Agent-operable · You own it · Apache-2.0. There was no design source in the repo; the cover is now a screenshot of docs/screenshots/hero-cover-dark.html taken by docs/screenshots/render-hero-cover.mjs with the preinstalled Playwright Chromium (resolved from the showcase example's @playwright/test), and the webp is derived from that PNG by sharp (quality 80, effort 6, as the HERO_COVER provenance block prescribes; sharp resolved from the docs app's Next dependency). The provenance block records the new bytes (563,953 B PNG, 85,472 B webp) and sharp 0.35.4; the declared 2400x1200 is unchanged and matches the files. Claude-Session: https://claude.ai/code/session_011hRnra93sK5Q2gTYYTbdJR Co-authored-by: Claude <noreply@anthropic.com>
1 parent 352c97d commit eb2b7e1

5 files changed

Lines changed: 392 additions & 5 deletions

File tree

‎apps/docs/lib/site.ts‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,20 @@ export function absoluteUrl(path: string): string {
6060
*
6161
* ## Provenance — how to re-encode it
6262
*
63-
* The master is `docs/screenshots/hero-cover-dark.png` (2400x1200, 406,703 B),
63+
* The master is `docs/screenshots/hero-cover-dark.png` (2400x1200, 563,953 B),
6464
* which the README embeds directly from the repo and which is NOT served by this
65-
* site. This file is a lossy WebP derived from that master, and the derivation is
66-
* the whole reason it is small enough to sit above the fold:
65+
* site. The master is itself a render of `docs/screenshots/hero-cover-dark.html`
66+
* by `docs/screenshots/render-hero-cover.mjs` (the preinstalled Playwright
67+
* Chromium), so a copy change is an edit to that template and one re-render —
68+
* the script performs the derivation below as its second step. This file is a
69+
* lossy WebP derived from that master, and the derivation is the whole reason it
70+
* is small enough to sit above the fold:
6771
*
6872
* sharp('docs/screenshots/hero-cover-dark.png')
69-
* .webp({ quality: 80, effort: 6 }) // sharp 0.35.3 / libwebp 1.6.0
73+
* .webp({ quality: 80, effort: 6 }) // sharp 0.35.4 / libwebp 1.6.0
7074
* .toFile('apps/docs/public/hero-cover-dark.webp')
7175
*
72-
* 83,272 B — 20.5% of the PNG. Re-encode from the master, never from this file:
76+
* 85,472 B — 15.2% of the PNG. Re-encode from the master, never from this file:
7377
* a lossy re-encode of a lossy source compounds. And do not "restore" the PNG
7478
* alongside it — a `public/` holding both is how the next re-encode picks the
7579
* wrong one.
2.15 KB
Loading
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
<!doctype html>
2+
<!--
3+
hero-cover-dark.html — the design source of the README hero cover.
4+
5+
Renders at exactly 2400 x 1200. The committed master
6+
`docs/screenshots/hero-cover-dark.png` is a screenshot of this page, and
7+
`apps/docs/public/hero-cover-dark.webp` (the docs site's og:image and video
8+
poster) is derived from that PNG — see the provenance block on `HERO_COVER` in
9+
`apps/docs/lib/site.ts`. The next copy change is an edit here plus one
10+
re-render; nothing is hand-painted.
11+
12+
Re-render both files (the preinstalled Playwright Chromium, never
13+
`playwright install`; sharp resolves from the docs app's Next dependency):
14+
15+
node docs/screenshots/render-hero-cover.mjs
16+
17+
Preview without touching the committed files:
18+
19+
node docs/screenshots/render-hero-cover.mjs --out /tmp/hero-preview
20+
21+
The equivalent of the webp step by hand, from the PNG master and never from a
22+
previous webp (a lossy re-encode of a lossy source compounds):
23+
24+
sharp('docs/screenshots/hero-cover-dark.png')
25+
.webp({ quality: 80, effort: 6 })
26+
.toFile('apps/docs/public/hero-cover-dark.webp')
27+
28+
Typography is Inter and IBM Plex Mono from Google Fonts; the render script
29+
refuses to write the committed files when those fonts did not load, so an
30+
offline render cannot silently ship the system fallback.
31+
32+
Copy on this page is fixed by the positioning card: the slogan, the
33+
descriptor and the four promises are quoted verbatim from README.md and must
34+
change together with it.
35+
-->
36+
<html lang="en">
37+
<head>
38+
<meta charset="utf-8">
39+
<title>ObjectStack — hero cover</title>
40+
<link rel="preconnect" href="https://fonts.googleapis.com">
41+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
42+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=IBM+Plex+Mono:wght@400;500&display=block" rel="stylesheet">
43+
<style>
44+
:root {
45+
--bg: #08080f;
46+
--bg-2: #0d0e18;
47+
--grid: rgba(148, 163, 230, 0.075);
48+
--fg: #f5f6fa;
49+
--fg-2: #c7cad6;
50+
--fg-3: #9598ab;
51+
--indigo: #818cf8;
52+
--violet: #a78bfa;
53+
--purple: #bd84fc;
54+
--card: #12131f;
55+
--card-2: #191920;
56+
--line: #2a2a30;
57+
--amber: #f5a623;
58+
--dash-bg: #f4f5f8;
59+
--dash-card: #ffffff;
60+
--dash-line: #e6e8ee;
61+
--dash-fg: #1f2433;
62+
--dash-fg-2: #6b7085;
63+
--kpi-blue: #2f5fd6;
64+
--kpi-green: #1a9c6e;
65+
--kpi-orange: #e8752a;
66+
--font-sans: Inter, 'Liberation Sans', 'DejaVu Sans', system-ui, sans-serif;
67+
--font-mono: 'IBM Plex Mono', 'Liberation Mono', 'DejaVu Sans Mono', monospace;
68+
}
69+
* { box-sizing: border-box; margin: 0; padding: 0; }
70+
html, body { width: 2400px; height: 1200px; overflow: hidden; }
71+
body {
72+
position: relative;
73+
background: var(--bg);
74+
color: var(--fg);
75+
font-family: var(--font-sans);
76+
-webkit-font-smoothing: antialiased;
77+
text-rendering: geometricPrecision;
78+
}
79+
.backdrop {
80+
position: absolute; inset: 0;
81+
background:
82+
radial-gradient(1100px 760px at 0% 0%, rgba(99, 102, 241, 0.30), transparent 70%),
83+
radial-gradient(900px 600px at 100% 0%, rgba(99, 102, 241, 0.10), transparent 70%),
84+
linear-gradient(180deg, var(--bg-2) 0%, var(--bg) 55%, #090b11 100%);
85+
}
86+
.grid {
87+
position: absolute; inset: 0;
88+
background-image:
89+
linear-gradient(to right, var(--grid) 1px, transparent 1px),
90+
linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
91+
background-size: 80px 80px;
92+
background-position: 32px 32px;
93+
-webkit-mask-image: radial-gradient(ellipse 85% 75% at 45% 40%, black 30%, transparent 100%);
94+
mask-image: radial-gradient(ellipse 85% 75% at 45% 40%, black 30%, transparent 100%);
95+
}
96+
97+
/* ── left column ───────────────────────────────────────── */
98+
.left { position: absolute; left: 112px; top: 0; width: 1040px; height: 1200px; }
99+
.brand { position: absolute; top: 236px; left: 0; display: flex; align-items: center; gap: 18px; }
100+
.brand svg { width: 56px; height: 56px; }
101+
.brand span { font-size: 44px; font-weight: 700; letter-spacing: -0.02em; color: var(--fg); }
102+
h1 {
103+
position: absolute; top: 330px; left: -6px;
104+
font-size: 128px; line-height: 1.02; font-weight: 800; letter-spacing: -0.038em;
105+
color: var(--fg); white-space: nowrap;
106+
}
107+
h1 .accent {
108+
display: block;
109+
background: linear-gradient(90deg, var(--indigo) 0%, var(--violet) 55%, var(--purple) 100%);
110+
-webkit-background-clip: text; background-clip: text; color: transparent;
111+
padding-right: 0.08em; /* keeps the period's clip box clear of the gradient edge */
112+
}
113+
.sub { position: absolute; left: 0; width: 1040px; font-weight: 400; letter-spacing: -0.012em; }
114+
.sub-1 { top: 598px; font-size: 40px; color: var(--fg-2); }
115+
.sub-1 b { font-weight: 600; color: var(--fg); }
116+
.sub-2 { top: 658px; font-size: 36px; line-height: 1.32; color: var(--fg-3); }
117+
.cmd {
118+
position: absolute; top: 798px; left: 0; display: inline-flex; align-items: center; gap: 18px;
119+
padding: 0 30px; height: 72px; border-radius: 14px;
120+
background: var(--card-2); border: 1.5px solid var(--line);
121+
font-family: var(--font-mono); font-size: 29px; color: var(--fg-2);
122+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
123+
}
124+
.cmd .prompt { color: var(--indigo); }
125+
.chips { position: absolute; top: 912px; left: 0; display: flex; gap: 14px; white-space: nowrap; }
126+
.chip {
127+
display: inline-flex; align-items: center; height: 56px; padding: 0 24px; border-radius: 12px;
128+
background: #19191f; border: 1.5px solid var(--line);
129+
font-size: 24px; font-weight: 500; letter-spacing: -0.01em; color: var(--fg-2);
130+
}
131+
.chip.licence {
132+
font-family: var(--font-mono); font-size: 23px; font-weight: 500; color: var(--indigo);
133+
border-color: rgba(129, 140, 248, 0.55); background: rgba(129, 140, 248, 0.08);
134+
}
135+
136+
/* ── right column ──────────────────────────────────────── */
137+
.code {
138+
position: absolute; left: 1190px; top: 130px; width: 598px; height: 462px;
139+
border-radius: 18px; background: var(--card); border: 1.5px solid var(--line);
140+
box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5); padding: 32px 34px;
141+
font-family: var(--font-mono); font-size: 26px; line-height: 40px; color: var(--fg-2); z-index: 3;
142+
}
143+
.code .file { font-size: 20px; letter-spacing: 0.14em; color: var(--fg-3); margin-bottom: 14px; }
144+
.code pre { font: inherit; white-space: pre; }
145+
.code .s { color: var(--indigo); }
146+
.code .fn { color: var(--amber); }
147+
.code .pn { color: var(--fg-3); }
148+
.pill {
149+
position: absolute; left: 1205px; top: 611px; height: 60px; padding: 0 30px; border-radius: 30px;
150+
display: inline-flex; align-items: center; gap: 12px; z-index: 3;
151+
background: linear-gradient(90deg, #6d6cf5 0%, #9b6cf6 100%);
152+
font-size: 25px; font-weight: 700; letter-spacing: -0.01em; color: #fff;
153+
box-shadow: 0 12px 30px rgba(99, 102, 241, 0.45);
154+
}
155+
.dash {
156+
position: absolute; left: 1442px; top: 456px; width: 1120px; height: 720px;
157+
border-radius: 20px 0 0 20px; background: var(--dash-bg);
158+
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55); overflow: hidden; z-index: 2;
159+
color: var(--dash-fg); font-family: var(--font-sans);
160+
}
161+
.dash .bar {
162+
height: 46px; border-bottom: 1px solid var(--dash-line); background: #fff;
163+
display: flex; align-items: center; justify-content: flex-start; padding: 0 0 0 352px;
164+
font-size: 15px; color: var(--dash-fg-2);
165+
}
166+
.dash .bar .sel { border: 1px solid var(--dash-line); border-radius: 6px; padding: 4px 10px; }
167+
.dash .body { padding: 22px 0 0 20px; display: grid; grid-template-columns: 322px 322px 322px 322px; gap: 20px; }
168+
.kpi, .panel { background: var(--dash-card); border: 1px solid var(--dash-line); border-radius: 10px; padding: 18px 20px; }
169+
.kpi .t { font-size: 15px; font-weight: 600; }
170+
.kpi .d { font-size: 11px; color: var(--dash-fg-2); margin-top: 4px; }
171+
.kpi .v { font-size: 30px; font-weight: 700; margin-top: 34px; letter-spacing: -0.02em; }
172+
.kpi .l { font-size: 11px; color: var(--dash-fg-2); margin-top: 2px; }
173+
.blue { color: var(--kpi-blue); } .green { color: var(--kpi-green); } .orange { color: var(--kpi-orange); }
174+
.panel { grid-column: span 2; height: 440px; }
175+
.panel .t { font-size: 15px; font-weight: 600; }
176+
.panel .d { font-size: 11px; color: var(--dash-fg-2); margin-top: 4px; }
177+
.panel svg { display: block; margin-top: 22px; width: 100%; height: 340px; }
178+
.wide { grid-column: span 2; }
179+
</style>
180+
</head>
181+
<body>
182+
<div class="backdrop"></div>
183+
<div class="grid"></div>
184+
185+
<div class="left">
186+
<div class="brand" aria-label="ObjectStack">
187+
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
188+
<defs>
189+
<linearGradient id="logo-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
190+
<stop offset="0%" stop-color="#818cf8" />
191+
<stop offset="100%" stop-color="#c084fc" />
192+
</linearGradient>
193+
</defs>
194+
<path d="M12 22.5L2 17.5L12 12.5L22 17.5L12 22.5Z" fill="url(#logo-gradient)" fill-opacity="0.6" />
195+
<path d="M12 17.5L2 12.5L12 7.5L22 12.5L12 17.5Z" fill="url(#logo-gradient)" fill-opacity="0.8" />
196+
<path d="M12 12.5L2 7.5L12 2.5L22 7.5L12 12.5Z" fill="url(#logo-gradient)" fill-opacity="1" />
197+
</svg>
198+
<span>ObjectStack</span>
199+
</div>
200+
201+
<h1>The ontology<span class="accent">is the software.</span></h1>
202+
203+
<p class="sub sub-1"><b>One executable business ontology.</b></p>
204+
<p class="sub sub-2">AI writes it, the runtime runs it, agents operate it, you own it.</p>
205+
206+
<div class="cmd"><span class="prompt">$</span><span>npm create objectstack@latest</span></div>
207+
208+
<div class="chips">
209+
<span class="chip">Executable</span>
210+
<span class="chip">AI-writable</span>
211+
<span class="chip">Agent-operable</span>
212+
<span class="chip">You own it</span>
213+
<span class="chip licence">Apache-2.0</span>
214+
</div>
215+
</div>
216+
217+
<div class="code">
218+
<div class="file">TICKET.OBJECT.TS</div>
219+
<pre>ObjectSchema.create({
220+
name: <span class="s">'support_ticket'</span>,
221+
fields: {
222+
subject: Field.<span class="fn">text</span>(),
223+
status: Field.<span class="fn">select</span>([<span class="pn">…</span>]),
224+
owner: Field.<span class="fn">lookup</span>(<span class="s">'user'</span>),
225+
},
226+
sharingModel: <span class="s">'private'</span>,
227+
})</pre>
228+
</div>
229+
230+
<div class="pill">one definition <span aria-hidden="true">→</span></div>
231+
232+
<div class="dash" aria-hidden="true">
233+
<div class="bar"><span class="sel">All ⌄</span></div>
234+
<div class="body">
235+
<div class="kpi"><div class="t">Closed Won (QTD)</div><div class="d">Revenue closed this quarter</div><div class="v blue">2,510,000</div><div class="l">Total Amount</div></div>
236+
<div class="kpi"><div class="t">Open Opportunities</div><div class="d">Active deals in flight</div><div class="v orange">5</div><div class="l">Opportunities</div></div>
237+
<div class="kpi"><div class="t">Win Rate (12M)</div><div class="d">Deals won as a share of all deals settled</div><div class="v green">62%</div><div class="l">Win Rate</div></div>
238+
<div class="kpi"><div class="t">Deals Won (12M)</div><div class="d">The numerator of the win rate</div><div class="v blue">8</div><div class="l">Won Deals</div></div>
239+
<div class="panel">
240+
<div class="t">Pipeline by Stage</div><div class="d">Open opportunity value at each sales stage</div>
241+
<svg viewBox="0 0 600 340">
242+
<rect x="40" y="22" width="520" height="44" rx="5" fill="#2f5fd6"/>
243+
<rect x="90" y="86" width="420" height="44" rx="5" fill="#4b7be0"/>
244+
<rect x="140" y="150" width="320" height="44" rx="5" fill="#6d94ea"/>
245+
<rect x="190" y="214" width="220" height="44" rx="5" fill="#1a9c6e"/>
246+
<rect x="235" y="278" width="130" height="44" rx="5" fill="#30b68a"/>
247+
<g font-size="15" font-weight="500" fill="#fff" text-anchor="middle" font-family="Inter">
248+
<text x="300" y="49">Qualification</text>
249+
<text x="300" y="113">Needs Analysis</text>
250+
<text x="300" y="177">Proposal</text>
251+
<text x="300" y="241">Negotiation</text>
252+
<text x="300" y="305">Closed</text>
253+
</g>
254+
</svg>
255+
</div>
256+
<div class="panel">
257+
<div class="t">Monthly Revenue Trend</div><div class="d">Closed-won revenue, last 12 months</div>
258+
<svg viewBox="0 0 600 220">
259+
<g stroke="#e6e8ee" stroke-width="1">
260+
<line x1="60" y1="30" x2="580" y2="30"/><line x1="60" y1="80" x2="580" y2="80"/>
261+
<line x1="60" y1="130" x2="580" y2="130"/><line x1="60" y1="180" x2="580" y2="180"/>
262+
</g>
263+
<g font-size="11" fill="#6b7085" font-family="Inter" text-anchor="end">
264+
<text x="52" y="34">300,000</text><text x="52" y="84">200,000</text><text x="52" y="134">100,000</text><text x="52" y="184">0</text>
265+
</g>
266+
<polyline fill="none" stroke="#2f5fd6" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"
267+
points="60,150 105,140 150,155 195,120 240,128 285,100 330,110 375,84 420,92 465,66 510,74 555,48"/>
268+
<polygon fill="#2f5fd6" fill-opacity="0.10"
269+
points="60,150 105,140 150,155 195,120 240,128 285,100 330,110 375,84 420,92 465,66 510,74 555,48 555,180 60,180"/>
270+
</svg>
271+
</div>
272+
</div>
273+
</div>
274+
</body>
275+
</html>
154 KB
Loading

0 commit comments

Comments
 (0)