|
| 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> |
0 commit comments