@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.
Files changed (59) hide show
  1. package/.bongos-core.json +130 -50
  2. package/config/branding.neutral.json +19 -19
  3. package/config/design-tokens.neutral.json +31 -36
  4. package/docs/adr/0196-chrome-world-token-contract-and-the-landing-sheets.md +31 -0
  5. package/docs/adr/README.md +1 -0
  6. package/docs/branding-contract.md +1 -1
  7. package/docs/copy-inventory.md +78 -53
  8. package/docs/copy-registry.json +389 -120
  9. package/docs/file-map.md +14 -0
  10. package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
  11. package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
  12. package/modules/dev-box/app/assets/tray/win-off.png +0 -0
  13. package/modules/dev-box/app/assets/tray/win-on.png +0 -0
  14. package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
  15. package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
  16. package/modules/dev-box/app/build-resources/icon.png +0 -0
  17. package/modules/dev-box/app/renderer/index.html +1 -1
  18. package/modules/dev-box/app/renderer/styles.css +3 -1
  19. package/modules/dev-box/app/renderer/theme.generated.css +13 -13
  20. package/modules/dev-box/app/scripts/gen-brand.js +13 -13
  21. package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +3 -3
  22. package/modules/dev-box/app/scripts/gen-icons.js +3 -3
  23. package/modules/dev-box/app/src/brand.generated.json +1 -1
  24. package/modules/provisioning/routes/provisioning.js +4 -4
  25. package/modules/status-ui/public/explore.html +12 -6
  26. package/package-lock.json +2 -2
  27. package/package.json +1 -1
  28. package/public-landing/assets/cosmos.css +82 -64
  29. package/public-landing/assets/world/og-card.jpg +0 -0
  30. package/public-landing/assets/world/r2-bongos-chrome-a-cut.webp +0 -0
  31. package/public-landing/assets/world/r2-bongos-chrome-a-cut.webp.json +12 -0
  32. package/public-landing/assets/world/r2-bongos-chrome-b.jpg +0 -0
  33. package/public-landing/assets/world/r2-bongos-chrome-b.jpg.json +12 -0
  34. package/public-landing/assets/world/r2-chrome-blob-one-cut.webp +0 -0
  35. package/public-landing/assets/world/r2-chrome-blob-one-cut.webp.json +13 -0
  36. package/public-landing/assets/world/r2-chrome-blob-one.jpg +0 -0
  37. package/public-landing/assets/world/r2-chrome-blob-one.jpg.json +12 -0
  38. package/public-landing/assets/world/r2-chrome-blobs-set-cut.webp +0 -0
  39. package/public-landing/assets/world/r2-chrome-blobs-set-cut.webp.json +13 -0
  40. package/public-landing/assets/world/r2-chrome-blobs-set.jpg +0 -0
  41. package/public-landing/assets/world/r2-chrome-blobs-set.jpg.json +12 -0
  42. package/public-landing/assets/world.css +241 -0
  43. package/public-landing/contact.html +1 -1
  44. package/public-landing/index.html +1139 -580
  45. package/public-landing/privacy.html +1 -1
  46. package/public-landing/projects.html +2 -2
  47. package/public-landing/terms.html +1 -1
  48. package/src/bongos/platform-visibility-gate.js +8 -7
  49. package/src/bongos/stealth-gate.js +9 -8
  50. package/src/module-api.js +3 -1
  51. package/src/ui/adapters/claude-design/index.js +2 -2
  52. package/tests/cloud_bongos_pack.mjs +22 -18
  53. package/tests/cosmos_token_contrast.mjs +23 -8
  54. package/tests/landing_legal_pages.mjs +8 -5
  55. package/tests/landing_page.mjs +295 -166
  56. package/tests/platform_boot.mjs +3 -1
  57. package/tests/projects_hub.mjs +9 -5
  58. package/tests/projects_hub_pre_uat.mjs +3 -1
  59. 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}} — {{tagline}}</title>
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}} — {{tagline}}">
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}}/buddha.png">
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}} — {{tagline}}">
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}}/buddha.png">
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=Baloo+2:wght@500;600;700&family=Quicksand:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap">
37
- <!-- The shared token + component layer. /create and /projects link the same
38
- file; the landing page adds components, it does not fork the system. -->
39
- <link rel="stylesheet" href="assets/cosmos.css">
40
- <script>
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
- Only components that are genuinely this page's: the hero split, the bongo
50
- pair, the mechanism cards, the proof band, the doors. The container, type
51
- scale, spacing scale, grid primitive and mid breakpoint all live in
52
- cosmos.css so the landing page and the hub share them (direction §6).
53
-
54
- There is deliberately NO :root block here. injectBrandingGlobal splices the
55
- branding pack's theme <style> at the END of <head>; a page-local :root sits
56
- at the same specificity and, being earlier, would be silently outranked in
57
- one direction and would defeat re-branding in the other (direction C1).
58
- Page-local custom properties are therefore scoped to their component, and
59
- every dark-mode rule is written TWICE — the data-mode block and its
60
- prefers-color-scheme twin — matching cosmos.css's own pattern (C2).
61
- ──────────────────────────────────────────────────────────────────────── */
62
-
63
- /* ── S2 · hero ── */
64
- .lead{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(18px,4vw,52px);align-items:center;padding:clamp(8px,2.5vw,26px) 0 clamp(30px,5vw,60px);}
65
- .eyebrow{font-family:var(--font-mono);font-size:13.5px;font-weight:500;color:var(--chrome-ink-dim);margin-bottom:18px;text-shadow:var(--chrome-glow);}
66
- .eyebrow .typed{color:var(--chrome-accent);font-weight:600;}
67
- .eyebrow .caret{display:inline-block;width:8px;color:var(--chrome-accent);animation:blink 1.05s step-end infinite;}
68
- @keyframes blink{50%{opacity:0;}}
69
- h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4vw,58px);line-height:1.05;letter-spacing:-1.3px;color:var(--chrome-ink);margin-bottom:18px;text-shadow:var(--chrome-glow);}
70
- .lead .lede{font-size:clamp(15.5px,1.85vw,18px);color:var(--hero-lede);max-width:50ch;margin-bottom:26px;line-height:1.58;text-shadow:var(--chrome-glow);font-weight:500;}
71
- .cta-row{display:flex;gap:12px;flex-wrap:wrap;align-items:center;}
72
- .btn.lg{font-size:16.5px;padding:14px 28px;}
73
-
74
- /* ── the bongo pair · the hero mark ──
75
- The brand's OWN mark made dimensional: the favicon is already two circles,
76
- big and small, with light heads. Every fill is a token, so it follows a
77
- re-theme instead of pinning one instance's art onto every instance's page.
78
-
79
- The head is SKIN, not a card. It carries its own --skin-* tokens and must
80
- NEVER inherit --bg-card — that turns it black in dark mode. These are
81
- component-scoped (on .bongos, not :root) so they cannot collide with the
82
- pack's contract tier.
83
-
84
- The ground shadow follows cosmos.css's --shadow-card rule: ink-derived in
85
- light (warm brown), plain black in dark — in dark, --ink is near-white and
86
- an ink fill would glow, not shade. */
87
- .bongos{display:block;width:100%;height:auto;overflow:visible;
88
- --skin-hi:#fffdf8;--skin-mid:#fff6e9;--skin-lo:#e8d3b6;--drum-shadow:var(--ink);}
89
- :root[data-mode="dark"] .bongos{--skin-hi:#fdf7ea;--skin-mid:#efe1c8;--skin-lo:#c3ae90;--drum-shadow:#000;}
90
- @media (prefers-color-scheme:dark){:root:not([data-mode="light"]) .bongos{--skin-hi:#fdf7ea;--skin-mid:#efe1c8;--skin-lo:#c3ae90;--drum-shadow:#000;}}
91
- .mascot{position:relative;}
92
- .mascot::before{content:"";position:absolute;inset:-8% -12%;z-index:-1;border-radius:50%;
93
- background:radial-gradient(ellipse at center,rgba(255,255,255,.95) 0%,rgba(255,255,255,.7) 45%,rgba(255,255,255,0) 72%);}
94
- :root[data-mode="dark"] .mascot::before{background:radial-gradient(ellipse at center,rgba(255,217,138,.22) 0%,rgba(251,146,60,.10) 46%,rgba(255,255,255,0) 72%);}
95
- @media (prefers-color-scheme:dark){:root:not([data-mode="light"]) .mascot::before{background:radial-gradient(ellipse at center,rgba(255,217,138,.22) 0%,rgba(251,146,60,.10) 46%,rgba(255,255,255,0) 72%);}}
96
- /* the beat — a 1.6s loop: BIG on 0.0 and 0.8 · SMALL on 0.4, 1.0 and 1.25 */
97
- .bongos .glow {transform-box:fill-box;transform-origin:center;animation:glowPulse 1.6s ease-out infinite;}
98
- .bongos .bshadow{transform-box:fill-box;transform-origin:center;animation:shadowPulse 1.6s ease-out infinite;}
99
- .bongos .drumBig{transform-origin:150px 270px;animation:bobBig 1.6s ease-out infinite;}
100
- .bongos .drumSml{transform-origin:296px 270px;animation:bobSml 1.6s ease-out infinite;}
101
- .bongos .headBig{transform-origin:150px 118px;animation:hitBig 1.6s ease-out infinite;}
102
- .bongos .headSml{transform-origin:296px 134px;animation:hitSml 1.6s ease-out infinite;}
103
- .bongos .ring {transform-box:fill-box;transform-origin:center;opacity:0;animation:burst 1.6s ease-out infinite;}
104
- @keyframes hitBig{
105
- 0%{transform:translateY(5px) scaleY(.88);} 9%{transform:translateY(0) scaleY(1);}
106
- 50%{transform:translateY(0) scaleY(1);} 51%{transform:translateY(5px) scaleY(.88);}
107
- 60%,100%{transform:translateY(0) scaleY(1);}
108
- }
109
- @keyframes hitSml{
110
- 0%,24%{transform:translateY(0) scaleY(1);} 25.5%{transform:translateY(3.5px) scaleY(.88);}
111
- 34%,62%{transform:translateY(0) scaleY(1);} 63.5%{transform:translateY(3.5px) scaleY(.88);}
112
- 71%,77%{transform:translateY(0) scaleY(1);} 78.5%{transform:translateY(2.5px) scaleY(.91);}
113
- 87%,100%{transform:translateY(0) scaleY(1);}
114
- }
115
- @keyframes bobBig{
116
- 0%{transform:translateY(2px);} 11%,50%{transform:translateY(0);}
117
- 52%{transform:translateY(2px);} 63%,100%{transform:translateY(0);}
118
- }
119
- @keyframes bobSml{
120
- 0%,24%{transform:translateY(0);} 26%{transform:translateY(1.5px);}
121
- 36%,64%{transform:translateY(0);} 65%{transform:translateY(1.5px);}
122
- 74%,79%{transform:translateY(0);} 80%{transform:translateY(1.2px);}
123
- 89%,100%{transform:translateY(0);}
124
- }
125
- @keyframes burst{0%{transform:scale(.3);opacity:.85;}26%{opacity:0;}100%{transform:scale(1.18);opacity:0;}}
126
- @keyframes glowPulse{
127
- 0%{opacity:.85;transform:scale(1.035);} 16%{opacity:.42;transform:scale(1);}
128
- 50%{opacity:.72;transform:scale(1.025);} 66%,100%{opacity:.42;transform:scale(1);}
129
- }
130
- @keyframes shadowPulse{
131
- 0%{transform:scaleX(1.06);opacity:1;} 11%,50%{transform:scaleX(1);opacity:.78;}
132
- 53%{transform:scaleX(1.05);opacity:.95;} 64%,100%{transform:scaleX(1);opacity:.78;}
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
- /* THE STATIC POSE (direction §7). The drums, heads, glow and shadow are stopped
135
- by cosmos.css's own `*,*::before,*::after{animation:none !important}` under
136
- reduce — an !important author declaration, so it beats these normal ones
137
- whatever the order or specificity. Deliberately NOT duplicated here: one
138
- global motion policy is the point of a shared layer. That cross-file
139
- dependency is load-bearing, so tests/landing_page.mjs asserts BOTH halves —
140
- that cosmos.css still carries the kill switch, and that nothing on this page
141
- marks an animation !important and so escapes it.
142
- `animation:none` renders the base style, not keyframe 0% — so the drums rest,
143
- they do not freeze mid-hit. Only the ripples need hiding: they are opacity:0
144
- at rest and would otherwise sit visible at full stroke. */
145
- @media (prefers-reduced-motion:reduce){.bongos .ring{display:none;}}
146
-
147
- /* ── S3 · the builder-credit strip (apex-only content; classes .verro*) ──
148
- Deliberately a different house from the rest of the page. Its palette is a
149
- LOCAL token set, not the Bongos one: cool greys that hold in both schemes,
150
- so the block reads as an inset plaque rather than another warm card. Only
151
- these five tokens are local — everything else still comes from cosmos.css. */
152
- .verro{
153
- --v-ground:#e9ecec; --v-surface:#f3f5f5; --v-ink:#4e5457;
154
- --v-ink-strong:#33383b; --v-hair:#aeb6b8;
155
- background:var(--v-ground);border-radius:20px;
156
- padding:clamp(18px,2.4vw,26px) clamp(20px,2.8vw,30px);
157
- box-shadow:var(--shadow-card);overflow:hidden;
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
- .verro{--v-ground:#15181a;--v-surface:#1b1f21;--v-ink:#aeb6b9;--v-ink-strong:#d6dcde;--v-hair:#3d4548;}
161
- }
162
- :root[data-theme="dark"] .verro{--v-ground:#15181a;--v-surface:#1b1f21;--v-ink:#aeb6b9;--v-ink-strong:#d6dcde;--v-hair:#3d4548;}
163
- :root[data-theme="light"] .verro{--v-ground:#e9ecec;--v-surface:#f3f5f5;--v-ink:#4e5457;--v-ink-strong:#33383b;--v-hair:#aeb6b8;}
164
- /* mark LEFT, text RIGHT — a fixed logo column, the copy takes the rest */
165
- .verroInner{position:relative;display:grid;grid-template-columns:124px 1fr;gap:clamp(18px,2.6vw,30px);align-items:center;}
166
- .verroMark{
167
- font-family:system-ui,-apple-system,"Segoe UI",Arial,sans-serif;font-weight:400;
168
- font-size:clamp(19px,2.6vw,26px);letter-spacing:.075em;line-height:1;
169
- color:var(--v-ink-strong);margin-bottom:11px;
170
- text-shadow:-1px 0 rgba(214,120,40,.26),1px 0 rgba(60,120,190,.26);
171
- }
172
- .verroRule{width:40px;height:1px;background:var(--v-hair);margin-bottom:12px;}
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
- /* ── S6b · the basics ── */
191
- .basics{--col:300px;align-items:stretch;}
192
- .basic{background:var(--card);border-radius:22px;box-shadow:var(--shadow-card);padding:clamp(20px,2.8vw,28px);}
193
- .basicWho{font-family:var(--font-mono);font-size:12px;font-weight:500;color:var(--accent);letter-spacing:1.3px;text-transform:uppercase;margin-bottom:11px;}
194
- .basic ul{list-style:none;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:13px;}
195
- .basic li{position:relative;padding-left:19px;font-size:var(--fs-body);font-weight:500;color:var(--ink-soft);line-height:1.55;}
196
- .basic li::before{content:"";position:absolute;left:0;top:.62em;width:7px;height:7px;border-radius:50%;background:var(--accent);}
197
- .basic li b{font-weight:700;color:var(--ink);}
198
- /* second level: the points that fold up under a tier. Hollow marker so the
199
- nesting reads as subordinate rather than as another top-level choice. */
200
- .basic li ul{margin:9px 0 0;gap:7px;}
201
- .basic li li{padding-left:16px;font-size:calc(var(--fs-body) - .5px);}
202
- .basic li li::before{top:.66em;width:5px;height:5px;background:transparent;border:1.5px solid var(--accent);}
203
-
204
- /* ── S4 · the lifecycle: cosmos.css's .strip, unmodified, doing marketing work.
205
- The marketing page and the working page share the component rather than
206
- merely resembling each other. ── */
207
- .railWrap{background:var(--card);border-radius:24px;box-shadow:var(--shadow-card);padding:var(--pad-lg) var(--pad-lg) clamp(14px,2vw,20px);}
208
- .railWrap .strip{margin-top:18px;}
209
-
210
- /* ── S5 · living proof: the sky appears exactly ONCE in the warm page, as a
211
- full-bleed band, so the catalog reads as a window into the hub rather
212
- than as wallpaper. ── */
213
- .band{position:relative;z-index:1;background:linear-gradient(180deg,#080a1c 0%,#0d0f28 52%,#0a0c20 100%);
214
- padding:clamp(34px,5vw,58px) 0;margin-bottom:var(--pad-xl);overflow:hidden;}
215
- .band::before{content:"";position:absolute;inset:0;pointer-events:none;
216
- background:radial-gradient(60% 90% at 22% 12%,rgba(43,36,86,.75) 0%,rgba(43,36,86,0) 62%),
217
- radial-gradient(50% 80% at 82% 88%,rgba(251,146,60,.16) 0%,rgba(251,146,60,0) 60%);}
218
- .band .wrap{position:relative;z-index:1;}
219
- .band h2.sec-h{color:#fff6e6;}
220
- .bandSub{font-size:var(--fs-body);font-weight:600;color:rgba(255,246,230,.62);margin-bottom:22px;}
221
- .bandHead{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;flex-wrap:wrap;}
222
- .bandHead .linklike{color:var(--gold);}
223
- .conGrid{--col:268px;}
224
- .conItem{display:flex;gap:13px;align-items:center;text-decoration:none;color:inherit;
225
- border:1.5px solid rgba(255,217,138,.22);border-radius:16px;padding:13px 15px;
226
- background:rgba(13,15,40,.55);backdrop-filter:blur(6px);transition:transform .14s ease,border-color .14s ease;}
227
- .conItem:hover{transform:translateY(-2px);border-color:rgba(255,217,138,.5);}
228
- .conItem .orb{flex:0 0 38px;width:38px;height:38px;cursor:inherit;}
229
- .conName{font-family:var(--font-display);font-weight:700;font-size:15.5px;letter-spacing:-.2px;line-height:1.2;color:#fff6e6;display:block;}
230
- .conTag{font-size:12.5px;font-weight:600;color:rgba(255,246,230,.58);margin-top:3px;line-height:1.35;display:block;}
231
- .conItem .chip{margin-top:8px;white-space:nowrap;}
232
-
233
- /* ── S7 ── */
234
- /* S7 closes on two bare buttons — the door cards are gone (task 1002934), and
235
- .door/.door p went with them rather than lingering as dead rules. */
236
- .closing .cta-row{justify-content:center;}
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
- .lead{grid-template-columns:1fr;text-align:center;}
240
- .lead .lede{margin-left:auto;margin-right:auto;}
241
- .cta-row{justify-content:center;}
242
- .mascot{max-width:330px;margin:0 auto;order:-1;}
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
- <!-- C3: the pack's theme.backdrop emits a body{background:…} that overrides
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
- <!-- ══ S2 · HERO ═══════════════════════════════════════════════════════════ -->
288
- <div class="wrap">
289
- <section class="lead">
290
- <div>
291
- <p class="eyebrow">type <b>bongos</b> in <span class="typed" id="typed">any AI</span><span class="caret" aria-hidden="true">▌</span></p>
292
- <h1 class="big" data-brand-tagline>Building Digitally, as simple as&nbsp;Bongos.</h1>
293
- <p class="lede">AI agents claim tasks and ship code. You direct them.</p>
294
- <!-- ONE button by owner direction (task 1002934). The former ghost CTA
295
- ("See what's building") is gone; the survivor keeps .btn.lg exactly as
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
- </section>
386
- </div>
810
+ <div class="scrim" aria-hidden="true"></div>
387
811
 
388
- <!--apex-only:start-->
389
- <!-- ══ S3 · VERRO LABS ═════════════════════════════════════════════════════
390
- Replaces the three mechanism cards (the ledger / the gates / the grade) by
391
- owner direction, task 1002934.
392
-
393
- APEX-ONLY, and it must stay that way: a child instance is NOT built by
394
- Verro Labs and must never say so. The block the beats used to occupy was
395
- deliberately portable; this one cannot be. Consequence a child instance
396
- now has nothing between the hero and the lifecycle — flagged to the owner
397
- as a follow-up decision, not silently absorbed.
398
-
399
- The design is drawn FROM the Verro Labs logo so it reads as a different
400
- house inside the warm Bongos page: cool grey monochrome, a wide-tracked
401
- uppercase wordmark, and a dialled-down chromatic fringe.
402
-
403
- NO HAIRLINE FRAME. An earlier pass added one, reading the thin rectangle
404
- in the logo lockup as part of the mark; the owner confirmed it was not
405
- meant to be there. Do not reintroduce it.
406
- ═══════════════════════════════════════════════════════════════════════ -->
407
- <div class="wrap">
408
- <section class="sec verro">
409
- <div class="verroInner">
410
- <!-- mark first in source order as well as visually, so it reads the same
411
- way to a screen reader and stacks logo-above-text on a narrow screen
412
- without needing an order override -->
413
- <div class="verroMedal">
414
- <!-- The Verro Labs medallion. Cropped from the supplied artwork to the
415
- circle only and masked to a transparent-cornered disc, so there is
416
- no white plate and no square frame to hide with CSS. The file is
417
- 384px against a 124px box — comfortably past 2x, so it stays sharp
418
- if the block is ever scaled back up.
419
-
420
- NOTE ON SHARPNESS: the source was a 217x210 screenshot and the disc
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
- <div>
429
- <div class="verroMark">VERRO LABS</div>
430
- <div class="verroRule"></div>
431
- <p>Built by Verro Labs, Cloud Bongos is our way of making AI co-building usable by entire
432
- project groups — whether that's a 5,000-person company, a 3,000-person department inside
433
- one, a college research project, or a solo dev.</p>
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
- <!-- ══ S4 · THE LIFECYCLE ══════════════════════════════════════════════════ -->
442
- <div class="wrap">
443
- <section class="sec railWrap">
444
- <h2 class="sec-h">A project as a codebase, regardless of function.</h2>
445
- <p class="sec-p">A laddered beat progresses all tasks.</p>
446
- <div class="strip isLive" role="img" aria-label="claim, build, grade, ship, credit">
447
- <ul class="stages">
448
- <li class="done"><span class="sdot"></span><span class="slbl">claim</span></li>
449
- <li class="done"><span class="sdot"></span><span class="slbl">build</span></li>
450
- <li class="done"><span class="sdot"></span><span class="slbl">grade</span></li>
451
- <li class="done"><span class="sdot"></span><span class="slbl">ship</span></li>
452
- <li class="now"><span class="sdot"></span><span class="slbl">credit</span></li>
453
- </ul>
454
- <div class="pnote">Locked to one agent · built in its own worktree · read by a panel · merged and
455
- verified live · paid only once it lands.</div>
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
- <!-- ══ S5 · LIVING PROOF ═══════════════════════════════════════════════════
462
- A catalog of OTHER instances is only true of the platform, so the whole
463
- band is apex-only. It is ALSO independently self-hiding: it ships `hidden`
464
- and is only revealed once the feed answers 200 with at least one project,
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
- <h2 class="sec-h">Built on Cloud Bongos</h2>
473
- <p class="bandSub">At present, Bongos projects are all held by Verro Labs or its pilot
474
- clients.</p>
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
- <div class="grid conGrid" id="proofGrid"></div>
479
- </div>
480
- </section>
975
+ </section>
481
976
  <!--apex-only:end-->
482
977
 
483
978
  <!--apex-only:start-->
484
- <!-- ══ S6b · THE BASICS ═════════════════════════════════════════════════════
485
- New dual block, owner direction (task 1002934). Inside the existing
486
- apex-only run: it describes Verro Labs' commercial offering, which is not
487
- true of a child instance.
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
- your team.</li>
501
- <!-- "Bongos Managed" and "Cloud Bongos Managed" were two separate tiers with
502
- near-identical names, which read as the same thing said twice. Consolidated
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 class="basic">
515
- <div class="basicWho">For builders</div>
516
- <h3 class="sec-h3">Ways to get paid</h3>
997
+ <div>
998
+ <p class="label">For builders</p>
999
+ <h3>Ways to get paid</h3>
517
1000
  <ul>
518
- <li><b>Build equity</b> in multiple projects.</li>
519
- <li><b>Work as a contractor</b> for companies big, medium and small — and get paid
520
- instantly.</li>
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
- <!-- ══ S7 · THE CLOSING PAIR ═══════════════════════════════════════════════
530
- Was two "door" cards (Build a project / Join as a builder). Reduced to two
531
- bare buttons by owner direction, task 1002934 — the hero already made the
532
- argument, so the page closes on the choice rather than re-pitching it.
533
- ═══════════════════════════════════════════════════════════════════════ -->
534
- <div class="wrap">
535
- <section class="sec closing">
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
- </div>
1019
+ </main>
542
1020
 
543
- <!-- The footer line SPLITS. Its first half is a claim about the platform itself
544
- and is untrue of a project built on it, so only the attribution half travels.
545
- (Today's page ships the whole line to every instance — this is that fix.) -->
546
- <div class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud&nbsp;Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a></nav></div>
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&nbsp;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
- /* ── the light/dark switch. The pre-paint script above already applied the
551
- stored choice; this only paints the icon and handles the click. ── */
552
- var root = document.documentElement, btn = document.getElementById('mode');
553
- function isDark(){
554
- return root.getAttribute('data-mode') === 'dark' ||
555
- (!root.getAttribute('data-mode') && matchMedia('(prefers-color-scheme:dark)').matches);
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
- function paint(){ btn.textContent = isDark() ? '☀' : '☾'; }
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
- paint();
565
-
566
- /* ── brand-pack fill. The server injects window.__BRANDING__ synchronously
567
- ahead of this script; the baked markup is the no-JS / standalone fallback.
568
- Origins are NOT filled here — clientBranding() does not project domains, so
569
- every href that points at another host is server-stamped instead (C6). ── */
570
- var b = (typeof window !== 'undefined' && window.__BRANDING__) || null;
571
- if (b) {
572
- var copy = b.copy || {};
573
- if (copy.tagline) {
574
- document.querySelectorAll('[data-brand-tagline]').forEach(function (n) { n.textContent = copy.tagline; });
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 chip. Signed out (401) leaves it empty — the header's own
605
- doors are the signed-out affordance. Built with createElement/textContent,
606
- never innerHTML: the same rule the status UI keeps. Signed in it carries
607
- the one session control the page owes its visitor: Sign out (task 1002952). ── */
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 hue(s) {
680
- var h = 0;
681
- for (var i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;
682
- return h;
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
- var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
686
-
687
- /* ── the self-typing eyebrow. DECORATION over real text: the markup already
688
- reads "type bongos in any AI" and stays correct with scripting off or
689
- motion reduced.
690
-
691
- `bongos` is the CONSTANT and the ASSISTANT is what varies — that is the
692
- whole claim: one word, and it works in any of them. Typing the first slot
693
- instead produced "type claude in any AI", which nobody does and which
694
- inverted the point of the line. ── */
695
- if (!reduce) {
696
- var el = document.getElementById('typed'), words = ['Claude','ChatGPT','Gemini','any AI'], w = 0;
697
- (function cycle(){
698
- var word = words[w++ % words.length], i = 0;
699
- el.textContent = '';
700
- (function type(){
701
- if (i <= word.length) { el.textContent = word.slice(0, i++); setTimeout(type, 72); }
702
- else setTimeout(function erase(){
703
- if (el.textContent.length) { el.textContent = el.textContent.slice(0, -1); setTimeout(erase, 38); }
704
- else cycle();
705
- }, word === 'any AI' ? 2100 : 900);
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>