@bongos/core 1.19.387 → 1.19.388
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +130 -50
- package/config/branding.neutral.json +19 -19
- package/config/design-tokens.neutral.json +31 -36
- package/docs/adr/0196-chrome-world-token-contract-and-the-landing-sheets.md +31 -0
- package/docs/adr/README.md +1 -0
- package/docs/branding-contract.md +1 -1
- package/docs/copy-inventory.md +78 -53
- package/docs/copy-registry.json +389 -120
- package/docs/file-map.md +14 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/renderer/index.html +1 -1
- package/modules/dev-box/app/renderer/styles.css +3 -1
- package/modules/dev-box/app/renderer/theme.generated.css +13 -13
- package/modules/dev-box/app/scripts/gen-brand.js +13 -13
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +3 -3
- package/modules/dev-box/app/scripts/gen-icons.js +3 -3
- package/modules/dev-box/app/src/brand.generated.json +1 -1
- package/modules/provisioning/routes/provisioning.js +4 -4
- package/modules/status-ui/public/explore.html +12 -6
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +82 -64
- package/public-landing/assets/world/og-card.jpg +0 -0
- package/public-landing/assets/world/r2-bongos-chrome-a-cut.webp +0 -0
- package/public-landing/assets/world/r2-bongos-chrome-a-cut.webp.json +12 -0
- package/public-landing/assets/world/r2-bongos-chrome-b.jpg +0 -0
- package/public-landing/assets/world/r2-bongos-chrome-b.jpg.json +12 -0
- package/public-landing/assets/world/r2-chrome-blob-one-cut.webp +0 -0
- package/public-landing/assets/world/r2-chrome-blob-one-cut.webp.json +13 -0
- package/public-landing/assets/world/r2-chrome-blob-one.jpg +0 -0
- package/public-landing/assets/world/r2-chrome-blob-one.jpg.json +12 -0
- package/public-landing/assets/world/r2-chrome-blobs-set-cut.webp +0 -0
- package/public-landing/assets/world/r2-chrome-blobs-set-cut.webp.json +13 -0
- package/public-landing/assets/world/r2-chrome-blobs-set.jpg +0 -0
- package/public-landing/assets/world/r2-chrome-blobs-set.jpg.json +12 -0
- package/public-landing/assets/world.css +241 -0
- package/public-landing/contact.html +1 -1
- package/public-landing/index.html +1139 -580
- package/public-landing/privacy.html +1 -1
- package/public-landing/projects.html +2 -2
- package/public-landing/terms.html +1 -1
- package/src/bongos/platform-visibility-gate.js +8 -7
- package/src/bongos/stealth-gate.js +9 -8
- package/src/module-api.js +3 -1
- package/src/ui/adapters/claude-design/index.js +2 -2
- package/tests/cloud_bongos_pack.mjs +22 -18
- package/tests/cosmos_token_contrast.mjs +23 -8
- package/tests/landing_legal_pages.mjs +8 -5
- package/tests/landing_page.mjs +295 -166
- package/tests/platform_boot.mjs +3 -1
- package/tests/projects_hub.mjs +9 -5
- package/tests/projects_hub_pre_uat.mjs +3 -1
- package/tests/world_tokens.mjs +351 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
|
+
<script>
|
|
6
|
+
/* Mode, resolved before first paint so an explicit choice never flashes. ?mode=
|
|
7
|
+
wins (it is how a reviewer walks both modes on one URL), then the stored
|
|
8
|
+
choice, then the OS preference. */
|
|
9
|
+
(function(){try{var q=new URLSearchParams(location.search).get('mode');var m=(q==='light'||q==='dark')?q:localStorage.getItem('cb-color-mode');if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
|
|
10
|
+
</script>
|
|
5
11
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
12
|
<!-- ── S0 · HEAD METADATA — SERVER-STAMPED ──────────────────────────────────
|
|
7
13
|
The {{…}} tokens below are replaced by landingPage() (src/platform-server.js)
|
|
@@ -15,494 +21,971 @@
|
|
|
15
21
|
The baked values are the NEUTRAL pack's, so opening this file standalone
|
|
16
22
|
reads correctly and leaks nothing about any particular instance.
|
|
17
23
|
─────────────────────────────────────────────────────────────────────── -->
|
|
18
|
-
<title>{{productName}}
|
|
24
|
+
<title>{{productName}}: {{tagline}}</title>
|
|
19
25
|
<meta name="description" content="{{tagline}} {{productName}} is an open, AI-first build platform: agents claim tasks and ship the code, you set the direction and the gates.">
|
|
20
26
|
<link rel="canonical" href="{{publicOrigin}}/">
|
|
21
27
|
<meta property="og:type" content="website">
|
|
22
28
|
<meta property="og:site_name" content="{{productName}}">
|
|
23
29
|
<meta property="og:url" content="{{publicOrigin}}/">
|
|
24
|
-
<meta property="og:title" content="{{productName}}
|
|
30
|
+
<meta property="og:title" content="{{productName}}: {{tagline}}">
|
|
25
31
|
<meta property="og:description" content="{{tagline}} {{productName}} is an open, AI-first build platform: agents claim tasks and ship the code, you set the direction and the gates.">
|
|
26
|
-
<meta property="og:image" content="{{publicOrigin}}/
|
|
32
|
+
<meta property="og:image" content="{{publicOrigin}}/assets/world/og-card.jpg">
|
|
27
33
|
<meta name="twitter:card" content="summary_large_image">
|
|
28
|
-
<meta name="twitter:title" content="{{productName}}
|
|
34
|
+
<meta name="twitter:title" content="{{productName}}: {{tagline}}">
|
|
29
35
|
<meta name="twitter:description" content="{{tagline}} {{productName}} is an open, AI-first build platform: agents claim tasks and ship the code, you set the direction and the gates.">
|
|
30
|
-
<meta name="twitter:image" content="{{publicOrigin}}/
|
|
36
|
+
<meta name="twitter:image" content="{{publicOrigin}}/assets/world/og-card.jpg">
|
|
31
37
|
<!-- Fonts: production also injects these from the branding pack
|
|
32
38
|
(config/branding.json theme.ui.fonts.links); kept here so the page reads
|
|
33
39
|
correctly when viewed standalone. -->
|
|
34
40
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
35
41
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
36
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=
|
|
37
|
-
<!-- The
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
/* Mode, resolved before first paint so an explicit choice never flashes.
|
|
42
|
-
Same contract as /create and /projects: data-mode on <html>, else the OS
|
|
43
|
-
preference decides. */
|
|
44
|
-
(function(){try{var m=localStorage.getItem('cb-color-mode');
|
|
45
|
-
if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
|
|
46
|
-
</script>
|
|
42
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
43
|
+
<!-- The world's token layer. Root-absolute so the server's ?v= content stamp
|
|
44
|
+
applies (a relative href is never stamped, and a four-hour browser cache
|
|
45
|
+
then styles new markup with an old sheet). -->
|
|
46
|
+
<link rel="stylesheet" href="/assets/world.css">
|
|
47
47
|
<style>
|
|
48
48
|
/* ── PAGE-LOCAL SHEET ────────────────────────────────────────────────────
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
/*
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.
|
|
92
|
-
.
|
|
93
|
-
background:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
@keyframes
|
|
126
|
-
@keyframes
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
49
|
+
The composition: how this page arranges the world's sculpture, and nothing
|
|
50
|
+
that another surface in the world would need. There is deliberately NO :root
|
|
51
|
+
block here — injectBrandingGlobal splices the branding pack's theme <style>
|
|
52
|
+
at the END of <head>, and a page-local :root sits at the same specificity,
|
|
53
|
+
so it would be silently outranked one way and defeat re-branding the other.
|
|
54
|
+
Page-local tokens live on body{}, with every dark rule written TWICE (the
|
|
55
|
+
data-mode block and its prefers-color-scheme twin), both at (0,2,0). */
|
|
56
|
+
*{box-sizing:border-box}
|
|
57
|
+
html,body{margin:0;padding:0}
|
|
58
|
+
body{background:var(--bg);color:var(--ink);font-family:var(--font-body);font-weight:400;
|
|
59
|
+
-webkit-font-smoothing:antialiased;overflow-x:hidden}
|
|
60
|
+
a{color:inherit;text-decoration:none}
|
|
61
|
+
img{max-width:100%;display:block}
|
|
62
|
+
h1,h2,h3,p,ol,ul,figure{margin:0}
|
|
63
|
+
ol,ul{list-style:none;padding:0}
|
|
64
|
+
::selection{background:var(--accent);color:var(--accent-on)}
|
|
65
|
+
/* the ring is two-toned: the accent outline sits inside a --bg halo, so the indicator keeps a
|
|
66
|
+
~6:1 edge of its own over whatever chrome it lands on (a bare outline measured 1.1:1 against the
|
|
67
|
+
light bar's ground). The halo follows the control's own radius, so no radius is forced here.
|
|
68
|
+
It rides on box-shadow, so any control carrying a shadow of its own would otherwise win on
|
|
69
|
+
source order: the primary door re-states the pair below, glow first so dark keeps its lift. */
|
|
70
|
+
:focus-visible{outline:2px solid var(--focus);outline-offset:3px;box-shadow:0 0 0 8px var(--bg)}
|
|
71
|
+
.skip{position:absolute;left:-9999px;top:0;z-index:9;min-height:24px;padding:11px 18px;border-radius:999px;
|
|
72
|
+
background:var(--bg-card);color:var(--ink);border:1px solid var(--rule);font-size:15px}
|
|
73
|
+
.skip:focus{left:14px;top:14px}
|
|
74
|
+
/* the skip link's target: focusable so the skip actually lands, but never ringed */
|
|
75
|
+
main:focus{outline:none;box-shadow:none}
|
|
76
|
+
|
|
77
|
+
/* ===================== the chrome field =====================
|
|
78
|
+
Three plates, each one crop of one rendered plate. In dark the plates are the black-ground JPEGs
|
|
79
|
+
composited with lighten; in light they are the alpha cutouts on the pale ground with a contact
|
|
80
|
+
shadow, so the same sculpture reads as objects on paper. Crop maths: size/position derive from the
|
|
81
|
+
crop rectangle [u0,u1] x [v0,v1] and the plate aspect, so a layer scales with its own width token. */
|
|
82
|
+
.field{position:absolute;top:calc(var(--top-h) * -1);left:0;right:0;bottom:0;overflow:hidden;
|
|
83
|
+
pointer-events:none;isolation:isolate;
|
|
84
|
+
-webkit-mask-image:var(--sky-mask);mask-image:var(--sky-mask)}
|
|
85
|
+
.plate{position:absolute;background-repeat:no-repeat;mix-blend-mode:var(--sky-blend);
|
|
86
|
+
filter:var(--sky-shadow) hue-rotate(0deg);pointer-events:none}
|
|
87
|
+
|
|
88
|
+
/* r2-chrome-blobs-set, u[.22,.96] v[0,1] : the sweeping form across the upper right. The crop runs
|
|
89
|
+
to the plate's own bottom edge, which is empty on the right, so the layer has no hard lower cut.
|
|
90
|
+
The crop's lower left catches the round blob's shoulder, so two masks union to "keep the right
|
|
91
|
+
side OR the top", dissolving that corner instead of cutting it square. */
|
|
92
|
+
.form{left:var(--form-x);top:var(--form-y);width:var(--form-w);aspect-ratio:1.32584;
|
|
93
|
+
background-image:var(--sky-set);
|
|
94
|
+
background-size:calc(var(--form-w) * 1.35135) auto;
|
|
95
|
+
background-position:calc(var(--form-w) * -.2973) 0;
|
|
96
|
+
-webkit-mask-image:var(--form-mask);mask-image:var(--form-mask);
|
|
97
|
+
animation:arrive-form 700ms cubic-bezier(.16,1,.3,1) both}
|
|
98
|
+
|
|
99
|
+
/* r2-chrome-blob-one, cropped to the form's own silhouette u[.14,.865] v[.172,.808] : the big round
|
|
100
|
+
blob the doors float inside. The single-blob plate is used rather than the set's round blob
|
|
101
|
+
because the set's runs off its own lower edge, and a form cropped by nothing is what light mode
|
|
102
|
+
needs. This is the one that breathes, and the only one the hue drift moves. */
|
|
103
|
+
.orb{left:var(--orb-x);top:var(--orb-y);width:var(--orb-w);aspect-ratio:1.13994;
|
|
104
|
+
background-image:var(--sky-bead);
|
|
105
|
+
background-size:calc(var(--orb-w) * 1.37931) auto;
|
|
106
|
+
background-position:calc(var(--orb-w) * -.1931) calc(var(--orb-w) * -.23724);
|
|
107
|
+
animation:arrive-orb 700ms cubic-bezier(.16,1,.3,1) 70ms both,
|
|
108
|
+
breathe 32s ease-in-out 700ms infinite,
|
|
109
|
+
sheen 40s ease-in-out infinite}
|
|
110
|
+
|
|
111
|
+
/* r2-chrome-blobs-set, u[.808,.952] v[.50,.84] : the set's own small bead, the third form in the
|
|
112
|
+
plate, orbiting the big blob. Its centre token is the blob's centre; the orbit translates it out
|
|
113
|
+
by --orbit-r and walks it round in twelve steps. */
|
|
114
|
+
.bead{left:calc(var(--bead-cx) - var(--bead-w) / 2);
|
|
115
|
+
top:calc(var(--bead-cy) - var(--bead-w) * .65885);
|
|
116
|
+
width:var(--bead-w);aspect-ratio:.75891;background-image:var(--sky-set);
|
|
117
|
+
background-size:calc(var(--bead-w) * 6.94444) auto;
|
|
118
|
+
background-position:calc(var(--bead-w) * -5.61111) calc(var(--bead-w) * -1.93798);
|
|
119
|
+
translate:var(--orbit-r) 0;
|
|
120
|
+
animation:arrive-bead 700ms cubic-bezier(.16,1,.3,1) 140ms both,orbit 60s linear infinite}
|
|
121
|
+
|
|
122
|
+
/* the authored moment: the forms arrive from slightly apart and converge into place */
|
|
123
|
+
@keyframes arrive-form{from{translate:3.4vw -2.2vh;opacity:.36}to{translate:0 0;opacity:1}}
|
|
124
|
+
@keyframes arrive-orb{from{translate:-2.8vw 2.4vh;opacity:.36}to{translate:0 0;opacity:1}}
|
|
125
|
+
@keyframes arrive-bead{from{scale:.78;opacity:0}to{scale:1;opacity:1}}
|
|
126
|
+
@keyframes arrive-mark{from{translate:2.6vw 1.8vh;opacity:.36}to{translate:0 0;opacity:1}}
|
|
127
|
+
/* the breath: a 32s swell, and a 40s hue drift so the iridescence keeps moving */
|
|
128
|
+
@keyframes breathe{0%{scale:1}50%{scale:1.035}100%{scale:1}}
|
|
129
|
+
@keyframes sheen{0%{filter:var(--sky-shadow) hue-rotate(0deg)}
|
|
130
|
+
25%{filter:var(--sky-shadow) hue-rotate(-12deg)}
|
|
131
|
+
75%{filter:var(--sky-shadow) hue-rotate(12deg)}
|
|
132
|
+
100%{filter:var(--sky-shadow) hue-rotate(0deg)}}
|
|
133
|
+
/* a 60s circular path, walked as twelve steps on the translate property */
|
|
134
|
+
@keyframes orbit{
|
|
135
|
+
0%{translate:calc(var(--orbit-r) * 1) 0}
|
|
136
|
+
8.333%{translate:calc(var(--orbit-r) * .866) calc(var(--orbit-r) * .5)}
|
|
137
|
+
16.666%{translate:calc(var(--orbit-r) * .5) calc(var(--orbit-r) * .866)}
|
|
138
|
+
25%{translate:0 calc(var(--orbit-r) * 1)}
|
|
139
|
+
33.333%{translate:calc(var(--orbit-r) * -.5) calc(var(--orbit-r) * .866)}
|
|
140
|
+
41.666%{translate:calc(var(--orbit-r) * -.866) calc(var(--orbit-r) * .5)}
|
|
141
|
+
50%{translate:calc(var(--orbit-r) * -1) 0}
|
|
142
|
+
58.333%{translate:calc(var(--orbit-r) * -.866) calc(var(--orbit-r) * -.5)}
|
|
143
|
+
66.666%{translate:calc(var(--orbit-r) * -.5) calc(var(--orbit-r) * -.866)}
|
|
144
|
+
75%{translate:0 calc(var(--orbit-r) * -1)}
|
|
145
|
+
83.333%{translate:calc(var(--orbit-r) * .5) calc(var(--orbit-r) * -.866)}
|
|
146
|
+
91.666%{translate:calc(var(--orbit-r) * .866) calc(var(--orbit-r) * -.5)}
|
|
147
|
+
100%{translate:calc(var(--orbit-r) * 1) 0}}
|
|
148
|
+
.scrim{position:absolute;top:calc(var(--top-h) * -1);left:0;right:0;bottom:0;z-index:1;
|
|
149
|
+
background:var(--sky-scrim);pointer-events:none}
|
|
150
|
+
|
|
151
|
+
/* ===================== top bar: three pill groups on one line ===================== */
|
|
152
|
+
.topbar{position:relative;z-index:3;height:var(--top-h);display:flex;align-items:center;padding:0 26px}
|
|
153
|
+
.topbar::before{content:"";position:absolute;z-index:-1;left:0;right:0;top:0;height:128px;
|
|
154
|
+
background:var(--sky-topscrim);pointer-events:none}
|
|
155
|
+
.topnav{display:flex;align-items:center;gap:12px;width:100%}
|
|
156
|
+
.pill{display:inline-flex;align-items:center;justify-content:center;min-height:46px;padding:0 22px;
|
|
157
|
+
border-radius:var(--radius-pill);background:var(--pill-bg);border:1px solid var(--pill-edge);
|
|
158
|
+
backdrop-filter:blur(var(--glass-blur));-webkit-backdrop-filter:blur(var(--glass-blur));
|
|
159
|
+
font-family:inherit;font-size:16px;color:var(--ink);cursor:pointer;
|
|
160
|
+
transition:background .25s cubic-bezier(.2,.7,.3,1),color .2s ease}
|
|
161
|
+
.pill:hover{background:var(--pill-bg-hi)}
|
|
162
|
+
.mark-pill{gap:10px;padding:0 24px;background:var(--pill-bg-hi);letter-spacing:-.012em}
|
|
163
|
+
.mark-pill svg{width:23px;height:18px;display:block;flex:none}
|
|
164
|
+
.nav-pill{gap:0;padding:0 6px;margin:0 auto}
|
|
165
|
+
.nav-pill a{padding:13px 22px;border-radius:var(--radius-pill);font-size:16px;text-transform:lowercase;
|
|
166
|
+
color:var(--ink-soft);transition:color .2s ease}
|
|
167
|
+
.nav-pill a:hover{color:var(--ink)}
|
|
168
|
+
.right-pills{display:flex;align-items:center;gap:10px}
|
|
169
|
+
.mode-toggle{width:46px;min-width:46px;padding:0}
|
|
170
|
+
.mode-toggle svg{width:18px;height:18px}
|
|
171
|
+
.sun{display:none}
|
|
172
|
+
.signin{background:color-mix(in srgb,var(--ink) 92%,var(--bg));color:var(--bg);border-color:transparent;
|
|
173
|
+
text-transform:lowercase;font-weight:500;padding:0 30px}
|
|
174
|
+
.signin:hover{background:var(--ink);color:var(--bg)}
|
|
175
|
+
/* the signed-in chip replaces the sign-in door in the same slot */
|
|
176
|
+
#who img{width:26px;height:26px;border-radius:var(--radius-pill);flex:none;object-fit:cover}
|
|
177
|
+
.who-name{font-size:15px;color:var(--ink);text-shadow:var(--type-halo);white-space:nowrap}
|
|
178
|
+
.signout{text-transform:lowercase;padding:0 18px}
|
|
179
|
+
:root[data-mode="dark"] .sun{display:block}
|
|
180
|
+
:root[data-mode="dark"] .moon{display:none}
|
|
181
|
+
@media (prefers-color-scheme:dark){:root:not([data-mode="light"]) .sun{display:block}
|
|
182
|
+
:root:not([data-mode="light"]) .moon{display:none}}
|
|
183
|
+
|
|
184
|
+
/* ===================== hero ===================== */
|
|
185
|
+
.hero{position:relative;min-height:calc(var(--stage) - var(--top-h));padding:0 26px}
|
|
186
|
+
.layer{position:var(--layer-pos);top:calc(var(--top-h) * -1);left:0;right:0;bottom:0;z-index:2;
|
|
187
|
+
pointer-events:none}
|
|
188
|
+
h1{position:var(--float2);left:var(--h1-x);right:var(--h1-x);top:var(--h1-top);margin:0;
|
|
189
|
+
font-family:var(--font-display);
|
|
190
|
+
font-weight:300;font-size:var(--h1-size);line-height:.92;letter-spacing:-.042em;
|
|
191
|
+
text-transform:lowercase;color:var(--ink);text-shadow:var(--type-halo)}
|
|
192
|
+
h1 span{display:block;white-space:nowrap}
|
|
193
|
+
h1 .l2{margin-left:var(--h1-indent)}
|
|
194
|
+
/* a host whose tagline is not the neutral two halves renders it whole, and wraps */
|
|
195
|
+
h1 .whole{white-space:normal;margin-left:0}
|
|
196
|
+
.lede{position:var(--float2);left:var(--lede-x);top:var(--lede-y);width:var(--lede-w);max-width:100%;
|
|
197
|
+
font-size:18.5px;line-height:1.42;color:var(--ink-soft);text-shadow:var(--type-halo)}
|
|
198
|
+
|
|
199
|
+
/* the hero object: the three-quarter chrome pair, an alpha cutout with its own contact shadow */
|
|
200
|
+
.mark{position:var(--float2);left:var(--mark-x);top:var(--mark-y);width:var(--mark-w);aspect-ratio:1;
|
|
201
|
+
background-image:var(--sky-bongos);background-repeat:no-repeat;background-size:100% auto;
|
|
202
|
+
filter:var(--sky-shadow);
|
|
203
|
+
pointer-events:none;animation:arrive-mark 700ms cubic-bezier(.16,1,.3,1) 210ms both}
|
|
204
|
+
|
|
205
|
+
/* leader-line callouts: annotations on the object, on real ledger numbers */
|
|
206
|
+
.callout{position:var(--float2);z-index:0}
|
|
207
|
+
/* Every callout carries its own ground, and only where the ink is: a feathered patch of the page
|
|
208
|
+
ground behind the figure and behind the caption, never the leader and never past the glyphs.
|
|
209
|
+
The plates scale with the viewport WIDTH while the type is fixed in px, so between about 1000
|
|
210
|
+
and 1300px the lit chrome walks under a caption that sits on black (or paper) at 1440; no
|
|
211
|
+
single position clears every width. One box spanning the leader's full width used to reach a
|
|
212
|
+
plate's rim and cut a flat-edged card out of it in light; sized to the glyphs and faded to
|
|
213
|
+
nothing at its own edge, the ground reads as haze wherever it does meet the sculpture. */
|
|
214
|
+
.fig::before,.cap::before{content:"";position:absolute;left:-20px;right:-20px;top:-10px;bottom:-8px;
|
|
215
|
+
z-index:-1;filter:blur(8px);pointer-events:none;
|
|
216
|
+
/* the solid core must reach the glyph box's CORNERS (an ellipse inscribed in the box misses
|
|
217
|
+
them, and the first and last letters of a wide caption then sit on the feather); measured at
|
|
218
|
+
1024 to 1920 in both modes at four animation phases */
|
|
219
|
+
background:radial-gradient(ellipse 62% 75% at 50% 50%,var(--bg) 0 88%,transparent 100%)}
|
|
220
|
+
.fig:empty::before{display:none} /* a child instance has no figure for a ground to carry */
|
|
221
|
+
.c1{left:var(--c1-x);top:var(--c1-y)}
|
|
222
|
+
.c2{left:var(--c2-x);top:var(--c2-y)}
|
|
223
|
+
.c3{left:var(--c3-x);top:var(--c3-y)}
|
|
224
|
+
.leader{display:block;width:var(--leader-len);height:30px;margin-bottom:12px;overflow:visible;
|
|
225
|
+
color:var(--ink-soft);opacity:.7}
|
|
226
|
+
.c2 .leader{transform:var(--c2-flip)}
|
|
227
|
+
.c3 .leader{transform:var(--c3-flip)}
|
|
228
|
+
.fig{display:block;position:relative;width:fit-content;max-width:100%;
|
|
229
|
+
min-height:1em;font-size:clamp(24px,2.4vw,34px);font-weight:300;letter-spacing:-.025em;
|
|
230
|
+
font-variant-numeric:tabular-nums;color:var(--ink);line-height:1;text-shadow:var(--type-halo)}
|
|
231
|
+
.cap{display:block;position:relative;width:fit-content;max-width:100%;
|
|
232
|
+
margin-top:8px;font-size:14px;color:var(--ink-soft);text-shadow:var(--type-halo)}
|
|
233
|
+
|
|
234
|
+
/* the two doors, one joined pill, floating inside the big blob */
|
|
235
|
+
.doors{position:var(--float2);left:var(--doors-cx);top:var(--doors-cy);transform:var(--doors-shift);
|
|
236
|
+
display:flex;justify-content:center;pointer-events:auto}
|
|
237
|
+
.doorbar{display:inline-flex;align-items:center;gap:6px;padding:7px;border-radius:var(--radius-pill);
|
|
238
|
+
background:var(--door-ground);border:1px solid var(--door-edge);
|
|
239
|
+
backdrop-filter:blur(var(--glass-blur));-webkit-backdrop-filter:blur(var(--glass-blur))}
|
|
240
|
+
.door{display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:0 30px;
|
|
241
|
+
border-radius:var(--radius-pill);font-size:17px;text-transform:lowercase;color:var(--ink);
|
|
242
|
+
transition:background .25s cubic-bezier(.2,.7,.3,1),color .25s ease}
|
|
243
|
+
.door:hover{background:var(--pill-bg-hi)}
|
|
244
|
+
.door--go{background:var(--accent);color:var(--accent-on);font-weight:500;box-shadow:var(--accent-glow)}
|
|
245
|
+
.door--go:hover{background:var(--accent-lift);color:var(--accent-on)}
|
|
246
|
+
.door--go:focus-visible{box-shadow:var(--accent-glow),0 0 0 8px var(--bg)}
|
|
247
|
+
|
|
248
|
+
/* ===================== the spine below the fold ===================== */
|
|
249
|
+
.band{position:relative;border-top:1px solid var(--rule-soft);
|
|
250
|
+
padding:clamp(58px,8.4vh,104px) 26px clamp(58px,8.4vh,104px)}
|
|
251
|
+
.wrap{max-width:1180px;margin:0 auto}
|
|
252
|
+
h2{font-family:var(--font-display);font-weight:300;font-size:clamp(26px,3.1vw,42px);line-height:1.06;
|
|
253
|
+
letter-spacing:-.035em;color:var(--ink)}
|
|
254
|
+
.sub{margin-top:14px;font-size:17px;line-height:1.5;color:var(--ink-soft);max-width:56ch}
|
|
255
|
+
.label{font-family:var(--label-face);font-size:11px;letter-spacing:.14em;text-transform:uppercase;
|
|
256
|
+
color:var(--ink-faint)}
|
|
257
|
+
.more{display:inline-block;margin-top:32px;min-height:24px;padding:3px 0;line-height:1.25;
|
|
258
|
+
font-size:16px;color:var(--accent-ink);
|
|
259
|
+
text-decoration:underline;text-underline-offset:4px;text-decoration-thickness:1px}
|
|
260
|
+
|
|
261
|
+
/* Living proof: real projects as chrome beads. Each project is a DIFFERENT object, addressed by the
|
|
262
|
+
same [u0,u1] x [v0,v1] crop maths .form and .bead use, so the constellation is three forms and not
|
|
263
|
+
one silhouette repeated: the round blob, the chrome pair, the set's small bead. Only those three
|
|
264
|
+
are WHOLE. A rectangle cannot isolate the set's other two forms: the sweeping one runs off the
|
|
265
|
+
plate's top over half its width and the round one off its bottom over a third, and either crop
|
|
266
|
+
also catches a tail of its neighbour. The closing figure is the pair again, at the doors. */
|
|
267
|
+
.orbs{display:flex;flex-wrap:wrap;align-items:flex-end;gap:clamp(28px,5vw,74px);margin-top:52px}
|
|
268
|
+
.orb-item{margin-bottom:var(--drop)}
|
|
269
|
+
.orb-item a{display:block}
|
|
270
|
+
.orb-bead{display:block;width:var(--dia);aspect-ratio:var(--bead-ar);background-image:var(--bead-src);
|
|
271
|
+
background-repeat:no-repeat;background-size:calc(var(--dia) * var(--bead-zoom)) auto;
|
|
272
|
+
background-position:calc(var(--dia) * var(--bead-px)) calc(var(--dia) * var(--bead-py));
|
|
273
|
+
mix-blend-mode:var(--sky-blend);
|
|
274
|
+
filter:var(--sky-shadow);transition:translate .4s cubic-bezier(.16,1,.3,1)}
|
|
275
|
+
.orb-item a:hover .orb-bead{translate:0 -8px}
|
|
276
|
+
.orb-name{display:block;margin-top:14px;font-size:19px;color:var(--ink);letter-spacing:-.015em}
|
|
277
|
+
.orb-meta{display:block;margin-top:5px;font-size:14px;color:var(--ink-soft)}
|
|
278
|
+
/* r2-chrome-blob-one, u[.14,.865] v[.172,.808] : the round blob, whole */
|
|
279
|
+
.o1{--dia:clamp(150px,17vw,232px);--drop:0;
|
|
280
|
+
--bead-src:var(--sky-bead);--bead-ar:1.13994;--bead-zoom:1.37931;--bead-px:-.19310;--bead-py:-.23724}
|
|
281
|
+
/* r2-bongos-chrome-a-cut, the whole plate: the chrome pair, the third object in the world's cast */
|
|
282
|
+
.o2{--dia:clamp(104px,11vw,146px);--drop:44px;
|
|
283
|
+
--bead-src:var(--sky-bongos);--bead-ar:1;--bead-zoom:1;--bead-px:0;--bead-py:0}
|
|
284
|
+
/* r2-chrome-blobs-set, u[.810,.955] v[.556,.840] : the set's small bead, whole on every side */
|
|
285
|
+
.o3{--dia:clamp(96px,10vw,132px);--drop:16px;
|
|
286
|
+
--bead-src:var(--sky-set);--bead-ar:.91476;--bead-zoom:6.89655;--bead-px:-5.58621;--bead-py:-2.14014}
|
|
287
|
+
|
|
288
|
+
/* the lifecycle: five beats on one rail, exactly one lit */
|
|
289
|
+
.beat{display:grid;grid-template-columns:repeat(5,1fr);margin-top:54px}
|
|
290
|
+
.beat li{padding:20px 22px 0 0;border-top:1px solid var(--rule-soft)}
|
|
291
|
+
.beat .dot{display:block;width:7px;height:7px;border-radius:50%;border:1px solid var(--ink-faint)}
|
|
292
|
+
.beat .step{display:block;margin-top:16px;font-family:var(--label-face);font-size:12px;
|
|
293
|
+
letter-spacing:.12em;text-transform:uppercase;color:var(--ink-faint)}
|
|
294
|
+
.beat .clause{display:block;margin-top:10px;font-size:15.5px;line-height:1.42;color:var(--ink-soft)}
|
|
295
|
+
.beat .lit{border-top-color:var(--accent)}
|
|
296
|
+
.beat .lit .dot{background:var(--accent);border-color:var(--accent);
|
|
297
|
+
box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 20%,transparent)}
|
|
298
|
+
.beat .lit .step{color:var(--ink)}
|
|
299
|
+
|
|
300
|
+
/* the ledger: real rows, tabular figures, hairlines */
|
|
301
|
+
.figline{font-family:var(--font-display);font-weight:300;font-size:clamp(30px,3.7vw,52px);
|
|
302
|
+
letter-spacing:-.035em;font-variant-numeric:tabular-nums;color:var(--ink);line-height:1;min-height:1em}
|
|
303
|
+
.meter{margin-top:22px;height:3px;background:var(--rule-soft);border-radius:2px;overflow:hidden}
|
|
304
|
+
.meter i{display:block;height:100%;width:0;background:var(--ink)}
|
|
305
|
+
.rows{margin-top:44px}
|
|
306
|
+
.rows li{display:grid;grid-template-columns:88px 1fr 152px 96px;gap:20px;align-items:baseline;
|
|
307
|
+
padding:15px 0;border-top:1px solid var(--rule-soft)}
|
|
308
|
+
.rows .id{font-family:var(--font-mono);font-size:13px;color:var(--ink-faint);
|
|
309
|
+
font-variant-numeric:tabular-nums}
|
|
310
|
+
/* live titles run long; two lines is the row's budget */
|
|
311
|
+
.rows .what{font-size:16px;line-height:1.4;color:var(--ink);
|
|
312
|
+
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
313
|
+
.rows .who,.rows .when{font-size:14px;color:var(--ink-soft)}
|
|
314
|
+
.rows .when{text-align:right;font-variant-numeric:tabular-nums}
|
|
315
|
+
/* Day one on a child instance: the sentinels strip the baked line, meter and rows, and its own
|
|
316
|
+
feed answers {tasks:[]}. A standing meter and "the six most recent" over nothing is a promise
|
|
317
|
+
the page cannot keep, so the empty parts collapse and only the line's shell waits for data. */
|
|
318
|
+
.figline:empty ~ .meter,.figline:empty ~ .sub{display:none}
|
|
319
|
+
.rows:empty{display:none}
|
|
320
|
+
|
|
321
|
+
/* verro labs: medallion, wordmark, one paragraph, no frame */
|
|
322
|
+
.verro{display:grid;grid-template-columns:auto 1fr;gap:clamp(26px,4vw,58px);align-items:start}
|
|
323
|
+
.verro img{width:clamp(92px,9vw,124px);height:auto}
|
|
324
|
+
.wordmark{font-family:var(--font-mono);font-size:13px;letter-spacing:.3em;color:var(--ink)}
|
|
325
|
+
.verro p.body{margin-top:18px;font-size:17.5px;line-height:1.55;color:var(--ink-soft);max-width:62ch}
|
|
326
|
+
|
|
327
|
+
/* the basics: two columns, never three */
|
|
328
|
+
.two{display:grid;grid-template-columns:1fr 1fr;gap:clamp(34px,5vw,84px);margin-top:8px}
|
|
329
|
+
.two h3{font-family:var(--font-display);font-weight:300;font-size:clamp(23px,2.4vw,32px);
|
|
330
|
+
letter-spacing:-.03em;color:var(--ink);margin-top:12px}
|
|
331
|
+
.two ul{margin-top:24px}
|
|
332
|
+
.two li{padding:16px 0;border-top:1px solid var(--rule-soft);font-size:16.5px;line-height:1.46;
|
|
333
|
+
color:var(--ink-soft)}
|
|
334
|
+
.two li b{font-weight:500;color:var(--ink)}
|
|
335
|
+
.two .nest{margin-top:12px;padding-left:18px;border-left:1px solid var(--rule-soft)}
|
|
336
|
+
.two .nest li{border-top:0;padding:5px 0;font-size:15.5px}
|
|
337
|
+
|
|
338
|
+
/* the closing pair */
|
|
339
|
+
.close{text-align:center}
|
|
340
|
+
.close .closemark{--dia:clamp(128px,14vw,196px);width:var(--dia);aspect-ratio:1;
|
|
341
|
+
margin:0 auto 34px;
|
|
342
|
+
background-image:var(--sky-bongos);background-repeat:no-repeat;background-size:100% auto;
|
|
343
|
+
filter:var(--sky-shadow)}
|
|
344
|
+
.close .doorbar{margin:0 auto}
|
|
345
|
+
|
|
346
|
+
/* footer */
|
|
347
|
+
footer{position:relative;border-top:1px solid var(--rule-soft);padding:30px 26px 52px;font-size:14.5px;
|
|
348
|
+
color:var(--ink-soft)}
|
|
349
|
+
.footrow{max-width:1180px;margin:0 auto;display:flex;flex-wrap:wrap;gap:10px 26px;align-items:baseline}
|
|
350
|
+
.footrow p{margin-right:auto;max-width:62ch}
|
|
351
|
+
/* the footer row aligns on the baseline, so the box that gets these links to 24px is symmetric */
|
|
352
|
+
footer a{display:inline-block;min-height:24px;padding:3px 0;line-height:1.24;
|
|
353
|
+
color:var(--accent-ink);text-decoration:underline;text-underline-offset:3px;
|
|
354
|
+
text-decoration-thickness:1px}
|
|
355
|
+
|
|
356
|
+
/* ===================== page-local tokens: LIGHT is the base ===================== */
|
|
357
|
+
/* Light is the rebuilt answer: no black panel. The same sculpture sits directly on the pale ground
|
|
358
|
+
as alpha cutouts with a soft contact shadow, the type moves to clean paper on the left, and the
|
|
359
|
+
doors and callouts keep their geometry as annotations on the object. */
|
|
360
|
+
body{
|
|
361
|
+
/* THE STAGE. The hero composition was graded at 1440x900 and its vertical tokens are
|
|
362
|
+
percentages of THIS box, not of the viewport: max(100dvh,900px) reproduces the graded frame
|
|
363
|
+
byte for byte at 900 and taller, and lets a shorter laptop viewport scroll rather than
|
|
364
|
+
recompose (raw vh ran the h1 through the lede and walked the third callout onto lit chrome
|
|
365
|
+
at every height under about 820). --vh is one percent of the stage. */
|
|
366
|
+
--stage:max(100dvh,900px);
|
|
367
|
+
--vh:calc(var(--stage) / 100);
|
|
368
|
+
--sky-set:var(--plate-set-cut);
|
|
369
|
+
--sky-bead:var(--plate-bead-cut);
|
|
370
|
+
--sky-bongos:var(--plate-bongos-cut);
|
|
371
|
+
--sky-blend:normal;
|
|
372
|
+
--sky-shadow:drop-shadow(0 26px 30px color-mix(in srgb,var(--ink) 20%,transparent));
|
|
373
|
+
--sky-mask:none;
|
|
374
|
+
--sky-scrim:none;
|
|
375
|
+
--sky-topscrim:none;
|
|
376
|
+
--pill-bg:color-mix(in srgb,var(--bg) 88%,transparent);
|
|
377
|
+
--pill-bg-hi:color-mix(in srgb,var(--bg-card) 94%,transparent);
|
|
378
|
+
--pill-edge:var(--rule-soft);
|
|
379
|
+
--door-ground:color-mix(in srgb,var(--bg-card) 93%,transparent);
|
|
380
|
+
--door-edge:var(--rule);
|
|
381
|
+
--layer-pos:absolute;
|
|
382
|
+
--float2:absolute;
|
|
383
|
+
--doors-shift:translate(-50%,-50%);
|
|
384
|
+
--h1-size:clamp(34px,5.3vw,78px);
|
|
385
|
+
--h1-x:3vw;
|
|
386
|
+
--h1-top:calc(var(--vh) * 15);
|
|
387
|
+
--h1-indent:5%;
|
|
388
|
+
--lede-x:3vw;
|
|
389
|
+
--lede-y:calc(var(--vh) * 32);
|
|
390
|
+
--lede-w:320px;
|
|
391
|
+
--leader-len:170px;
|
|
392
|
+
--form-mask:linear-gradient(to right,transparent 0 36%,#000 52%);
|
|
393
|
+
--form-w:52vw;
|
|
394
|
+
--form-x:56vw;
|
|
395
|
+
--form-y:calc(var(--vh) * -18);
|
|
396
|
+
--orb-w:48vw;
|
|
397
|
+
--orb-x:32vw;
|
|
398
|
+
--orb-y:calc(var(--vh) * 32);
|
|
399
|
+
--bead-w:7.2vw;
|
|
400
|
+
--bead-cx:56vw;
|
|
401
|
+
--bead-cy:calc(var(--vh) * 66);
|
|
402
|
+
--orbit-r:11vw;
|
|
403
|
+
--mark-w:22vw;
|
|
404
|
+
--mark-x:78vw;
|
|
405
|
+
--mark-y:calc(var(--vh) * 13);
|
|
406
|
+
--c1-x:63vw;
|
|
407
|
+
--c1-y:calc(var(--vh) * 15);
|
|
408
|
+
--c2-x:22vw;
|
|
409
|
+
--c2-y:calc(var(--vh) * 66);
|
|
410
|
+
--c2-flip:none;
|
|
411
|
+
--c3-x:20vw;
|
|
412
|
+
--c3-y:calc(var(--vh) * 44);
|
|
413
|
+
--c3-flip:none;
|
|
414
|
+
--doors-cx:56vw;
|
|
415
|
+
--doors-cy:calc(var(--vh) * 60);
|
|
133
416
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
--
|
|
154
|
-
--
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
417
|
+
|
|
418
|
+
:root[data-mode="dark"] body{
|
|
419
|
+
--sky-set:var(--plate-set);
|
|
420
|
+
--sky-bead:var(--plate-bead);
|
|
421
|
+
--sky-bongos:var(--plate-bongos-cut);
|
|
422
|
+
--sky-blend:var(--blob-blend);
|
|
423
|
+
--sky-shadow:opacity(1);
|
|
424
|
+
/* THE STAGE. The hero composition was graded at 1440x900 and its vertical tokens are
|
|
425
|
+
percentages of THIS box, not of the viewport: max(100dvh,900px) reproduces the graded frame
|
|
426
|
+
byte for byte at 900 and taller, and lets a shorter laptop viewport scroll rather than
|
|
427
|
+
recompose (raw vh ran the h1 through the lede and walked the third callout onto lit chrome
|
|
428
|
+
at every height under about 820). --vh is one percent of the stage. */
|
|
429
|
+
--stage:max(100dvh,900px);
|
|
430
|
+
--vh:calc(var(--stage) / 100);
|
|
431
|
+
|
|
432
|
+
--sky-mask:none;
|
|
433
|
+
--sky-scrim:radial-gradient(30% 13% at 15% 42%,rgba(0,0,0,.94),rgba(0,0,0,.6) 58%,rgba(0,0,0,0) 100%),radial-gradient(60% 55% at 26% 32%,rgba(0,0,0,.88),rgba(0,0,0,.42) 48%,rgba(0,0,0,0) 76%);
|
|
434
|
+
--sky-topscrim:linear-gradient(to bottom,rgba(0,0,0,.84) 0,rgba(0,0,0,.5) 46%,rgba(0,0,0,0) 100%);
|
|
435
|
+
--pill-bg:var(--void-glass);
|
|
436
|
+
--pill-bg-hi:var(--void-glass-strong);
|
|
437
|
+
--pill-edge:var(--void-glass-edge);
|
|
438
|
+
--door-ground:var(--void-pill);
|
|
439
|
+
--door-edge:var(--void-glass-edge);
|
|
440
|
+
--layer-pos:absolute;
|
|
441
|
+
--float2:absolute;
|
|
442
|
+
--doors-shift:translate(-50%,-50%);
|
|
443
|
+
--h1-size:clamp(34px,6.7vw,96px);
|
|
444
|
+
--h1-x:3vw;
|
|
445
|
+
--h1-top:calc(var(--vh) * 16);
|
|
446
|
+
--h1-indent:8%;
|
|
447
|
+
--lede-x:3vw;
|
|
448
|
+
--lede-y:calc(var(--vh) * 38);
|
|
449
|
+
--lede-w:300px;
|
|
450
|
+
--leader-len:104px;
|
|
451
|
+
--form-mask:linear-gradient(to right,transparent 0 34%,#000 50%),linear-gradient(to bottom,#000 0 44%,transparent 54%);
|
|
452
|
+
--form-w:70vw;
|
|
453
|
+
--form-x:40vw;
|
|
454
|
+
--form-y:calc(var(--vh) * -12);
|
|
455
|
+
--orb-w:54vw;
|
|
456
|
+
--orb-x:5vw;
|
|
457
|
+
--orb-y:calc(var(--vh) * 24);
|
|
458
|
+
--bead-w:8.3vw;
|
|
459
|
+
--bead-cx:32vw;
|
|
460
|
+
--bead-cy:calc(var(--vh) * 62);
|
|
461
|
+
--orbit-r:15vw;
|
|
462
|
+
--mark-w:27vw;
|
|
463
|
+
--mark-x:71vw;
|
|
464
|
+
--mark-y:calc(var(--vh) * 12);
|
|
465
|
+
--c1-x:61vw;
|
|
466
|
+
/* bound to WIDTH, not to the stage: the caption sits a whisker above the sweeping form's lit
|
|
467
|
+
ridge, and the form is sized from vw, so the ridge walks with the viewport WIDTH. The graded
|
|
468
|
+
1440 height put "tasks shipped" straight on the lit band at 1366 and 1280 (2.7:1). Hold the
|
|
469
|
+
graded value at 1440 and wider, then lift the block as the viewport narrows, floored so it
|
|
470
|
+
never climbs behind the top bar. */
|
|
471
|
+
--c1-y:max(70px, calc(var(--vh) * 13 - max(0px, (1440px - 100vw) * 0.32)));
|
|
472
|
+
--c2-x:64vw;
|
|
473
|
+
--c2-y:calc(var(--vh) * 74);
|
|
474
|
+
--c2-flip:scaleX(-1);
|
|
475
|
+
--c3-x:70vw;
|
|
476
|
+
/* bound to the sweeping form, not to the stage: the form is sized from vw, so a stage-anchored
|
|
477
|
+
callout slides up onto its lit lower lobe as the viewport widens. This is the graded 1440x900
|
|
478
|
+
position expressed as a fraction of the form's own height. */
|
|
479
|
+
--c3-y:calc(var(--form-y) + var(--form-w) / 1.32584 * 0.793136);
|
|
480
|
+
--c3-flip:scaleX(-1);
|
|
481
|
+
--doors-cx:32vw;
|
|
482
|
+
--doors-cy:calc(var(--vh) * 53);
|
|
158
483
|
}
|
|
484
|
+
|
|
159
485
|
@media (prefers-color-scheme:dark){
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
:
|
|
163
|
-
:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.verro p{
|
|
174
|
-
position:relative;font-family:system-ui,-apple-system,"Segoe UI",Arial,sans-serif;
|
|
175
|
-
font-size:clamp(14px,1.5vw,15.5px);font-weight:400;line-height:1.58;
|
|
176
|
-
color:var(--v-ink);max-width:62ch;
|
|
177
|
-
}
|
|
178
|
-
.verroMedal{justify-self:center;}
|
|
179
|
-
/* No border-radius needed: the PNG is already masked to a transparent-cornered
|
|
180
|
-
disc, so it sits on the grey block with nothing to clip. Width/height are
|
|
181
|
-
also set as attributes on the element so the box is reserved before the
|
|
182
|
-
image loads and the block does not reflow. */
|
|
183
|
-
.verroMedal .verroDisc{width:124px;height:124px;display:block;}
|
|
184
|
-
@media (max-width:760px){
|
|
185
|
-
.verroInner{grid-template-columns:1fr;justify-items:center;text-align:center;}
|
|
186
|
-
.verroRule{margin-left:auto;margin-right:auto;}
|
|
187
|
-
}
|
|
486
|
+
:root:not([data-mode="light"]) body{
|
|
487
|
+
--sky-set:var(--plate-set);
|
|
488
|
+
--sky-bead:var(--plate-bead);
|
|
489
|
+
--sky-bongos:var(--plate-bongos-cut);
|
|
490
|
+
--sky-blend:var(--blob-blend);
|
|
491
|
+
--sky-shadow:opacity(1);
|
|
492
|
+
/* THE STAGE. The hero composition was graded at 1440x900 and its vertical tokens are
|
|
493
|
+
percentages of THIS box, not of the viewport: max(100dvh,900px) reproduces the graded frame
|
|
494
|
+
byte for byte at 900 and taller, and lets a shorter laptop viewport scroll rather than
|
|
495
|
+
recompose (raw vh ran the h1 through the lede and walked the third callout onto lit chrome
|
|
496
|
+
at every height under about 820). --vh is one percent of the stage. */
|
|
497
|
+
--stage:max(100dvh,900px);
|
|
498
|
+
--vh:calc(var(--stage) / 100);
|
|
188
499
|
|
|
500
|
+
--sky-mask:none;
|
|
501
|
+
--sky-scrim:radial-gradient(30% 13% at 15% 42%,rgba(0,0,0,.94),rgba(0,0,0,.6) 58%,rgba(0,0,0,0) 100%),radial-gradient(60% 55% at 26% 32%,rgba(0,0,0,.88),rgba(0,0,0,.42) 48%,rgba(0,0,0,0) 76%);
|
|
502
|
+
--sky-topscrim:linear-gradient(to bottom,rgba(0,0,0,.84) 0,rgba(0,0,0,.5) 46%,rgba(0,0,0,0) 100%);
|
|
503
|
+
--pill-bg:var(--void-glass);
|
|
504
|
+
--pill-bg-hi:var(--void-glass-strong);
|
|
505
|
+
--pill-edge:var(--void-glass-edge);
|
|
506
|
+
--door-ground:var(--void-pill);
|
|
507
|
+
--door-edge:var(--void-glass-edge);
|
|
508
|
+
--layer-pos:absolute;
|
|
509
|
+
--float2:absolute;
|
|
510
|
+
--doors-shift:translate(-50%,-50%);
|
|
511
|
+
--h1-size:clamp(34px,6.7vw,96px);
|
|
512
|
+
--h1-x:3vw;
|
|
513
|
+
--h1-top:calc(var(--vh) * 16);
|
|
514
|
+
--h1-indent:8%;
|
|
515
|
+
--lede-x:3vw;
|
|
516
|
+
--lede-y:calc(var(--vh) * 38);
|
|
517
|
+
--lede-w:300px;
|
|
518
|
+
--leader-len:104px;
|
|
519
|
+
--form-mask:linear-gradient(to right,transparent 0 34%,#000 50%),linear-gradient(to bottom,#000 0 44%,transparent 54%);
|
|
520
|
+
--form-w:70vw;
|
|
521
|
+
--form-x:40vw;
|
|
522
|
+
--form-y:calc(var(--vh) * -12);
|
|
523
|
+
--orb-w:54vw;
|
|
524
|
+
--orb-x:5vw;
|
|
525
|
+
--orb-y:calc(var(--vh) * 24);
|
|
526
|
+
--bead-w:8.3vw;
|
|
527
|
+
--bead-cx:32vw;
|
|
528
|
+
--bead-cy:calc(var(--vh) * 62);
|
|
529
|
+
--orbit-r:15vw;
|
|
530
|
+
--mark-w:27vw;
|
|
531
|
+
--mark-x:71vw;
|
|
532
|
+
--mark-y:calc(var(--vh) * 12);
|
|
533
|
+
--c1-x:61vw;
|
|
534
|
+
/* bound to WIDTH, not to the stage: the caption sits a whisker above the sweeping form's lit
|
|
535
|
+
ridge, and the form is sized from vw, so the ridge walks with the viewport WIDTH. The graded
|
|
536
|
+
1440 height put "tasks shipped" straight on the lit band at 1366 and 1280 (2.7:1). Hold the
|
|
537
|
+
graded value at 1440 and wider, then lift the block as the viewport narrows, floored so it
|
|
538
|
+
never climbs behind the top bar. */
|
|
539
|
+
--c1-y:max(70px, calc(var(--vh) * 13 - max(0px, (1440px - 100vw) * 0.32)));
|
|
540
|
+
--c2-x:64vw;
|
|
541
|
+
--c2-y:calc(var(--vh) * 74);
|
|
542
|
+
--c2-flip:scaleX(-1);
|
|
543
|
+
--c3-x:70vw;
|
|
544
|
+
/* bound to the sweeping form, not to the stage: the form is sized from vw, so a stage-anchored
|
|
545
|
+
callout slides up onto its lit lower lobe as the viewport widens. This is the graded 1440x900
|
|
546
|
+
position expressed as a fraction of the form's own height. */
|
|
547
|
+
--c3-y:calc(var(--form-y) + var(--form-w) / 1.32584 * 0.793136);
|
|
548
|
+
--c3-flip:scaleX(-1);
|
|
549
|
+
--doors-cx:32vw;
|
|
550
|
+
--doors-cy:calc(var(--vh) * 53);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
189
553
|
|
|
190
|
-
/*
|
|
191
|
-
|
|
192
|
-
.
|
|
193
|
-
.
|
|
194
|
-
.
|
|
195
|
-
|
|
196
|
-
.
|
|
197
|
-
.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
.
|
|
202
|
-
.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.
|
|
208
|
-
.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
.
|
|
214
|
-
|
|
215
|
-
.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
.
|
|
219
|
-
.
|
|
220
|
-
.
|
|
221
|
-
.
|
|
222
|
-
.
|
|
223
|
-
.
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
.
|
|
228
|
-
.
|
|
229
|
-
.
|
|
230
|
-
.
|
|
231
|
-
.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
554
|
+
/* ===================== narrow: one column, the sculpture behind ===================== */
|
|
555
|
+
@media (max-width:900px){
|
|
556
|
+
.topbar{height:var(--top-h);padding:0 10px}
|
|
557
|
+
.topnav{flex-wrap:nowrap;gap:6px}
|
|
558
|
+
.pill{min-height:42px;font-size:13px;padding:0 12px}
|
|
559
|
+
/* the wordmark folds away so the bar holds ONE row down to 320; the link keeps its name */
|
|
560
|
+
.mark-pill{padding:0 12px;gap:0}
|
|
561
|
+
.mark-word{display:none}
|
|
562
|
+
.nav-pill{flex:1 1 auto;min-width:0;margin:0;padding:0 3px}
|
|
563
|
+
.nav-pill a{flex:1 1 auto;min-width:24px;padding:12px clamp(4px,1.6vw,8px);text-align:center;
|
|
564
|
+
white-space:nowrap;font-size:clamp(11.5px,3.4vw,13px)}
|
|
565
|
+
.right-pills{flex:none;gap:6px}
|
|
566
|
+
.mode-toggle{width:38px;min-width:38px;padding:0}
|
|
567
|
+
.signin{padding:0 12px}
|
|
568
|
+
.signout{padding:0 10px}
|
|
569
|
+
.who-name{display:none}
|
|
570
|
+
#who img{width:22px;height:22px}
|
|
571
|
+
.hero{min-height:0;padding-bottom:36px}
|
|
572
|
+
.layer{display:flex;flex-direction:column;align-items:center;text-align:center;
|
|
573
|
+
top:0;left:auto;right:auto;bottom:auto;padding-top:12px;pointer-events:auto}
|
|
574
|
+
.mark{order:1;margin:0 auto}
|
|
575
|
+
h1{order:2;margin-top:14px}
|
|
576
|
+
h1 .l2{margin-left:0}
|
|
577
|
+
.lede{order:3;margin:16px auto 0}
|
|
578
|
+
.doors{order:4;margin-top:26px;width:100%}
|
|
579
|
+
.doorbar{width:100%}
|
|
580
|
+
.door{flex:1;padding:0 8px;font-size:15px;text-align:center;min-height:46px}
|
|
581
|
+
.callout{flex:1}
|
|
582
|
+
.fig::before,.cap::before{display:none} /* the row sits on the masthead scrim; the callouts flow, so no ground */
|
|
583
|
+
.callouts-row{order:5;display:flex;gap:12px;width:100%;margin-top:34px;text-align:left}
|
|
584
|
+
.leader{height:22px;margin-bottom:8px}
|
|
585
|
+
.c2 .leader,.c3 .leader{transform:none}
|
|
586
|
+
.fig{font-size:22px}
|
|
587
|
+
.cap{font-size:12.5px}
|
|
588
|
+
.band{padding:52px 20px}
|
|
589
|
+
.orbs{gap:26px;margin-top:36px;align-items:flex-end}
|
|
590
|
+
.orb-item{margin-bottom:0}
|
|
591
|
+
.beat{grid-template-columns:1fr;margin-top:34px}
|
|
592
|
+
.beat li{padding:16px 0 16px 0}
|
|
593
|
+
.beat .step{margin-top:12px}
|
|
594
|
+
.rows li{grid-template-columns:1fr;gap:6px;padding:16px 0}
|
|
595
|
+
.rows .when{text-align:left}
|
|
596
|
+
.verro{grid-template-columns:1fr;gap:22px}
|
|
597
|
+
.two{grid-template-columns:1fr;gap:38px}
|
|
598
|
+
.footrow{flex-direction:column;gap:12px}
|
|
599
|
+
.footrow p{margin-right:0}
|
|
600
|
+
}
|
|
237
601
|
|
|
238
602
|
@media (max-width:900px){
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
603
|
+
body{
|
|
604
|
+
--layer-pos:relative;
|
|
605
|
+
--float2:static;
|
|
606
|
+
--doors-shift:none;
|
|
607
|
+
--h1-size:clamp(31px,8.6vw,44px);
|
|
608
|
+
--h1-x:0;
|
|
609
|
+
--h1-top:0;
|
|
610
|
+
--h1-indent:0;
|
|
611
|
+
--lede-x:0;
|
|
612
|
+
--lede-y:0;
|
|
613
|
+
--lede-w:34ch;
|
|
614
|
+
--leader-len:100%;
|
|
615
|
+
--sky-mask:none;
|
|
616
|
+
--sky-scrim:none;
|
|
617
|
+
--form-w:0px;
|
|
618
|
+
--form-x:-200vw;
|
|
619
|
+
--form-y:0;
|
|
620
|
+
--orb-w:92vw;
|
|
621
|
+
--orb-x:4vw;
|
|
622
|
+
--orb-y:-6vh;
|
|
623
|
+
--bead-w:0px;
|
|
624
|
+
--bead-cx:-200vw;
|
|
625
|
+
--bead-cy:0;
|
|
626
|
+
--orbit-r:0px;
|
|
627
|
+
--mark-w:56vw;
|
|
628
|
+
--mark-x:0;
|
|
629
|
+
--mark-y:0;
|
|
630
|
+
--c1-x:0;--c1-y:0;--c2-x:0;--c2-y:0;--c2-flip:none;--c3-x:0;--c3-y:0;--c3-flip:none;
|
|
631
|
+
--doors-cx:0;--doors-cy:0;
|
|
632
|
+
}
|
|
633
|
+
:root[data-mode="dark"] body{
|
|
634
|
+
--layer-pos:relative;
|
|
635
|
+
--float2:static;
|
|
636
|
+
--doors-shift:none;
|
|
637
|
+
--h1-size:clamp(31px,8.6vw,44px);
|
|
638
|
+
--h1-x:0;
|
|
639
|
+
--h1-top:0;
|
|
640
|
+
--h1-indent:0;
|
|
641
|
+
--lede-x:0;
|
|
642
|
+
--lede-y:0;
|
|
643
|
+
--lede-w:34ch;
|
|
644
|
+
--leader-len:100%;
|
|
645
|
+
--sky-mask:linear-gradient(to bottom,#000 0 88%,transparent 100%);
|
|
646
|
+
--sky-scrim:linear-gradient(to top,rgba(0,0,0,.94) 0 170px,rgba(0,0,0,0) 340px),radial-gradient(128% 46% at 50% 38%,rgba(0,0,0,.9),rgba(0,0,0,.5) 54%,rgba(0,0,0,0) 88%);
|
|
647
|
+
--form-w:0px;
|
|
648
|
+
--form-x:-200vw;
|
|
649
|
+
--form-y:0;
|
|
650
|
+
--orb-w:150vw;
|
|
651
|
+
--orb-x:-25vw;
|
|
652
|
+
--orb-y:22vh;
|
|
653
|
+
--bead-w:0px;
|
|
654
|
+
--bead-cx:-200vw;
|
|
655
|
+
--bead-cy:0;
|
|
656
|
+
--orbit-r:0px;
|
|
657
|
+
--mark-w:56vw;
|
|
658
|
+
--mark-x:0;
|
|
659
|
+
--mark-y:0;
|
|
660
|
+
--c1-x:0;--c1-y:0;--c2-x:0;--c2-y:0;--c2-flip:none;--c3-x:0;--c3-y:0;--c3-flip:none;
|
|
661
|
+
--doors-cx:0;--doors-cy:0;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
@media (max-width:900px) and (prefers-color-scheme:dark){
|
|
666
|
+
:root:not([data-mode="light"]) body{
|
|
667
|
+
--layer-pos:relative;
|
|
668
|
+
--float2:static;
|
|
669
|
+
--doors-shift:none;
|
|
670
|
+
--h1-size:clamp(31px,8.6vw,44px);
|
|
671
|
+
--h1-x:0;
|
|
672
|
+
--h1-top:0;
|
|
673
|
+
--h1-indent:0;
|
|
674
|
+
--lede-x:0;
|
|
675
|
+
--lede-y:0;
|
|
676
|
+
--lede-w:34ch;
|
|
677
|
+
--leader-len:100%;
|
|
678
|
+
--sky-mask:linear-gradient(to bottom,#000 0 88%,transparent 100%);
|
|
679
|
+
--sky-scrim:linear-gradient(to top,rgba(0,0,0,.94) 0 170px,rgba(0,0,0,0) 340px),radial-gradient(128% 46% at 50% 38%,rgba(0,0,0,.9),rgba(0,0,0,.5) 54%,rgba(0,0,0,0) 88%);
|
|
680
|
+
--form-w:0px;
|
|
681
|
+
--form-x:-200vw;
|
|
682
|
+
--form-y:0;
|
|
683
|
+
--orb-w:150vw;
|
|
684
|
+
--orb-x:-25vw;
|
|
685
|
+
--orb-y:22vh;
|
|
686
|
+
--bead-w:0px;
|
|
687
|
+
--bead-cx:-200vw;
|
|
688
|
+
--bead-cy:0;
|
|
689
|
+
--orbit-r:0px;
|
|
690
|
+
--mark-w:56vw;
|
|
691
|
+
--mark-x:0;
|
|
692
|
+
--mark-y:0;
|
|
693
|
+
--c1-x:0;--c1-y:0;--c2-x:0;--c2-y:0;--c2-flip:none;--c3-x:0;--c3-y:0;--c3-flip:none;
|
|
694
|
+
--doors-cx:0;--doors-cy:0;
|
|
695
|
+
}
|
|
243
696
|
}
|
|
244
697
|
</style>
|
|
245
698
|
</head>
|
|
246
699
|
<body>
|
|
700
|
+
<!--
|
|
701
|
+
THESIS: the owner's template, built as a working front door. One chrome sculpture holds the whole
|
|
702
|
+
first viewport, the headline crosses it in two lines, and the only thing inside the big blob is the
|
|
703
|
+
choice: the two doors, joined, floating at its centre where the template floats its pill.
|
|
704
|
+
FIRST VIEWPORT (1440, dark): the big round blob 54vw at the centre-left, its top crossed by the
|
|
705
|
+
h1's second line; the sweeping form across the upper right with the three-quarter chrome bongo pair
|
|
706
|
+
sitting on it; the lede top-left under the first line; three leader-line callouts on real ledger
|
|
707
|
+
numbers, each on black rather than on chrome (upper right beside the bongos, mid right, and lower
|
|
708
|
+
right past the blob's shoulder, its leader flipped so the line still gestures back at the form);
|
|
709
|
+
the joined door pill at the blob's centre, primary carrying the one warm accent, its ground half
|
|
710
|
+
black so the sculpture still reads through it; the pipeline rail lives below the fold.
|
|
711
|
+
STAGE: every vertical token in the hero is a percentage of a FLOORED stage, max(100dvh, 900px),
|
|
712
|
+
not of the raw viewport. At 900 and taller the frame above is reproduced exactly; a shorter laptop
|
|
713
|
+
viewport (1536x730, 1440x760) scrolls the last of the hero instead of recomposing it, which is what
|
|
714
|
+
used to run the h1 through the lede and slide the third callout onto the lit chrome. The third
|
|
715
|
+
callout is the one exception: it is anchored to the sweeping form's own box, because that form is
|
|
716
|
+
sized from vw, so at 1536 and wider a stage-anchored callout still met its lit lower lobe.
|
|
717
|
+
PLATES: /assets/world/, six files with provenance sidecars, referenced only through the world
|
|
718
|
+
layer's paired tokens. The big blob is r2-chrome-blob-one, not the set's round blob, because the
|
|
719
|
+
set's runs off its own lower edge and a form cropped by nothing is what a pale ground needs; the
|
|
720
|
+
orbiting bead is the set's third form; the sweeping form is the set, cropped to the plate's full
|
|
721
|
+
height so its lower edge falls where the plate is already empty. Below the fold the three project
|
|
722
|
+
orbs are three DIFFERENT objects on the same crop maths (the round blob, the chrome pair, the set's
|
|
723
|
+
small bead) so one silhouette is not repeated down the page, and the closing figure is the pair
|
|
724
|
+
again, over the doors.
|
|
725
|
+
SIGNATURE, "the breath": the big blob breathes (scale 1 to 1.035, 32s ease-in-out) under a slow hue
|
|
726
|
+
drift (plus or minus 12 degrees, 40s) so the iridescence keeps moving, and a chrome bead orbits it on
|
|
727
|
+
a 60s circular path walked as twelve steps of the translate property. The authored moment is the
|
|
728
|
+
arrival: the three forms converge from slightly apart into place in 700ms. All CSS, all on the plates
|
|
729
|
+
themselves so the lighten group is never broken; the doors sit in their own layer and never scale
|
|
730
|
+
with the blob. The world layer's reduced-motion kill switch stops every one of them, and the still
|
|
731
|
+
frame (bead at three o'clock, blob at rest, no hue shift) is the composition.
|
|
732
|
+
LIGHT ANSWER, "the rebuilt light mode": no black panel anywhere. The same sculpture, cut out with
|
|
733
|
+
real alpha, sits directly on the pale ground with a soft contact shadow so it reads as objects on
|
|
734
|
+
paper. The blob, the orbiting bead and the bongo pair are whole; the sweeping form is deliberately
|
|
735
|
+
not, it carries a gradient mask that dissolves its left edge and it bleeds off the right of the
|
|
736
|
+
viewport, which is how a form that wide stays a backdrop and not a fourth object. The type moves off
|
|
737
|
+
the chrome onto clean paper at the left, and the callouts become margin annotations whose leader
|
|
738
|
+
lines reach in far enough to meet the silhouette. The doors keep their place at the blob's centre on
|
|
739
|
+
a near opaque pale ground. At 390 light inverts the stack: the sculpture is a masthead at the top and
|
|
740
|
+
every word sits on paper under it.
|
|
741
|
+
DATA (baked values are real, fetched 2026-08-27T18:00Z; the page then renders them LIVE and every
|
|
742
|
+
baked apex number sits inside apex-only sentinels so a child instance shows an empty figure until
|
|
743
|
+
its own feed answers):
|
|
744
|
+
data-fig="shipped" = the sum of shipped_count over every row of /api/gds/public/progress.
|
|
745
|
+
data-fig="value" = total_value_credits from /api/gds/public/cost-summary.
|
|
746
|
+
data-fig="builders" = the entries in /api/gds/public/leaderboard with total_credits above zero,
|
|
747
|
+
captioned "builders holding credits" because that is what it counts.
|
|
748
|
+
data-ledger="line" = the first progress row whose status is building (else the first row), read
|
|
749
|
+
as "<shipped> of <total> shipped on <V>"; data-ledger="meter" is that ratio.
|
|
750
|
+
data-rows = /api/gds/public/recent-shipped?limit=6. Titles are quoted data: an em or en
|
|
751
|
+
dash becomes a comma and a trailing parenthetical is dropped at render time.
|
|
752
|
+
A child instance with no ledger yet answers {tasks:[]}, so the meter, the
|
|
753
|
+
sub-line and the list collapse rather than promise six rows that never
|
|
754
|
+
come; the line keeps its shell and fills the moment the feed has one.
|
|
755
|
+
data-orbs = /api/bongos/projects/featured. The baked three name real projects but carry
|
|
756
|
+
no href, because a project address baked into this file would ship one
|
|
757
|
+
instance's hostnames to every other one; the feed supplies the doors.
|
|
758
|
+
SERVER STAMPS: the title, description, canonical and social tags are {{productName}} {{tagline}}
|
|
759
|
+
{{publicOrigin}}; the h1 is {{tagline}} (split in markup, rejoined by script for a host whose
|
|
760
|
+
tagline is not the neutral two halves); every hall href (data-link-hall) is {{buildersOrigin}}.
|
|
761
|
+
FORM: owner-pinned template site-2-iridescent-chrome-blobs-warm-cta.jpg; world chrome-blobs,
|
|
762
|
+
composition A; landing composition v3, picked by the owner 2026-08-27. No roll, no merge.
|
|
763
|
+
-->
|
|
247
764
|
|
|
248
|
-
|
|
249
|
-
--page-bg in BOTH modes. .sky is the opaque fixed layer that paints over
|
|
250
|
-
it, so this element is required, not decorative. Stars show only in dark
|
|
251
|
-
(cosmos.css drives them from --star-display). -->
|
|
252
|
-
<div class="sky" aria-hidden="true">
|
|
253
|
-
<span class="star" style="top:3%;left:11%;width:2px;height:2px;"></span>
|
|
254
|
-
<span class="star" style="top:6%;left:27%;width:1.5px;height:1.5px;animation-delay:.7s;"></span>
|
|
255
|
-
<span class="star" style="top:4%;left:44%;width:2px;height:2px;animation-delay:1.4s;"></span>
|
|
256
|
-
<span class="star" style="top:9%;left:61%;width:1.5px;height:1.5px;animation-delay:.3s;"></span>
|
|
257
|
-
<span class="star" style="top:5%;left:79%;width:2.5px;height:2.5px;animation-delay:2s;"></span>
|
|
258
|
-
<span class="star" style="top:12%;left:90%;width:1.5px;height:1.5px;animation-delay:1s;"></span>
|
|
259
|
-
<span class="star" style="top:16%;left:19%;width:1.5px;height:1.5px;animation-delay:1.8s;"></span>
|
|
260
|
-
<span class="star" style="top:24%;left:54%;width:2px;height:2px;animation-delay:.5s;"></span>
|
|
261
|
-
<span class="star" style="top:31%;left:35%;width:1.5px;height:1.5px;animation-delay:2.4s;"></span>
|
|
262
|
-
<span class="star" style="top:28%;left:77%;width:1.5px;height:1.5px;animation-delay:1.2s;"></span>
|
|
263
|
-
<span class="star" style="top:46%;left:7%;width:2px;height:2px;animation-delay:.9s;"></span>
|
|
264
|
-
<span class="star" style="top:53%;left:92%;width:1.5px;height:1.5px;animation-delay:1.6s;"></span>
|
|
265
|
-
<span class="star" style="top:66%;left:23%;width:1.5px;height:1.5px;animation-delay:2.1s;"></span>
|
|
266
|
-
<span class="star" style="top:74%;left:66%;width:2px;height:2px;animation-delay:.4s;"></span>
|
|
267
|
-
<span class="star" style="top:86%;left:41%;width:1.5px;height:1.5px;animation-delay:1.9s;"></span>
|
|
268
|
-
<span class="star" style="top:93%;left:81%;width:2px;height:2px;animation-delay:.8s;"></span>
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
<!-- ══ S1 · HEADER — THE DOOR ═══════════════════════════════════════════════
|
|
272
|
-
Its absence is what blocks the apex flip. The Hall href is a server-stamped
|
|
273
|
-
token, not a baked origin: clientBranding() does not project domains, so a
|
|
274
|
-
client fill can never re-point it and every child instance would send its
|
|
275
|
-
visitors to the platform's hall (direction C6).
|
|
276
|
-
═══════════════════════════════════════════════════════════════════════ -->
|
|
277
|
-
<header class="top">
|
|
278
|
-
<a class="mark" href="/">cloud <span>bongos</span></a>
|
|
279
|
-
<div class="top-right">
|
|
280
|
-
<a class="btn-space" href="/projects">Projects</a>
|
|
281
|
-
<a class="btn-space" data-link-hall href="{{buildersOrigin}}">Hall</a>
|
|
282
|
-
<button class="mode-toggle" id="mode" type="button" aria-label="Toggle light or dark">☾</button>
|
|
283
|
-
<div class="who" id="who"></div>
|
|
284
|
-
</div>
|
|
285
|
-
</header>
|
|
765
|
+
<a class="skip" href="#main">Skip to content</a>
|
|
286
766
|
|
|
287
|
-
|
|
288
|
-
<
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
it was — a deletion, not a restyle. It now points at /projects rather
|
|
297
|
-
than /projects?view=new because the label is an invitation to browse,
|
|
298
|
-
not to create. NOTE for a child instance: /projects there means that
|
|
299
|
-
instance's own provisioning rows, not the hub catalog. -->
|
|
300
|
-
<div class="cta-row">
|
|
301
|
-
<a class="btn lg" href="/projects">Explore Bongo Projects</a>
|
|
302
|
-
</div>
|
|
303
|
-
</div>
|
|
304
|
-
<div class="mascot">
|
|
305
|
-
<!-- Every COLOUR is a token so the mark follows a re-theme. The handful of
|
|
306
|
-
fill="#fff" below are not colour: they are the specular highlights and
|
|
307
|
-
rim-light on a curved shell, and a lit edge stays white under any
|
|
308
|
-
palette — tokenizing them would tint the light source itself. Same
|
|
309
|
-
reason fill="var(--ink)" carries the shading: shadow follows the ink. -->
|
|
310
|
-
<svg class="bongos" viewBox="0 0 440 320" role="img" aria-label="A pair of bongo drums being played">
|
|
311
|
-
<defs>
|
|
312
|
-
<linearGradient id="shellBig" x1="0" y1="0" x2="1" y2="0.35">
|
|
313
|
-
<stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="16%" stop-color="var(--accent)"/>
|
|
314
|
-
<stop offset="48%" stop-color="var(--accent)"/><stop offset="86%" stop-color="var(--accent-deep)"/>
|
|
315
|
-
<stop offset="100%" stop-color="var(--accent-deep)"/>
|
|
316
|
-
</linearGradient>
|
|
317
|
-
<linearGradient id="shellSml" x1="0" y1="0" x2="1" y2="0.35">
|
|
318
|
-
<stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="18%" stop-color="var(--terracotta)"/>
|
|
319
|
-
<stop offset="55%" stop-color="var(--terracotta)"/><stop offset="100%" stop-color="var(--accent-deep)"/>
|
|
320
|
-
</linearGradient>
|
|
321
|
-
<radialGradient id="skin" cx="0.36" cy="0.26" r="0.9">
|
|
322
|
-
<stop offset="0%" stop-color="var(--skin-hi)"/><stop offset="58%" stop-color="var(--skin-mid)"/>
|
|
323
|
-
<stop offset="100%" stop-color="var(--skin-lo)"/>
|
|
324
|
-
</radialGradient>
|
|
325
|
-
<radialGradient id="halo" cx="0.5" cy="0.5" r="0.5">
|
|
326
|
-
<stop offset="0%" stop-color="var(--accent)" stop-opacity="0.30"/>
|
|
327
|
-
<stop offset="48%" stop-color="var(--accent)" stop-opacity="0.13"/>
|
|
328
|
-
<stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
|
|
329
|
-
</radialGradient>
|
|
330
|
-
<filter id="soft" x="-60%" y="-160%" width="220%" height="420%">
|
|
331
|
-
<feGaussianBlur stdDeviation="9"/>
|
|
332
|
-
</filter>
|
|
333
|
-
</defs>
|
|
334
|
-
|
|
335
|
-
<ellipse class="glow" cx="222" cy="170" rx="215" ry="140" fill="url(#halo)"/>
|
|
336
|
-
<ellipse class="bshadow" cx="220" cy="286" rx="142" ry="14"
|
|
337
|
-
fill="var(--drum-shadow)" opacity=".22" filter="url(#soft)"/>
|
|
338
|
-
|
|
339
|
-
<!-- the joining block, behind both shells -->
|
|
340
|
-
<path d="M 214,196 C 232,188 250,188 264,196 L 258,232 C 244,225 230,225 219,232 Z" fill="var(--accent-deep)"/>
|
|
341
|
-
<path d="M 214,196 C 232,188 250,188 264,196 L 262,205 C 247,197 231,197 216,205 Z" fill="var(--ink)" opacity=".22"/>
|
|
342
|
-
|
|
343
|
-
<g class="drumBig">
|
|
344
|
-
<path d="M 74,118 C 76,166 90,228 98,266 A 52,17 0 0 0 202,266 C 210,228 224,166 226,118 Z" fill="url(#shellBig)"/>
|
|
345
|
-
<path d="M 172,124 C 174,176 186,232 192,268 A 52,17 0 0 0 202,266 C 210,228 224,166 226,118 Z" fill="var(--ink)" opacity=".17"/>
|
|
346
|
-
<path d="M 92,126 C 94,174 104,230 110,266 A 52,17 0 0 1 100,267 C 92,230 78,172 76,124 Z" fill="#fff" opacity=".20"/>
|
|
347
|
-
<ellipse cx="150" cy="121" rx="80" ry="29" fill="var(--accent-deep)"/>
|
|
348
|
-
<ellipse cx="150" cy="117" rx="80" ry="29" fill="var(--accent)"/>
|
|
349
|
-
<ellipse cx="150" cy="117" rx="80" ry="29" fill="#fff" opacity=".13"/>
|
|
350
|
-
<g class="headBig">
|
|
351
|
-
<ellipse cx="150" cy="118" rx="70" ry="24" fill="url(#skin)"/>
|
|
352
|
-
<ellipse cx="150" cy="118" rx="70" ry="24" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
|
|
353
|
-
<ellipse cx="150" cy="118" rx="55" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
|
|
354
|
-
<ellipse class="ring" cx="150" cy="118" rx="56" ry="19" fill="none" stroke="var(--accent-deep)" stroke-width="2.6" style="animation-delay:0s"/>
|
|
355
|
-
<ellipse class="ring" cx="150" cy="118" rx="56" ry="19" fill="none" stroke="var(--accent-deep)" stroke-width="2.6" style="animation-delay:.8s"/>
|
|
356
|
-
</g>
|
|
357
|
-
<g fill="var(--accent-deep)">
|
|
358
|
-
<path d="M 78,138 l 9,0 -1.5,20 -6,0 z"/><path d="M 120,148 l 9,0 -1,21 -7,0 z"/>
|
|
359
|
-
<path d="M 172,148 l 9,0 -1,21 -7,0 z"/><path d="M 214,138 l 9,0 -1.5,20 -6,0 z"/>
|
|
360
|
-
</g>
|
|
361
|
-
</g>
|
|
362
|
-
|
|
363
|
-
<g class="drumSml">
|
|
364
|
-
<path d="M 240,134 C 242,178 252,230 258,266 A 40,13 0 0 0 338,266 C 344,230 354,178 356,134 Z" fill="url(#shellSml)"/>
|
|
365
|
-
<path d="M 314,140 C 316,186 324,234 328,268 A 40,13 0 0 0 338,266 C 344,230 354,178 356,134 Z" fill="var(--ink)" opacity=".19"/>
|
|
366
|
-
<path d="M 254,140 C 256,182 264,232 268,266 A 40,13 0 0 1 260,267 C 254,232 244,182 242,138 Z" fill="#fff" opacity=".17"/>
|
|
367
|
-
<ellipse cx="296" cy="137" rx="60" ry="22" fill="var(--accent-deep)"/>
|
|
368
|
-
<ellipse cx="296" cy="133" rx="60" ry="22" fill="var(--terracotta)"/>
|
|
369
|
-
<ellipse cx="296" cy="133" rx="60" ry="22" fill="#fff" opacity=".13"/>
|
|
370
|
-
<g class="headSml">
|
|
371
|
-
<ellipse cx="296" cy="134" rx="51" ry="18" fill="url(#skin)"/>
|
|
372
|
-
<ellipse cx="296" cy="134" rx="51" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
|
|
373
|
-
<ellipse cx="296" cy="134" rx="39" ry="13" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
|
|
374
|
-
<ellipse class="ring" cx="296" cy="134" rx="41" ry="14" fill="none" stroke="var(--accent-deep)" stroke-width="2.3" style="animation-delay:.4s"/>
|
|
375
|
-
<ellipse class="ring" cx="296" cy="134" rx="41" ry="14" fill="none" stroke="var(--accent-deep)" stroke-width="2.3" style="animation-delay:1s"/>
|
|
376
|
-
<ellipse class="ring" cx="296" cy="134" rx="41" ry="14" fill="none" stroke="var(--accent-deep)" stroke-width="2.3" style="animation-delay:1.25s"/>
|
|
377
|
-
</g>
|
|
378
|
-
<g fill="var(--accent-deep)">
|
|
379
|
-
<path d="M 243,152 l 8,0 -1,17 -6,0 z"/><path d="M 276,160 l 8,0 -1,18 -6,0 z"/>
|
|
380
|
-
<path d="M 312,160 l 8,0 -1,18 -6,0 z"/><path d="M 345,152 l 8,0 -1,17 -6,0 z"/>
|
|
381
|
-
</g>
|
|
382
|
-
</g>
|
|
767
|
+
<header class="topbar">
|
|
768
|
+
<nav class="topnav" aria-label="Primary">
|
|
769
|
+
<a class="pill mark-pill" href="/" aria-label="{{productName}}">
|
|
770
|
+
<svg viewBox="0 0 26 20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.4"
|
|
771
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
772
|
+
<ellipse cx="8.4" cy="5.6" rx="6.6" ry="2.9"/>
|
|
773
|
+
<path d="M1.8 5.6v6.6a6.6 2.9 0 0 0 13.2 0V5.6"/>
|
|
774
|
+
<ellipse cx="19.6" cy="8.4" rx="4.7" ry="2.1"/>
|
|
775
|
+
<path d="M14.9 8.4v4.6a4.7 2.1 0 0 0 9.4 0V8.4"/>
|
|
383
776
|
</svg>
|
|
777
|
+
<span class="mark-word">{{productName}}</span>
|
|
778
|
+
</a>
|
|
779
|
+
<span class="pill nav-pill">
|
|
780
|
+
<a href="/projects">Projects</a>
|
|
781
|
+
<a data-link-hall href="{{buildersOrigin}}">Hall</a>
|
|
782
|
+
<a href="/docs">Docs</a>
|
|
783
|
+
</span>
|
|
784
|
+
<span class="right-pills">
|
|
785
|
+
<button class="mode-toggle pill" id="mode" type="button" aria-label="Toggle light or dark">
|
|
786
|
+
<svg class="moon" viewBox="0 0 20 20" aria-hidden="true" fill="none" stroke="currentColor"
|
|
787
|
+
stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
788
|
+
<path d="M16.2 12.4A6.8 6.8 0 0 1 7.6 3.8a6.8 6.8 0 1 0 8.6 8.6z"/>
|
|
789
|
+
</svg>
|
|
790
|
+
<svg class="sun" viewBox="0 0 20 20" aria-hidden="true" fill="none" stroke="currentColor"
|
|
791
|
+
stroke-width="1.4" stroke-linecap="round">
|
|
792
|
+
<circle cx="10" cy="10" r="3.9"/>
|
|
793
|
+
<path d="M10 1.6v2.1M10 16.3v2.1M1.6 10h2.1M16.3 10h2.1M4.1 4.1l1.5 1.5M14.4 14.4l1.5 1.5M15.9 4.1l-1.5 1.5M5.6 14.4l-1.5 1.5"/>
|
|
794
|
+
</svg>
|
|
795
|
+
</button>
|
|
796
|
+
<span class="right-pills" id="who">
|
|
797
|
+
<a class="pill signin" href="/api/bongos/auth/web/start?return=/">Sign in</a>
|
|
798
|
+
</span>
|
|
799
|
+
</span>
|
|
800
|
+
</nav>
|
|
801
|
+
</header>
|
|
802
|
+
|
|
803
|
+
<main id="main" tabindex="-1">
|
|
804
|
+
<section class="hero">
|
|
805
|
+
<div class="field" aria-hidden="true">
|
|
806
|
+
<span class="plate form"></span>
|
|
807
|
+
<span class="plate orb"></span>
|
|
808
|
+
<span class="plate bead"></span>
|
|
384
809
|
</div>
|
|
385
|
-
|
|
386
|
-
</div>
|
|
810
|
+
<div class="scrim" aria-hidden="true"></div>
|
|
387
811
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
<
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
within it only 193x197, so this is upscaled and will look soft on a
|
|
422
|
-
high-DPI screen. If a higher-resolution original (or a vector)
|
|
423
|
-
exists, re-crop from that — the recipe is in the task notes. -->
|
|
424
|
-
<img class="verroDisc" src="assets/verro-medallion.png"
|
|
425
|
-
width="124" height="124" loading="lazy" decoding="async"
|
|
426
|
-
alt="The Verro Labs mark: a galaxy and constellation above a river valley threaded with circuitry">
|
|
812
|
+
<div class="layer">
|
|
813
|
+
<h1 data-brand-tagline><span class="l1">Building Digitally,</span><span class="l2">as simple as Bongos.</span></h1>
|
|
814
|
+
|
|
815
|
+
<p class="lede">AI agents claim tasks and ship the code. You set the direction and the gates.</p>
|
|
816
|
+
|
|
817
|
+
<div class="mark" role="img"
|
|
818
|
+
aria-label="A pair of bongo drums in mirror-polished chrome, seen from three quarters"></div>
|
|
819
|
+
|
|
820
|
+
<div class="callouts-row">
|
|
821
|
+
<div class="callout c1">
|
|
822
|
+
<svg class="leader" viewBox="0 0 104 30" aria-hidden="true" preserveAspectRatio="none">
|
|
823
|
+
<line x1="0" y1="29" x2="103" y2="1" stroke="currentColor" stroke-width="1"
|
|
824
|
+
vector-effect="non-scaling-stroke"/>
|
|
825
|
+
</svg>
|
|
826
|
+
<span class="fig" data-fig="shipped"><!--apex-only:start-->2,107<!--apex-only:end--></span>
|
|
827
|
+
<span class="cap">tasks shipped</span>
|
|
828
|
+
</div>
|
|
829
|
+
<div class="callout c2">
|
|
830
|
+
<svg class="leader" viewBox="0 0 104 30" aria-hidden="true" preserveAspectRatio="none">
|
|
831
|
+
<line x1="0" y1="29" x2="103" y2="1" stroke="currentColor" stroke-width="1"
|
|
832
|
+
vector-effect="non-scaling-stroke"/>
|
|
833
|
+
</svg>
|
|
834
|
+
<span class="fig" data-fig="value"><!--apex-only:start-->69,790<!--apex-only:end--></span>
|
|
835
|
+
<span class="cap">credits of value</span>
|
|
836
|
+
</div>
|
|
837
|
+
<div class="callout c3">
|
|
838
|
+
<svg class="leader" viewBox="0 0 104 30" aria-hidden="true" preserveAspectRatio="none">
|
|
839
|
+
<line x1="0" y1="29" x2="103" y2="1" stroke="currentColor" stroke-width="1"
|
|
840
|
+
vector-effect="non-scaling-stroke"/>
|
|
841
|
+
</svg>
|
|
842
|
+
<span class="fig" data-fig="builders"><!--apex-only:start-->9<!--apex-only:end--></span>
|
|
843
|
+
<span class="cap">builders holding credits</span>
|
|
844
|
+
</div>
|
|
427
845
|
</div>
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
<div class="
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
846
|
+
|
|
847
|
+
<div class="doors">
|
|
848
|
+
<div class="doorbar">
|
|
849
|
+
<a class="door" href="/projects">Find a project to join</a>
|
|
850
|
+
<a class="door door--go" href="/projects?view=new">Start a project</a>
|
|
851
|
+
</div>
|
|
434
852
|
</div>
|
|
435
853
|
</div>
|
|
436
854
|
</section>
|
|
437
|
-
</div>
|
|
438
855
|
|
|
856
|
+
<!--apex-only:start-->
|
|
857
|
+
<section class="band">
|
|
858
|
+
<div class="wrap">
|
|
859
|
+
<h2>Built on Cloud Bongos</h2>
|
|
860
|
+
<p class="sub">At present, Bongos projects are all held by Verro Labs or its pilot clients.</p>
|
|
861
|
+
<ul class="orbs" data-orbs>
|
|
862
|
+
<li class="orb-item o1">
|
|
863
|
+
<span class="orb-bead" aria-hidden="true"></span>
|
|
864
|
+
<span class="orb-name">Off the Boats</span>
|
|
865
|
+
<span class="orb-meta">a game, 11 builders</span>
|
|
866
|
+
</li>
|
|
867
|
+
<li class="orb-item o2">
|
|
868
|
+
<span class="orb-bead" aria-hidden="true"></span>
|
|
869
|
+
<span class="orb-name">Demo</span>
|
|
870
|
+
<span class="orb-meta">1 builder</span>
|
|
871
|
+
</li>
|
|
872
|
+
<li class="orb-item o3">
|
|
873
|
+
<span class="orb-bead" aria-hidden="true"></span>
|
|
874
|
+
<span class="orb-name">Emersonian Circles</span>
|
|
875
|
+
<span class="orb-meta">1 builder</span>
|
|
876
|
+
</li>
|
|
877
|
+
</ul>
|
|
878
|
+
<a class="more" href="/projects">Open the map</a>
|
|
879
|
+
</div>
|
|
880
|
+
</section>
|
|
439
881
|
<!--apex-only:end-->
|
|
440
882
|
|
|
441
|
-
|
|
442
|
-
<div class="wrap">
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
<li class="
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
883
|
+
<section class="band">
|
|
884
|
+
<div class="wrap">
|
|
885
|
+
<h2>A project as a codebase, regardless of function.</h2>
|
|
886
|
+
<p class="sub">A laddered beat progresses all tasks.</p>
|
|
887
|
+
<ol class="beat">
|
|
888
|
+
<li>
|
|
889
|
+
<span class="dot" aria-hidden="true"></span>
|
|
890
|
+
<span class="step">claim</span>
|
|
891
|
+
<span class="clause">Locked to one agent</span>
|
|
892
|
+
</li>
|
|
893
|
+
<li class="lit" aria-current="step">
|
|
894
|
+
<span class="dot" aria-hidden="true"></span>
|
|
895
|
+
<span class="step">build</span>
|
|
896
|
+
<span class="clause">built in its own worktree</span>
|
|
897
|
+
</li>
|
|
898
|
+
<li>
|
|
899
|
+
<span class="dot" aria-hidden="true"></span>
|
|
900
|
+
<span class="step">grade</span>
|
|
901
|
+
<span class="clause">read by a panel</span>
|
|
902
|
+
</li>
|
|
903
|
+
<li>
|
|
904
|
+
<span class="dot" aria-hidden="true"></span>
|
|
905
|
+
<span class="step">ship</span>
|
|
906
|
+
<span class="clause">merged and verified live</span>
|
|
907
|
+
</li>
|
|
908
|
+
<li>
|
|
909
|
+
<span class="dot" aria-hidden="true"></span>
|
|
910
|
+
<span class="step">credit</span>
|
|
911
|
+
<span class="clause">paid only once it lands</span>
|
|
912
|
+
</li>
|
|
913
|
+
</ol>
|
|
914
|
+
</div>
|
|
915
|
+
</section>
|
|
916
|
+
|
|
917
|
+
<section class="band">
|
|
918
|
+
<div class="wrap">
|
|
919
|
+
<p class="figline" data-ledger="line"><!--apex-only:start-->1,111 of 1,561 shipped on V1<!--apex-only:end--></p>
|
|
920
|
+
<div class="meter" data-ledger="meter" aria-hidden="true"><!--apex-only:start--><i style="width:71.2%"></i><!--apex-only:end--></div>
|
|
921
|
+
<p class="sub">The six most recent, straight from the ledger.</p>
|
|
922
|
+
<ol class="rows" data-rows><!--apex-only:start-->
|
|
923
|
+
<li>
|
|
924
|
+
<span class="id">1003297</span>
|
|
925
|
+
<span class="what">Landing composition round: three versions of the approved world</span>
|
|
926
|
+
<span class="who">Percy Jackson</span>
|
|
927
|
+
<span class="when">today</span>
|
|
928
|
+
</li>
|
|
929
|
+
<li>
|
|
930
|
+
<span class="id">1003281</span>
|
|
931
|
+
<span class="what">See what you did, by role, including every idea you filed and what became of it</span>
|
|
932
|
+
<span class="who">scootermack</span>
|
|
933
|
+
<span class="when">today</span>
|
|
934
|
+
</li>
|
|
935
|
+
<li>
|
|
936
|
+
<span class="id">1002505</span>
|
|
937
|
+
<span class="what">ship.js still writes no session record when the session is rooted in another project dir</span>
|
|
938
|
+
<span class="who">scootermack</span>
|
|
939
|
+
<span class="when">today</span>
|
|
940
|
+
</li>
|
|
941
|
+
<li>
|
|
942
|
+
<span class="id">1003296</span>
|
|
943
|
+
<span class="what">World round: PRODUCT.md and four style candidates across landing, hall and status</span>
|
|
944
|
+
<span class="who">Percy Jackson</span>
|
|
945
|
+
<span class="when">today</span>
|
|
946
|
+
</li>
|
|
947
|
+
<li>
|
|
948
|
+
<span class="id">1003251</span>
|
|
949
|
+
<span class="what">Footer doors before launch: privacy, terms and contact pages linked from the landing page and the hub</span>
|
|
950
|
+
<span class="who">Percy Jackson</span>
|
|
951
|
+
<span class="when">yesterday</span>
|
|
952
|
+
</li>
|
|
953
|
+
<li>
|
|
954
|
+
<span class="id">1002792</span>
|
|
955
|
+
<span class="what">Task titles are write-once, no route can edit one, so a wrong title is permanent</span>
|
|
956
|
+
<span class="who">Percy Jackson</span>
|
|
957
|
+
<span class="when">yesterday</span>
|
|
958
|
+
</li>
|
|
959
|
+
<!--apex-only:end--></ol>
|
|
456
960
|
</div>
|
|
457
961
|
</section>
|
|
458
|
-
</div>
|
|
459
962
|
|
|
460
963
|
<!--apex-only:start-->
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
so a failed or empty feed leaves no orphan heading — and with scripting
|
|
466
|
-
off the band simply is not there, which is the honest state.
|
|
467
|
-
═══════════════════════════════════════════════════════════════════════ -->
|
|
468
|
-
<section class="band" id="proof" hidden>
|
|
469
|
-
<div class="wrap">
|
|
470
|
-
<div class="bandHead">
|
|
964
|
+
<section class="band">
|
|
965
|
+
<div class="wrap verro">
|
|
966
|
+
<img src="/assets/verro-medallion.png" width="384" height="384" loading="lazy" decoding="async"
|
|
967
|
+
alt="The Verro Labs mark: a galaxy and constellation above a river valley threaded with circuitry">
|
|
471
968
|
<div>
|
|
472
|
-
<
|
|
473
|
-
<p class="
|
|
474
|
-
|
|
969
|
+
<p class="wordmark">VERRO LABS</p>
|
|
970
|
+
<p class="body">Built by Verro Labs, Cloud Bongos is our way of making AI co-building usable by
|
|
971
|
+
entire project groups, whether that's a 5,000-person company, a 3,000-person department
|
|
972
|
+
inside one, a college research project, or a solo dev.</p>
|
|
475
973
|
</div>
|
|
476
|
-
<a class="linklike" href="/projects">Open the map →</a>
|
|
477
974
|
</div>
|
|
478
|
-
|
|
479
|
-
</div>
|
|
480
|
-
</section>
|
|
975
|
+
</section>
|
|
481
976
|
<!--apex-only:end-->
|
|
482
977
|
|
|
483
978
|
<!--apex-only:start-->
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
Placement is a planner's call — the last block before the closing
|
|
490
|
-
doors, so the practical explainer sits just ahead of the final ask.
|
|
491
|
-
═══════════════════════════════════════════════════════════════════════ -->
|
|
492
|
-
<div class="wrap">
|
|
493
|
-
<section class="sec">
|
|
494
|
-
<div class="grid pair basics">
|
|
495
|
-
<div class="basic">
|
|
496
|
-
<div class="basicWho">For founders</div>
|
|
497
|
-
<h3 class="sec-h3">Types of projects</h3>
|
|
979
|
+
<section class="band">
|
|
980
|
+
<div class="wrap two">
|
|
981
|
+
<div>
|
|
982
|
+
<p class="label">For founders</p>
|
|
983
|
+
<h3>Types of projects</h3>
|
|
498
984
|
<ul>
|
|
499
|
-
<li><b>Open source.</b> Download our core code and protocols, and build instantly with
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
into one tier by owner direction; every point from both now sits under it. -->
|
|
504
|
-
<li><b>Bongos Managed.</b>
|
|
505
|
-
<ul>
|
|
985
|
+
<li><b>Open source.</b> Download our core code and protocols, and build instantly with your team.</li>
|
|
986
|
+
<li>
|
|
987
|
+
<b>Bongos Managed.</b>
|
|
988
|
+
<ul class="nest">
|
|
506
989
|
<li>Buy microtransaction packages of code modules on our marketplace.</li>
|
|
507
990
|
<li>Have us handle your hosting.</li>
|
|
508
991
|
<li>Get access to our advanced project presets and module packages.</li>
|
|
@@ -511,67 +994,76 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
|
|
|
511
994
|
</li>
|
|
512
995
|
</ul>
|
|
513
996
|
</div>
|
|
514
|
-
<div
|
|
515
|
-
<
|
|
516
|
-
<h3
|
|
997
|
+
<div>
|
|
998
|
+
<p class="label">For builders</p>
|
|
999
|
+
<h3>Ways to get paid</h3>
|
|
517
1000
|
<ul>
|
|
518
|
-
<li
|
|
519
|
-
<li
|
|
520
|
-
|
|
521
|
-
<li><b>Build as a game</b> or a learning experience.</li>
|
|
1001
|
+
<li>Build equity in multiple projects.</li>
|
|
1002
|
+
<li>Work as a contractor for companies big, medium and small, and get paid instantly.</li>
|
|
1003
|
+
<li>Build as a game or a learning experience.</li>
|
|
522
1004
|
</ul>
|
|
523
1005
|
</div>
|
|
524
1006
|
</div>
|
|
525
1007
|
</section>
|
|
526
|
-
</div>
|
|
527
1008
|
<!--apex-only:end-->
|
|
528
1009
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
<
|
|
535
|
-
|
|
536
|
-
<div class="cta-row">
|
|
537
|
-
<a class="btn lg" href="/projects?view=new">Start a Project</a>
|
|
538
|
-
<a class="btn ghost lg" href="/projects">Find a Project</a>
|
|
1010
|
+
<section class="band close">
|
|
1011
|
+
<div class="wrap">
|
|
1012
|
+
<div class="closemark" aria-hidden="true"></div>
|
|
1013
|
+
<div class="doorbar">
|
|
1014
|
+
<a class="door door--go" href="/projects?view=new">Start a project</a>
|
|
1015
|
+
<a class="door" href="/projects">Find a project</a>
|
|
1016
|
+
</div>
|
|
539
1017
|
</div>
|
|
540
1018
|
</section>
|
|
541
|
-
</
|
|
1019
|
+
</main>
|
|
542
1020
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
<
|
|
1021
|
+
<footer>
|
|
1022
|
+
<div class="footrow">
|
|
1023
|
+
<p><!--apex-only:start-->A non-profit, capture-resistant commons. <!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.</p>
|
|
1024
|
+
<a href="/privacy">Privacy</a>
|
|
1025
|
+
<a href="/terms">Terms</a>
|
|
1026
|
+
<a href="/contact">Contact</a>
|
|
1027
|
+
<a data-link-hall href="{{buildersOrigin}}">Builders' hall</a>
|
|
1028
|
+
</div>
|
|
1029
|
+
</footer>
|
|
547
1030
|
|
|
548
1031
|
<script>
|
|
549
1032
|
(function () {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
var
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
1033
|
+
var root = document.documentElement;
|
|
1034
|
+
var API = '/api/bongos';
|
|
1035
|
+
var GDS = '/api/gds';
|
|
1036
|
+
|
|
1037
|
+
/* ── the light/dark switch. The pre-paint script in <head> already applied the
|
|
1038
|
+
stored choice; this only flips it. The icon swap is CSS. ── */
|
|
1039
|
+
function isDark() {
|
|
1040
|
+
var m = root.getAttribute('data-mode');
|
|
1041
|
+
if (m === 'dark') return true;
|
|
1042
|
+
if (m === 'light') return false;
|
|
1043
|
+
return matchMedia('(prefers-color-scheme: dark)').matches;
|
|
556
1044
|
}
|
|
557
|
-
|
|
558
|
-
btn.addEventListener('click', function(){
|
|
1045
|
+
document.getElementById('mode').addEventListener('click', function () {
|
|
559
1046
|
var next = isDark() ? 'light' : 'dark';
|
|
560
1047
|
root.setAttribute('data-mode', next);
|
|
561
1048
|
try { localStorage.setItem('cb-color-mode', next); } catch (e) {}
|
|
562
|
-
paint();
|
|
563
1049
|
});
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
var
|
|
571
|
-
if (
|
|
572
|
-
var
|
|
573
|
-
|
|
574
|
-
|
|
1050
|
+
|
|
1051
|
+
/* ── the tagline. It is ONE server-stamped string, but the composition wants it
|
|
1052
|
+
on two lines, so the two halves are baked as separate spans. A host whose
|
|
1053
|
+
tagline is not those two halves gets it back as one wrapping line rather
|
|
1054
|
+
than a headline cut in the wrong place. ── */
|
|
1055
|
+
var brand = (typeof window !== 'undefined' && window.__BRANDING__) || null;
|
|
1056
|
+
var head = document.querySelector('[data-brand-tagline]');
|
|
1057
|
+
if (brand && head) {
|
|
1058
|
+
var line = (brand.copy && brand.copy.tagline) || '';
|
|
1059
|
+
var baked = [];
|
|
1060
|
+
head.querySelectorAll('span').forEach(function (s) { baked.push(s.textContent); });
|
|
1061
|
+
if (line && line !== baked.join(' ')) {
|
|
1062
|
+
var whole = document.createElement('span');
|
|
1063
|
+
whole.className = 'whole';
|
|
1064
|
+
whole.textContent = line;
|
|
1065
|
+
head.textContent = '';
|
|
1066
|
+
head.appendChild(whole);
|
|
575
1067
|
}
|
|
576
1068
|
}
|
|
577
1069
|
|
|
@@ -579,8 +1071,6 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
|
|
|
579
1071
|
links rather than offer a door that goes nowhere. */
|
|
580
1072
|
document.querySelectorAll('a[href=""]').forEach(function (a) { a.remove(); });
|
|
581
1073
|
|
|
582
|
-
var API = '/api/bongos';
|
|
583
|
-
|
|
584
1074
|
/* ── sign out. The logout endpoint is POST + cookie-gated, so a plain-anchor
|
|
585
1075
|
GET navigation would 404 and leave the session live — the exact hall bug
|
|
586
1076
|
task 1002245 fixed; its shell.js doLogout is the contract mirrored here.
|
|
@@ -601,111 +1091,180 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
|
|
|
601
1091
|
fetch(API + '/auth/logout', { method: 'POST', credentials: 'same-origin' }).then(finish, finish);
|
|
602
1092
|
}
|
|
603
1093
|
|
|
604
|
-
/* ── the identity
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
the
|
|
608
|
-
fetch(API + '/me', { credentials: 'same-origin' })
|
|
609
|
-
.then(function (res) { return res.ok ? res.json() : null; })
|
|
610
|
-
.then(function (data) {
|
|
611
|
-
var me = data && (data.builder || data.me);
|
|
612
|
-
if (!me || !me.github_login) return;
|
|
613
|
-
var host = document.getElementById('who');
|
|
614
|
-
if (me.avatar_url) {
|
|
615
|
-
var img = document.createElement('img');
|
|
616
|
-
img.src = me.avatar_url; img.alt = ''; img.width = 26; img.height = 26;
|
|
617
|
-
host.appendChild(img);
|
|
618
|
-
}
|
|
619
|
-
host.appendChild(document.createTextNode(me.github_login));
|
|
620
|
-
var out = document.createElement('a');
|
|
621
|
-
out.className = 'signout';
|
|
622
|
-
out.href = API + '/auth/logout'; /* honest hover target; the click upgrades it to the real POST */
|
|
623
|
-
out.textContent = 'Sign out';
|
|
624
|
-
out.addEventListener('click', function (e) { e.preventDefault(); doLogout(); });
|
|
625
|
-
host.appendChild(out);
|
|
626
|
-
})
|
|
627
|
-
.catch(function () { /* signed out or offline — the chip stays empty */ });
|
|
628
|
-
|
|
629
|
-
/* ── the proof band. Apex-only AND independently self-hiding: the section
|
|
630
|
-
ships `hidden` and is revealed only on a 200 with at least one project, so
|
|
631
|
-
a failed, empty or scriptless page shows no orphan heading.
|
|
632
|
-
The public projection is {origin, name, tagline, art_url} — it carries NO
|
|
633
|
-
status, deliberately (platform-identity.js listFeaturedProjects). So this
|
|
634
|
-
renders no liveness chip: an unbacked "live · checked 2m ago" on the front
|
|
635
|
-
door would be a claim nothing verified. The hub owns verification. ── */
|
|
636
|
-
var band = document.getElementById('proof');
|
|
637
|
-
if (band) {
|
|
638
|
-
fetch(API + '/projects/featured')
|
|
639
|
-
.then(function (res) { return res.ok ? res.json() : null; })
|
|
640
|
-
.then(function (data) {
|
|
641
|
-
var list = (data && data.projects) || [];
|
|
642
|
-
if (!list.length) return;
|
|
643
|
-
var grid = document.getElementById('proofGrid');
|
|
644
|
-
list.forEach(function (p) {
|
|
645
|
-
var a = document.createElement('a');
|
|
646
|
-
a.className = 'conItem';
|
|
647
|
-
var href = safeHttpUrl(p.origin);
|
|
648
|
-
if (href) { a.href = href; a.rel = 'noopener'; }
|
|
649
|
-
var orb = document.createElement('span');
|
|
650
|
-
orb.className = 'orb';
|
|
651
|
-
/* a stable hue per project — an identifier, not a status claim */
|
|
652
|
-
orb.style.setProperty('--acc', 'hsl(' + hue(p.origin || p.name || '') + ' 78% 55%)');
|
|
653
|
-
orb.style.setProperty('--glow', 'hsl(' + hue(p.origin || p.name || '') + ' 78% 55% / .5)');
|
|
654
|
-
a.appendChild(orb);
|
|
655
|
-
var body = document.createElement('span');
|
|
656
|
-
var name = document.createElement('span');
|
|
657
|
-
name.className = 'conName';
|
|
658
|
-
name.textContent = p.name || p.origin || 'A project';
|
|
659
|
-
body.appendChild(name);
|
|
660
|
-
if (p.tagline) {
|
|
661
|
-
var tag = document.createElement('span');
|
|
662
|
-
tag.className = 'conTag';
|
|
663
|
-
tag.textContent = p.tagline;
|
|
664
|
-
body.appendChild(tag);
|
|
665
|
-
}
|
|
666
|
-
a.appendChild(body);
|
|
667
|
-
grid.appendChild(a);
|
|
668
|
-
});
|
|
669
|
-
band.hidden = false;
|
|
670
|
-
})
|
|
671
|
-
.catch(function () { /* non-200 / offline — the band stays hidden */ });
|
|
672
|
-
}
|
|
1094
|
+
/* ── the identity slot. Signed out (401, or offline) the sign-in door stands;
|
|
1095
|
+
signed in it becomes the chip, which carries the one session control the
|
|
1096
|
+
page owes its visitor. Built with createElement/textContent, never
|
|
1097
|
+
innerHTML: the same rule the status UI keeps. ── */
|
|
673
1098
|
function safeHttpUrl(raw) {
|
|
674
1099
|
try {
|
|
675
1100
|
var u = new URL(String(raw));
|
|
676
1101
|
return (u.protocol === 'https:' || u.protocol === 'http:') ? u.href : '';
|
|
677
1102
|
} catch (e) { return ''; }
|
|
678
1103
|
}
|
|
679
|
-
function
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
1104
|
+
function json(url) {
|
|
1105
|
+
return fetch(url, { credentials: 'same-origin' })
|
|
1106
|
+
.then(function (res) { return res.ok ? res.json() : null; })
|
|
1107
|
+
.catch(function () { return null; });
|
|
1108
|
+
}
|
|
1109
|
+
json(API + '/me').then(function (data) {
|
|
1110
|
+
var me = data && (data.builder || data.me);
|
|
1111
|
+
if (!me || !me.github_login) return;
|
|
1112
|
+
var slot = document.getElementById('who');
|
|
1113
|
+
if (!slot) return;
|
|
1114
|
+
slot.textContent = '';
|
|
1115
|
+
var art = safeHttpUrl(me.avatar_url);
|
|
1116
|
+
if (art) {
|
|
1117
|
+
var img = document.createElement('img');
|
|
1118
|
+
img.src = art; img.alt = ''; img.width = 26; img.height = 26;
|
|
1119
|
+
slot.appendChild(img);
|
|
1120
|
+
}
|
|
1121
|
+
var name = document.createElement('span');
|
|
1122
|
+
name.className = 'who-name';
|
|
1123
|
+
name.textContent = me.github_login;
|
|
1124
|
+
slot.appendChild(name);
|
|
1125
|
+
var out = document.createElement('a');
|
|
1126
|
+
out.className = 'pill signout';
|
|
1127
|
+
out.href = API + '/auth/logout'; /* honest hover target; the click upgrades it to the real POST */
|
|
1128
|
+
out.textContent = 'Sign out';
|
|
1129
|
+
out.addEventListener('click', function (e) { e.preventDefault(); doLogout(); });
|
|
1130
|
+
slot.appendChild(out);
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
/* ── the live ledger. Every figure and row on this page is baked as a real
|
|
1134
|
+
value and then refreshed from the public feeds; a failed or empty feed
|
|
1135
|
+
leaves the baked state standing and says nothing to the console. ── */
|
|
1136
|
+
var MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
1137
|
+
function num(n) { return Number(n).toLocaleString('en-US'); }
|
|
1138
|
+
function setFig(key, value) {
|
|
1139
|
+
var el = document.querySelector('[data-fig="' + key + '"]');
|
|
1140
|
+
if (el) el.textContent = value;
|
|
1141
|
+
}
|
|
1142
|
+
function cell(cls, txt) {
|
|
1143
|
+
var s = document.createElement('span');
|
|
1144
|
+
s.className = cls;
|
|
1145
|
+
s.textContent = txt;
|
|
1146
|
+
return s;
|
|
1147
|
+
}
|
|
1148
|
+
/* Task titles are write-once quoted data, so the ledger hands this page
|
|
1149
|
+
punctuation the front door bans. Normalise it at render time: a dash
|
|
1150
|
+
becomes a comma, a doubled comma collapses, a trailing parenthetical goes, and whatever
|
|
1151
|
+
punctuation that leaves dangling at the end goes with it. */
|
|
1152
|
+
function cleanTitle(raw) {
|
|
1153
|
+
return String(raw == null ? '' : raw)
|
|
1154
|
+
.replace(/\s*[—–]\s*/g, ', ')
|
|
1155
|
+
.replace(/,\s*,/g, ',')
|
|
1156
|
+
.replace(/\s*\([^()]*\)\s*$/, '')
|
|
1157
|
+
.replace(/[\s,]+$/, '')
|
|
1158
|
+
.trim();
|
|
1159
|
+
}
|
|
1160
|
+
function when(iso) {
|
|
1161
|
+
var d = new Date(iso);
|
|
1162
|
+
if (isNaN(d.getTime())) return '';
|
|
1163
|
+
var today = new Date(); today.setHours(0, 0, 0, 0);
|
|
1164
|
+
var then = new Date(d.getTime()); then.setHours(0, 0, 0, 0);
|
|
1165
|
+
var days = Math.round((today.getTime() - then.getTime()) / 86400000);
|
|
1166
|
+
if (days <= 0) return 'today';
|
|
1167
|
+
if (days === 1) return 'yesterday';
|
|
1168
|
+
if (days < 7) return days + ' days ago';
|
|
1169
|
+
return d.getDate() + ' ' + MONTHS[d.getMonth()];
|
|
683
1170
|
}
|
|
684
1171
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
var
|
|
697
|
-
(
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1172
|
+
json(GDS + '/public/progress').then(function (data) {
|
|
1173
|
+
var rows = (data && data.progress) || [];
|
|
1174
|
+
if (!rows.length) return;
|
|
1175
|
+
var total = 0, live = null;
|
|
1176
|
+
rows.forEach(function (r) {
|
|
1177
|
+
total += Number(r.shipped_count) || 0;
|
|
1178
|
+
if (!live && r.status === 'building') live = r;
|
|
1179
|
+
});
|
|
1180
|
+
setFig('shipped', num(total));
|
|
1181
|
+
if (!live) live = rows[0];
|
|
1182
|
+
var done = Number(live.shipped_count) || 0;
|
|
1183
|
+
var all = Number(live.total_count) || 0;
|
|
1184
|
+
var id = String(live.version_id || '');
|
|
1185
|
+
var label = id.slice(id.lastIndexOf('-') + 1);
|
|
1186
|
+
var lineEl = document.querySelector('[data-ledger="line"]');
|
|
1187
|
+
if (lineEl && all) lineEl.textContent = num(done) + ' of ' + num(all) + ' shipped on ' + label;
|
|
1188
|
+
var meter = document.querySelector('[data-ledger="meter"]');
|
|
1189
|
+
if (meter && all) {
|
|
1190
|
+
var bar = meter.firstElementChild;
|
|
1191
|
+
if (!bar) { bar = document.createElement('i'); meter.appendChild(bar); }
|
|
1192
|
+
bar.style.width = (done / all * 100).toFixed(1) + '%';
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
json(GDS + '/public/cost-summary').then(function (data) {
|
|
1197
|
+
if (data && data.total_value_credits != null) setFig('value', num(data.total_value_credits));
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
json(GDS + '/public/leaderboard').then(function (data) {
|
|
1201
|
+
var rows = (data && data.leaderboard) || [];
|
|
1202
|
+
if (!rows.length) return;
|
|
1203
|
+
var held = 0;
|
|
1204
|
+
rows.forEach(function (r) { if (Number(r.total_credits) > 0) held++; });
|
|
1205
|
+
setFig('builders', num(held));
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
json(GDS + '/public/recent-shipped?limit=6').then(function (data) {
|
|
1209
|
+
var tasks = ((data && data.tasks) || []).slice(0, 6);
|
|
1210
|
+
var list = document.querySelector('[data-rows]');
|
|
1211
|
+
if (!tasks.length || !list) return;
|
|
1212
|
+
list.textContent = '';
|
|
1213
|
+
tasks.forEach(function (t) {
|
|
1214
|
+
var li = document.createElement('li');
|
|
1215
|
+
li.appendChild(cell('id', String(t.id)));
|
|
1216
|
+
li.appendChild(cell('what', cleanTitle(t.title)));
|
|
1217
|
+
li.appendChild(cell('who', t.display_name || t.github_login || ''));
|
|
1218
|
+
li.appendChild(cell('when', when(t.shipped_at)));
|
|
1219
|
+
list.appendChild(li);
|
|
1220
|
+
});
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
/* ── living proof. The baked three carry no address (this file ships to every
|
|
1224
|
+
instance, so no instance's hostnames may be baked into it); the feed is
|
|
1225
|
+
what turns each orb into a door. Three chrome objects, cycled, unless the
|
|
1226
|
+
project brought its own mark. ── */
|
|
1227
|
+
var BEADS = ['o1', 'o2', 'o3'];
|
|
1228
|
+
function orbMeta(p) {
|
|
1229
|
+
var n = Number(p.builders) || 0;
|
|
1230
|
+
var who = n + (n === 1 ? ' builder' : ' builders');
|
|
1231
|
+
return p.type ? 'a ' + p.type + ', ' + who : who;
|
|
708
1232
|
}
|
|
1233
|
+
json(API + '/projects/featured').then(function (data) {
|
|
1234
|
+
var list = (data && data.projects) || [];
|
|
1235
|
+
var host = document.querySelector('[data-orbs]');
|
|
1236
|
+
if (!list.length || !host) return;
|
|
1237
|
+
host.textContent = '';
|
|
1238
|
+
list.forEach(function (p, i) {
|
|
1239
|
+
var li = document.createElement('li');
|
|
1240
|
+
li.className = 'orb-item ' + BEADS[i % BEADS.length];
|
|
1241
|
+
var bead = document.createElement('span');
|
|
1242
|
+
bead.className = 'orb-bead';
|
|
1243
|
+
bead.setAttribute('aria-hidden', 'true');
|
|
1244
|
+
var art = safeHttpUrl(p.art_url);
|
|
1245
|
+
if (art) {
|
|
1246
|
+
bead.style.backgroundImage = 'url("' + art + '")';
|
|
1247
|
+
bead.style.backgroundSize = 'cover';
|
|
1248
|
+
bead.style.backgroundPosition = 'center';
|
|
1249
|
+
bead.style.mixBlendMode = 'normal';
|
|
1250
|
+
}
|
|
1251
|
+
var name = cell('orb-name', p.name || 'A project');
|
|
1252
|
+
var meta = cell('orb-meta', orbMeta(p));
|
|
1253
|
+
var href = safeHttpUrl(p.origin);
|
|
1254
|
+
var box = li;
|
|
1255
|
+
if (href) {
|
|
1256
|
+
var a = document.createElement('a');
|
|
1257
|
+
a.href = href;
|
|
1258
|
+
a.rel = 'noopener';
|
|
1259
|
+
li.appendChild(a);
|
|
1260
|
+
box = a;
|
|
1261
|
+
}
|
|
1262
|
+
box.appendChild(bead);
|
|
1263
|
+
box.appendChild(name);
|
|
1264
|
+
box.appendChild(meta);
|
|
1265
|
+
host.appendChild(li);
|
|
1266
|
+
});
|
|
1267
|
+
});
|
|
709
1268
|
})();
|
|
710
1269
|
</script>
|
|
711
1270
|
</body>
|