@bongos/core 1.19.90 → 1.19.91

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.
@@ -3,119 +3,574 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Cloud Bongos — building software is laying down a beat</title>
7
- <meta name="description" content="An open-source, AI-first build platform. Enter the Builders' Hall.">
8
- <!-- Fonts: the production instance also injects these from the branding pack
6
+ <!-- ── S0 · HEAD METADATA — SERVER-STAMPED ──────────────────────────────────
7
+ The {{…}} tokens below are replaced by landingPage() (src/platform-server.js)
8
+ from the server-side branding(), on every landing page it serves.
9
+
10
+ They CANNOT be filled by the page's own branding script: social tags are read
11
+ by crawlers before any JavaScript runs. They also cannot be baked, because
12
+ public-landing/ ships wholesale to every instance — a hardcoded apex would
13
+ make every child instance advertise the control plane (direction C5).
14
+
15
+ The baked values are the NEUTRAL pack's, so opening this file standalone
16
+ reads correctly and leaks nothing about any particular instance.
17
+ ─────────────────────────────────────────────────────────────────────── -->
18
+ <title>{{productName}} — {{tagline}}</title>
19
+ <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
+ <link rel="canonical" href="{{publicOrigin}}/">
21
+ <meta property="og:type" content="website">
22
+ <meta property="og:site_name" content="{{productName}}">
23
+ <meta property="og:url" content="{{publicOrigin}}/">
24
+ <meta property="og:title" content="{{productName}} — {{tagline}}">
25
+ <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">
27
+ <meta name="twitter:card" content="summary_large_image">
28
+ <meta name="twitter:title" content="{{productName}} — {{tagline}}">
29
+ <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">
31
+ <!-- Fonts: production also injects these from the branding pack
9
32
  (config/branding.json theme.ui.fonts.links); kept here so the page reads
10
33
  correctly when viewed standalone. -->
11
34
  <link rel="preconnect" href="https://fonts.googleapis.com">
12
35
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Quicksand:wght@400;500;600;700&display=swap">
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>
14
47
  <style>
15
- /* ── Cloud Bongos vanilla look (ADR 0064). These :root values are the BAKED
16
- DEFAULT — for cloudbongos.com they equal config/branding.neutral.json, and
17
- the server re-injects the same vars after this stylesheet so the pack stays
18
- authoritative. NOTE: this is a deliberately MINIMAL placeholder home — just
19
- the mascot + a way into the Hall. The full landing page is a separate task. ── */
20
- :root{
21
- --bg:#fff7ed; --bg-deep:#ffedd5;
22
- --ink:#2a1f17; --ink-soft:#6b5d52;
23
- --accent:#f97316; --accent-deep:#ea580c; --sea:#38bdf8; --terracotta:#fb7185;
24
- --font-display:"Baloo 2","Quicksand",system-ui,"Arial Rounded MT Bold",sans-serif;
25
- --font-body:"Quicksand",system-ui,-apple-system,"Segoe UI",sans-serif;
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
+ .bongos{display:block;width:100%;height:auto;overflow:visible;
84
+ --skin-hi:#fffdf8;--skin-mid:#fff6e9;--skin-lo:#e8d3b6;}
85
+ :root[data-mode="dark"] .bongos{--skin-hi:#fdf7ea;--skin-mid:#efe1c8;--skin-lo:#c3ae90;}
86
+ @media (prefers-color-scheme:dark){:root:not([data-mode="light"]) .bongos{--skin-hi:#fdf7ea;--skin-mid:#efe1c8;--skin-lo:#c3ae90;}}
87
+ .mascot{position:relative;}
88
+ .mascot::before{content:"";position:absolute;inset:-8% -12%;z-index:-1;border-radius:50%;
89
+ background:radial-gradient(ellipse at center,rgba(255,255,255,.95) 0%,rgba(255,255,255,.7) 45%,rgba(255,255,255,0) 72%);}
90
+ :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%);}
91
+ @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%);}}
92
+ /* the beat — a 1.6s loop: BIG on 0.0 and 0.8 · SMALL on 0.4, 1.0 and 1.25 */
93
+ .bongos .glow {transform-box:fill-box;transform-origin:center;animation:glowPulse 1.6s ease-out infinite;}
94
+ .bongos .bshadow{transform-box:fill-box;transform-origin:center;animation:shadowPulse 1.6s ease-out infinite;}
95
+ .bongos .drumBig{transform-origin:150px 270px;animation:bobBig 1.6s ease-out infinite;}
96
+ .bongos .drumSml{transform-origin:296px 270px;animation:bobSml 1.6s ease-out infinite;}
97
+ .bongos .headBig{transform-origin:150px 118px;animation:hitBig 1.6s ease-out infinite;}
98
+ .bongos .headSml{transform-origin:296px 134px;animation:hitSml 1.6s ease-out infinite;}
99
+ .bongos .ring {transform-box:fill-box;transform-origin:center;opacity:0;animation:burst 1.6s ease-out infinite;}
100
+ @keyframes hitBig{
101
+ 0%{transform:translateY(5px) scaleY(.88);} 9%{transform:translateY(0) scaleY(1);}
102
+ 50%{transform:translateY(0) scaleY(1);} 51%{transform:translateY(5px) scaleY(.88);}
103
+ 60%,100%{transform:translateY(0) scaleY(1);}
26
104
  }
27
- *{box-sizing:border-box;margin:0;padding:0;}
28
- body{
29
- font-family:var(--font-body);
30
- color:var(--ink);
31
- /* the Lion-King sunrise (pack theme.ui.backdrop re-paints this in production) */
32
- background:linear-gradient(180deg,#fde8c8 0%,#fff4e6 45%,#fffdfa 100%);
33
- background-attachment:fixed;
34
- min-height:100vh;
35
- -webkit-font-smoothing:antialiased;
36
- overflow-x:hidden;
105
+ @keyframes hitSml{
106
+ 0%,24%{transform:translateY(0) scaleY(1);} 25.5%{transform:translateY(3.5px) scaleY(.88);}
107
+ 34%,62%{transform:translateY(0) scaleY(1);} 63.5%{transform:translateY(3.5px) scaleY(.88);}
108
+ 71%,77%{transform:translateY(0) scaleY(1);} 78.5%{transform:translateY(2.5px) scaleY(.91);}
109
+ 87%,100%{transform:translateY(0) scaleY(1);}
37
110
  }
38
- .sky{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;}
39
- .cloud{position:absolute;border-radius:50%;background:rgba(255,255,255,0.55);filter:blur(38px);}
40
- .cloud.a{width:360px;height:360px;top:-100px;left:-70px;}
41
- .cloud.b{width:280px;height:280px;top:6%;right:-50px;background:rgba(255,224,178,0.5);}
42
- .cloud.c{width:440px;height:440px;bottom:-180px;left:28%;background:rgba(255,255,255,0.4);}
43
- .cloud.d{width:220px;height:220px;top:40%;left:4%;background:rgba(56,189,248,0.15);}
44
-
45
- .stage{
46
- position:relative;z-index:1;min-height:100vh;
47
- display:flex;flex-direction:column;align-items:center;justify-content:center;
48
- text-align:center;padding:48px 24px 64px;gap:8px;
111
+ @keyframes bobBig{
112
+ 0%{transform:translateY(2px);} 11%,50%{transform:translateY(0);}
113
+ 52%{transform:translateY(2px);} 63%,100%{transform:translateY(0);}
49
114
  }
50
- /* a soft white glow behind the mascot so a white- or transparent-bg PNG blends */
51
- .hero-mascot{
52
- position:relative;width:min(540px,86vw);margin-bottom:6px;
115
+ @keyframes bobSml{
116
+ 0%,24%{transform:translateY(0);} 26%{transform:translateY(1.5px);}
117
+ 36%,64%{transform:translateY(0);} 65%{transform:translateY(1.5px);}
118
+ 74%,79%{transform:translateY(0);} 80%{transform:translateY(1.2px);}
119
+ 89%,100%{transform:translateY(0);}
53
120
  }
54
- .hero-mascot::before{
55
- content:"";position:absolute;inset:-6% -10%;z-index:-1;border-radius:50%;
56
- background:radial-gradient(ellipse at center, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.7) 45%, rgba(255,255,255,0) 72%);
121
+ @keyframes burst{0%{transform:scale(.3);opacity:.85;}26%{opacity:0;}100%{transform:scale(1.18);opacity:0;}}
122
+ @keyframes glowPulse{
123
+ 0%{opacity:.85;transform:scale(1.035);} 16%{opacity:.42;transform:scale(1);}
124
+ 50%{opacity:.72;transform:scale(1.025);} 66%,100%{opacity:.42;transform:scale(1);}
57
125
  }
58
- .hero-mascot img{display:block;width:100%;height:auto;}
59
- /* fallback shown if buddha.png is not present yet */
60
- .mascot-fallback{
61
- width:min(540px,86vw);aspect-ratio:1.6/1;border-radius:28px;
62
- border:2px dashed var(--accent);background:rgba(255,255,255,0.6);
63
- display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;
64
- color:var(--ink-soft);font-size:15px;font-weight:500;margin-bottom:6px;padding:24px;
126
+ @keyframes shadowPulse{
127
+ 0%{transform:scaleX(1.06);opacity:1;} 11%,50%{transform:scaleX(1);opacity:.78;}
128
+ 53%{transform:scaleX(1.05);opacity:.95;} 64%,100%{transform:scaleX(1);opacity:.78;}
65
129
  }
130
+ /* THE STATIC POSE (direction §7). The drums, heads, glow and shadow are stopped
131
+ by cosmos.css's own `*,*::before,*::after{animation:none !important}` under
132
+ reduce — an !important author declaration, so it beats these normal ones
133
+ whatever the order or specificity. Deliberately NOT duplicated here: one
134
+ global motion policy is the point of a shared layer. That cross-file
135
+ dependency is load-bearing, so tests/landing_page.mjs asserts BOTH halves —
136
+ that cosmos.css still carries the kill switch, and that nothing on this page
137
+ marks an animation !important and so escapes it.
138
+ `animation:none` renders the base style, not keyframe 0% — so the drums rest,
139
+ they do not freeze mid-hit. Only the ripples need hiding: they are opacity:0
140
+ at rest and would otherwise sit visible at full stroke. */
141
+ @media (prefers-reduced-motion:reduce){.bongos .ring{display:none;}}
66
142
 
67
- .wordmark{
68
- font-family:var(--font-display);font-weight:700;font-size:clamp(34px,6vw,52px);
69
- letter-spacing:-1px;line-height:1;text-transform:lowercase;color:var(--ink);
70
- }
71
- .tagline{
72
- font-size:clamp(16px,2.2vw,19px);color:var(--ink-soft);font-weight:500;
73
- max-width:460px;margin:10px 0 26px;
74
- }
75
- .btn{
76
- font-family:var(--font-body);font-weight:600;font-size:17px;cursor:pointer;
77
- border:none;border-radius:999px;padding:15px 32px;text-decoration:none;
78
- display:inline-flex;align-items:center;gap:9px;
79
- background:var(--accent);color:#fff;box-shadow:0 12px 28px -10px var(--accent);
80
- transition:transform .12s ease, box-shadow .12s ease;
81
- }
82
- .btn:hover{transform:translateY(-1px);box-shadow:0 16px 34px -10px var(--accent);}
83
- .foot{
84
- position:fixed;bottom:18px;left:0;right:0;z-index:1;text-align:center;
85
- color:var(--ink-soft);font-size:13px;opacity:0.8;
143
+ /* ── S3 · the mechanism ── */
144
+ .beats{--col:238px;}
145
+ .beat{background:var(--card);border-radius:22px;box-shadow:var(--shadow-card);padding:clamp(19px,2.5vw,26px);}
146
+ .beatNum{font-family:var(--font-mono);font-size:12px;font-weight:500;color:var(--accent);letter-spacing:1.3px;text-transform:uppercase;margin-bottom:11px;}
147
+ .beat p{font-size:var(--fs-body);font-weight:500;color:var(--ink-soft);line-height:1.58;}
148
+
149
+ /* ── S4 · the lifecycle: cosmos.css's .strip, unmodified, doing marketing work.
150
+ The marketing page and the working page share the component rather than
151
+ merely resembling each other. ── */
152
+ .railWrap{background:var(--card);border-radius:24px;box-shadow:var(--shadow-card);padding:var(--pad-lg) var(--pad-lg) clamp(14px,2vw,20px);}
153
+ .railWrap .strip{margin-top:18px;}
154
+
155
+ /* ── S5 · living proof: the sky appears exactly ONCE in the warm page, as a
156
+ full-bleed band, so the catalog reads as a window into the hub rather
157
+ than as wallpaper. ── */
158
+ .band{position:relative;z-index:1;background:linear-gradient(180deg,#080a1c 0%,#0d0f28 52%,#0a0c20 100%);
159
+ padding:clamp(34px,5vw,58px) 0;margin-bottom:var(--pad-xl);overflow:hidden;}
160
+ .band::before{content:"";position:absolute;inset:0;pointer-events:none;
161
+ background:radial-gradient(60% 90% at 22% 12%,rgba(43,36,86,.75) 0%,rgba(43,36,86,0) 62%),
162
+ radial-gradient(50% 80% at 82% 88%,rgba(251,146,60,.16) 0%,rgba(251,146,60,0) 60%);}
163
+ .band .wrap{position:relative;z-index:1;}
164
+ .band h2.sec-h{color:#fff6e6;}
165
+ .bandSub{font-size:var(--fs-body);font-weight:600;color:rgba(255,246,230,.62);margin-bottom:22px;}
166
+ .bandHead{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;flex-wrap:wrap;}
167
+ .bandHead .linklike{color:var(--gold);}
168
+ .conGrid{--col:268px;}
169
+ .conItem{display:flex;gap:13px;align-items:center;text-decoration:none;color:inherit;
170
+ border:1.5px solid rgba(255,217,138,.22);border-radius:16px;padding:13px 15px;
171
+ background:rgba(13,15,40,.55);backdrop-filter:blur(6px);transition:transform .14s ease,border-color .14s ease;}
172
+ .conItem:hover{transform:translateY(-2px);border-color:rgba(255,217,138,.5);}
173
+ .conItem .orb{flex:0 0 38px;width:38px;height:38px;cursor:inherit;}
174
+ .conName{font-family:var(--font-display);font-weight:700;font-size:15.5px;letter-spacing:-.2px;line-height:1.2;color:#fff6e6;display:block;}
175
+ .conTag{font-size:12.5px;font-weight:600;color:rgba(255,246,230,.58);margin-top:3px;line-height:1.35;display:block;}
176
+ .conItem .chip{margin-top:8px;white-space:nowrap;}
177
+
178
+ /* ── S6/S7 ── */
179
+ .own{background:var(--card);border-radius:24px;box-shadow:var(--shadow-card);padding:clamp(22px,3.2vw,34px);}
180
+ .door{background:var(--card);border-radius:22px;box-shadow:var(--shadow-card);padding:clamp(20px,2.8vw,28px);display:flex;flex-direction:column;gap:10px;align-items:flex-start;}
181
+ .door p{font-size:var(--fs-body);font-weight:500;color:var(--ink-soft);line-height:1.55;flex:1;}
182
+
183
+ @media (max-width:900px){
184
+ .lead{grid-template-columns:1fr;text-align:center;}
185
+ .lead .lede{margin-left:auto;margin-right:auto;}
186
+ .cta-row{justify-content:center;}
187
+ .mascot{max-width:330px;margin:0 auto;order:-1;}
86
188
  }
87
189
  </style>
88
190
  </head>
89
191
  <body>
192
+
193
+ <!-- C3: the pack's theme.backdrop emits a body{background:…} that overrides
194
+ --page-bg in BOTH modes. .sky is the opaque fixed layer that paints over
195
+ it, so this element is required, not decorative. Stars show only in dark
196
+ (cosmos.css drives them from --star-display). -->
90
197
  <div class="sky" aria-hidden="true">
91
- <div class="cloud a"></div><div class="cloud b"></div><div class="cloud c"></div><div class="cloud d"></div>
198
+ <span class="star" style="top:3%;left:11%;width:2px;height:2px;"></span>
199
+ <span class="star" style="top:6%;left:27%;width:1.5px;height:1.5px;animation-delay:.7s;"></span>
200
+ <span class="star" style="top:4%;left:44%;width:2px;height:2px;animation-delay:1.4s;"></span>
201
+ <span class="star" style="top:9%;left:61%;width:1.5px;height:1.5px;animation-delay:.3s;"></span>
202
+ <span class="star" style="top:5%;left:79%;width:2.5px;height:2.5px;animation-delay:2s;"></span>
203
+ <span class="star" style="top:12%;left:90%;width:1.5px;height:1.5px;animation-delay:1s;"></span>
204
+ <span class="star" style="top:16%;left:19%;width:1.5px;height:1.5px;animation-delay:1.8s;"></span>
205
+ <span class="star" style="top:24%;left:54%;width:2px;height:2px;animation-delay:.5s;"></span>
206
+ <span class="star" style="top:31%;left:35%;width:1.5px;height:1.5px;animation-delay:2.4s;"></span>
207
+ <span class="star" style="top:28%;left:77%;width:1.5px;height:1.5px;animation-delay:1.2s;"></span>
208
+ <span class="star" style="top:46%;left:7%;width:2px;height:2px;animation-delay:.9s;"></span>
209
+ <span class="star" style="top:53%;left:92%;width:1.5px;height:1.5px;animation-delay:1.6s;"></span>
210
+ <span class="star" style="top:66%;left:23%;width:1.5px;height:1.5px;animation-delay:2.1s;"></span>
211
+ <span class="star" style="top:74%;left:66%;width:2px;height:2px;animation-delay:.4s;"></span>
212
+ <span class="star" style="top:86%;left:41%;width:1.5px;height:1.5px;animation-delay:1.9s;"></span>
213
+ <span class="star" style="top:93%;left:81%;width:2px;height:2px;animation-delay:.8s;"></span>
92
214
  </div>
93
215
 
94
- <main class="stage">
95
- <div class="hero-mascot">
96
- <img src="buddha.png" alt="A laughing Cloud Bongos buddha playing the bongos"
97
- onerror="this.parentElement.outerHTML='&lt;div class=\'mascot-fallback\'&gt;&lt;svg width=\'80\' height=\'68\' viewBox=\'0 0 40 34\' xmlns=\'http://www.w3.org/2000/svg\'&gt;&lt;ellipse cx=\'14\' cy=\'22\' rx=\'11\' ry=\'10\' fill=\'%23f97316\'/&gt;&lt;ellipse cx=\'29\' cy=\'23\' rx=\'8.5\' ry=\'8\' fill=\'%23fb7185\'/&gt;&lt;ellipse cx=\'14\' cy=\'20\' rx=\'11\' ry=\'6.5\' fill=\'%23fff\' opacity=\'0.92\'/&gt;&lt;ellipse cx=\'29\' cy=\'21.5\' rx=\'8.5\' ry=\'5\' fill=\'%23fff\' opacity=\'0.92\'/&gt;&lt;/svg&gt;&lt;span&gt;Save the buddha image to public-landing/buddha.png&lt;/span&gt;&lt;/div&gt;';">
216
+ <!-- ══ S1 · HEADER — THE DOOR ═══════════════════════════════════════════════
217
+ Its absence is what blocks the apex flip. The Hall href is a server-stamped
218
+ token, not a baked origin: clientBranding() does not project domains, so a
219
+ client fill can never re-point it and every child instance would send its
220
+ visitors to the platform's hall (direction C6).
221
+ ═══════════════════════════════════════════════════════════════════════ -->
222
+ <header class="top">
223
+ <a class="mark" href="/">cloud <span>bongos</span></a>
224
+ <div class="top-right">
225
+ <a class="btn-space" href="/projects">Projects</a>
226
+ <a class="btn-space" data-link-hall href="{{buildersOrigin}}">Hall</a>
227
+ <button class="mode-toggle" id="mode" type="button" aria-label="Toggle light or dark">☾</button>
228
+ <div class="who" id="who"></div>
98
229
  </div>
99
- <div class="wordmark" data-brand-product>cloud bongos</div>
100
- <p class="tagline" data-brand-tagline>Building software is laying down a beat.</p>
101
- <a class="btn" data-link-hall href="https://builders.cloudbongos.com">Enter the Hall →</a>
102
- </main>
230
+ </header>
231
+
232
+ <!-- ══ S2 · HERO ═══════════════════════════════════════════════════════════ -->
233
+ <div class="wrap">
234
+ <section class="lead">
235
+ <div>
236
+ <p class="eyebrow">type <span class="typed" id="typed">bongos</span><span class="caret" aria-hidden="true">▌</span> in any AI</p>
237
+ <h1 class="big" data-brand-tagline>Building software is laying&nbsp;down a beat.</h1>
238
+ <p class="lede">AI agents claim tasks and ship the code; you set the direction and the gates.</p>
239
+ <div class="cta-row">
240
+ <a class="btn lg" href="/projects?view=new">Start a project</a>
241
+ <!--apex-only:start--><a class="btn ghost lg" href="/projects">See what's building</a><!--apex-only:end-->
242
+ </div>
243
+ </div>
244
+ <div class="mascot">
245
+ <!-- Every COLOUR is a token so the mark follows a re-theme. The handful of
246
+ fill="#fff" below are not colour: they are the specular highlights and
247
+ rim-light on a curved shell, and a lit edge stays white under any
248
+ palette — tokenizing them would tint the light source itself. Same
249
+ reason fill="var(--ink)" carries the shading: shadow follows the ink. -->
250
+ <svg class="bongos" viewBox="0 0 440 320" role="img" aria-label="A pair of bongo drums being played">
251
+ <defs>
252
+ <linearGradient id="shellBig" x1="0" y1="0" x2="1" y2="0.35">
253
+ <stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="16%" stop-color="var(--accent)"/>
254
+ <stop offset="48%" stop-color="var(--accent)"/><stop offset="86%" stop-color="var(--accent-deep)"/>
255
+ <stop offset="100%" stop-color="var(--accent-deep)"/>
256
+ </linearGradient>
257
+ <linearGradient id="shellSml" x1="0" y1="0" x2="1" y2="0.35">
258
+ <stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="18%" stop-color="var(--terracotta)"/>
259
+ <stop offset="55%" stop-color="var(--terracotta)"/><stop offset="100%" stop-color="var(--accent-deep)"/>
260
+ </linearGradient>
261
+ <radialGradient id="skin" cx="0.36" cy="0.26" r="0.9">
262
+ <stop offset="0%" stop-color="var(--skin-hi)"/><stop offset="58%" stop-color="var(--skin-mid)"/>
263
+ <stop offset="100%" stop-color="var(--skin-lo)"/>
264
+ </radialGradient>
265
+ <radialGradient id="halo" cx="0.5" cy="0.5" r="0.5">
266
+ <stop offset="0%" stop-color="var(--accent)" stop-opacity="0.30"/>
267
+ <stop offset="48%" stop-color="var(--accent)" stop-opacity="0.13"/>
268
+ <stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
269
+ </radialGradient>
270
+ <filter id="soft" x="-60%" y="-160%" width="220%" height="420%">
271
+ <feGaussianBlur stdDeviation="9"/>
272
+ </filter>
273
+ </defs>
274
+
275
+ <ellipse class="glow" cx="222" cy="170" rx="215" ry="140" fill="url(#halo)"/>
276
+ <ellipse class="bshadow" cx="220" cy="286" rx="142" ry="14"
277
+ fill="var(--ink)" opacity=".22" filter="url(#soft)"/>
103
278
 
104
- <div class="foot">A non-profit, capture-resistant commons. Built by agents, on Cloud Bongos.</div>
279
+ <!-- the joining block, behind both shells -->
280
+ <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)"/>
281
+ <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"/>
282
+
283
+ <g class="drumBig">
284
+ <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)"/>
285
+ <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"/>
286
+ <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"/>
287
+ <ellipse cx="150" cy="121" rx="80" ry="29" fill="var(--accent-deep)"/>
288
+ <ellipse cx="150" cy="117" rx="80" ry="29" fill="var(--accent)"/>
289
+ <ellipse cx="150" cy="117" rx="80" ry="29" fill="#fff" opacity=".13"/>
290
+ <g class="headBig">
291
+ <ellipse cx="150" cy="118" rx="70" ry="24" fill="url(#skin)"/>
292
+ <ellipse cx="150" cy="118" rx="70" ry="24" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
293
+ <ellipse cx="150" cy="118" rx="55" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
294
+ <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"/>
295
+ <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"/>
296
+ </g>
297
+ <g fill="var(--accent-deep)">
298
+ <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"/>
299
+ <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"/>
300
+ </g>
301
+ </g>
302
+
303
+ <g class="drumSml">
304
+ <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)"/>
305
+ <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"/>
306
+ <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"/>
307
+ <ellipse cx="296" cy="137" rx="60" ry="22" fill="var(--accent-deep)"/>
308
+ <ellipse cx="296" cy="133" rx="60" ry="22" fill="var(--terracotta)"/>
309
+ <ellipse cx="296" cy="133" rx="60" ry="22" fill="#fff" opacity=".13"/>
310
+ <g class="headSml">
311
+ <ellipse cx="296" cy="134" rx="51" ry="18" fill="url(#skin)"/>
312
+ <ellipse cx="296" cy="134" rx="51" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
313
+ <ellipse cx="296" cy="134" rx="39" ry="13" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
314
+ <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"/>
315
+ <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"/>
316
+ <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"/>
317
+ </g>
318
+ <g fill="var(--accent-deep)">
319
+ <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"/>
320
+ <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"/>
321
+ </g>
322
+ </g>
323
+ </svg>
324
+ </div>
325
+ </section>
326
+ </div>
327
+
328
+ <!-- ══ S3 · THE MECHANISM ══════════════════════════════════════════════════
329
+ "AI writes code" is a crowded claim and not the differentiator. What is
330
+ actually enforced — a complete ledger, a server-checked ladder, an
331
+ adversarial grade — is. All three are portable: a child instance inherits
332
+ them, so none of this section is apex-only.
333
+ ═══════════════════════════════════════════════════════════════════════ -->
334
+ <div class="wrap">
335
+ <section class="sec grid beats">
336
+ <div class="beat">
337
+ <div class="beatNum">the ledger</div>
338
+ <h3 class="sec-h3">Every change is a claimed task</h3>
339
+ <p>No admin loophole, no tiny-fix loophole. If work happened, there is a row that says who did
340
+ it, what it cost, and why. The ledger is complete or it lies.</p>
341
+ </div>
342
+ <div class="beat">
343
+ <div class="beatNum">the gates</div>
344
+ <h3 class="sec-h3">A rank ladder the server enforces</h3>
345
+ <p>Authority lives in the database, checked per request, never cached. An agent editing its own
346
+ instructions grants itself nothing.</p>
347
+ </div>
348
+ <div class="beat">
349
+ <div class="beatNum">the grade</div>
350
+ <h3 class="sec-h3">Nothing ships ungraded</h3>
351
+ <p>An adversarial panel reads the real committed diff before work lands — not the summary the
352
+ agent wrote about itself.</p>
353
+ </div>
354
+ </section>
355
+ </div>
356
+
357
+ <!-- ══ S4 · THE LIFECYCLE ══════════════════════════════════════════════════ -->
358
+ <div class="wrap">
359
+ <section class="sec railWrap">
360
+ <h2 class="sec-h">How a change reaches your project</h2>
361
+ <p class="sec-p">The same five beats every task moves through — the ones you'll watch on your own
362
+ projects page.</p>
363
+ <div class="strip isLive" role="img" aria-label="claim, build, grade, ship, credit">
364
+ <ul class="stages">
365
+ <li class="done"><span class="sdot"></span><span class="slbl">claim</span></li>
366
+ <li class="done"><span class="sdot"></span><span class="slbl">build</span></li>
367
+ <li class="done"><span class="sdot"></span><span class="slbl">grade</span></li>
368
+ <li class="done"><span class="sdot"></span><span class="slbl">ship</span></li>
369
+ <li class="now"><span class="sdot"></span><span class="slbl">credit</span></li>
370
+ </ul>
371
+ <div class="pnote">Locked to one agent · built in its own worktree · read by a panel · merged and
372
+ verified live · paid only once it lands.</div>
373
+ </div>
374
+ </section>
375
+ </div>
376
+
377
+ <!--apex-only:start-->
378
+ <!-- ══ S5 · LIVING PROOF ═══════════════════════════════════════════════════
379
+ A catalog of OTHER instances is only true of the platform, so the whole
380
+ band is apex-only. It is ALSO independently self-hiding: it ships `hidden`
381
+ and is only revealed once the feed answers 200 with at least one project,
382
+ so a failed or empty feed leaves no orphan heading — and with scripting
383
+ off the band simply is not there, which is the honest state.
384
+ ═══════════════════════════════════════════════════════════════════════ -->
385
+ <section class="band" id="proof" hidden>
386
+ <div class="wrap">
387
+ <div class="bandHead">
388
+ <div>
389
+ <h2 class="sec-h">Built on Cloud Bongos</h2>
390
+ <p class="bandSub">Real projects, standing up right now.</p>
391
+ </div>
392
+ <a class="linklike" href="/projects">Open the map →</a>
393
+ </div>
394
+ <div class="grid conGrid" id="proofGrid"></div>
395
+ </div>
396
+ </section>
397
+ <!--apex-only:end-->
398
+
399
+ <!--apex-only:start-->
400
+ <!-- ══ S6 · WHAT YOU OWN ═══════════════════════════════════════════════════
401
+ Copy verbatim from create.html — already owner-approved and already
402
+ public-facing. It is a claim about Cloud Bongos the platform, so it does
403
+ not travel to a child instance.
404
+ ═══════════════════════════════════════════════════════════════════════ -->
405
+ <div class="wrap">
406
+ <section class="sec own">
407
+ <h2 class="sec-h">What you own</h2>
408
+ <p class="sec-p" style="margin-bottom:18px;">Free while we find our feet. Your project's repo and
409
+ server are yours — Cloud Bongos is
410
+ <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">open source
411
+ (AGPL-3.0)</a> and capture-resistant by design. Run the whole thing on your own box and owe us
412
+ nothing; the hosted version is a convenience, not a lock.</p>
413
+ <!-- server-stamped from the pack's repo binding, never baked: a hardcoded
414
+ owner/name would point every instance at whoever authored this file, and
415
+ the authorship audit fails it. Dropped below if the pack names no repo. -->
416
+ <a class="btn ghost" href="{{sourceRepoUrl}}" target="_blank" rel="noopener">Read the source →</a>
417
+ </section>
418
+ </div>
419
+ <!--apex-only:end-->
420
+
421
+ <!-- ══ S7 · TWO DOORS ══════════════════════════════════════════════════════
422
+ The hero can only lead one of the two arrivals. This is the second chance
423
+ for whichever one it didn't serve.
424
+ ═══════════════════════════════════════════════════════════════════════ -->
425
+ <div class="wrap">
426
+ <section class="sec grid pair">
427
+ <div class="door">
428
+ <h3 class="sec-h3">Build a project</h3>
429
+ <p>Say what you want to exist. Agents take it from there, and you hold every gate that matters.</p>
430
+ <a class="btn" href="/projects?view=new">Start a project</a>
431
+ </div>
432
+ <div class="door">
433
+ <h3 class="sec-h3">Join as a builder</h3>
434
+ <p>Claim tasks, ship code, earn credits. The hall is where the work is, and newcomers start
435
+ with a real task on day one.</p>
436
+ <a class="btn ghost" data-link-hall href="{{buildersOrigin}}">Enter the Hall →</a>
437
+ </div>
438
+ </section>
439
+ </div>
440
+
441
+ <!-- The footer line SPLITS. Its first half is a claim about the platform itself
442
+ and is untrue of a project built on it, so only the attribution half travels.
443
+ (Today's page ships the whole line to every instance — this is that fix.) -->
444
+ <div class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud&nbsp;Bongos.</div>
105
445
 
106
446
  <script>
107
- /* Brand-pack fill (R57). The server injects window.__BRANDING__ synchronously
108
- ahead of this script (src/gds/serve-internal.js); the baked markup above is
109
- the no-JS / standalone fallback. A differently-branded game-less instance
110
- overrides the product name, tagline, and Hall link from its own config. */
111
447
  (function () {
448
+ /* ── the light/dark switch. The pre-paint script above already applied the
449
+ stored choice; this only paints the icon and handles the click. ── */
450
+ var root = document.documentElement, btn = document.getElementById('mode');
451
+ function isDark(){
452
+ return root.getAttribute('data-mode') === 'dark' ||
453
+ (!root.getAttribute('data-mode') && matchMedia('(prefers-color-scheme:dark)').matches);
454
+ }
455
+ function paint(){ btn.textContent = isDark() ? '☀' : '☾'; }
456
+ btn.addEventListener('click', function(){
457
+ var next = isDark() ? 'light' : 'dark';
458
+ root.setAttribute('data-mode', next);
459
+ try { localStorage.setItem('cb-color-mode', next); } catch (e) {}
460
+ paint();
461
+ });
462
+ paint();
463
+
464
+ /* ── brand-pack fill. The server injects window.__BRANDING__ synchronously
465
+ ahead of this script; the baked markup is the no-JS / standalone fallback.
466
+ Origins are NOT filled here — clientBranding() does not project domains, so
467
+ every href that points at another host is server-stamped instead (C6). ── */
112
468
  var b = (typeof window !== 'undefined' && window.__BRANDING__) || null;
113
- if (!b) return;
114
- var id = b.identity || {}, copy = b.copy || {}, dom = b.domains || {};
115
- function set(sel, val){ if(!val) return; document.querySelectorAll(sel).forEach(function(n){ n.textContent = val; }); }
116
- if (id.productName) set('[data-brand-product]', id.productName.toLowerCase());
117
- if (copy.tagline) set('[data-brand-tagline]', copy.tagline);
118
- if (dom.buildersOrigin) document.querySelectorAll('[data-link-hall]').forEach(function(n){ n.setAttribute('href', dom.buildersOrigin); });
469
+ if (b) {
470
+ var copy = b.copy || {};
471
+ if (copy.tagline) {
472
+ document.querySelectorAll('[data-brand-tagline]').forEach(function (n) { n.textContent = copy.tagline; });
473
+ }
474
+ }
475
+
476
+ /* A server token that resolved to nothing leaves a dangling href. Drop those
477
+ links rather than offer a door that goes nowhere. */
478
+ document.querySelectorAll('a[href=""]').forEach(function (a) { a.remove(); });
479
+
480
+ var API = '/api/bongos';
481
+
482
+ /* ── the identity chip. Signed out (401) leaves it empty — the header's own
483
+ doors are the signed-out affordance. Built with createElement/textContent,
484
+ never innerHTML: the same rule the status UI keeps. ── */
485
+ fetch(API + '/me', { credentials: 'same-origin' })
486
+ .then(function (res) { return res.ok ? res.json() : null; })
487
+ .then(function (data) {
488
+ var me = data && (data.builder || data.me);
489
+ if (!me || !me.github_login) return;
490
+ var host = document.getElementById('who');
491
+ if (me.avatar_url) {
492
+ var img = document.createElement('img');
493
+ img.src = me.avatar_url; img.alt = ''; img.width = 26; img.height = 26;
494
+ host.appendChild(img);
495
+ }
496
+ host.appendChild(document.createTextNode(me.github_login));
497
+ })
498
+ .catch(function () { /* signed out or offline — the chip stays empty */ });
499
+
500
+ /* ── the proof band. Apex-only AND independently self-hiding: the section
501
+ ships `hidden` and is revealed only on a 200 with at least one project, so
502
+ a failed, empty or scriptless page shows no orphan heading.
503
+ The public projection is {origin, name, tagline, art_url} — it carries NO
504
+ status, deliberately (platform-identity.js listFeaturedProjects). So this
505
+ renders no liveness chip: an unbacked "live · checked 2m ago" on the front
506
+ door would be a claim nothing verified. The hub owns verification. ── */
507
+ var band = document.getElementById('proof');
508
+ if (band) {
509
+ fetch(API + '/projects/featured')
510
+ .then(function (res) { return res.ok ? res.json() : null; })
511
+ .then(function (data) {
512
+ var list = (data && data.projects) || [];
513
+ if (!list.length) return;
514
+ var grid = document.getElementById('proofGrid');
515
+ list.forEach(function (p) {
516
+ var a = document.createElement('a');
517
+ a.className = 'conItem';
518
+ var href = safeHttpUrl(p.origin);
519
+ if (href) { a.href = href; a.rel = 'noopener'; }
520
+ var orb = document.createElement('span');
521
+ orb.className = 'orb';
522
+ /* a stable hue per project — an identifier, not a status claim */
523
+ orb.style.setProperty('--acc', 'hsl(' + hue(p.origin || p.name || '') + ' 78% 55%)');
524
+ orb.style.setProperty('--glow', 'hsl(' + hue(p.origin || p.name || '') + ' 78% 55% / .5)');
525
+ a.appendChild(orb);
526
+ var body = document.createElement('span');
527
+ var name = document.createElement('span');
528
+ name.className = 'conName';
529
+ name.textContent = p.name || p.origin || 'A project';
530
+ body.appendChild(name);
531
+ if (p.tagline) {
532
+ var tag = document.createElement('span');
533
+ tag.className = 'conTag';
534
+ tag.textContent = p.tagline;
535
+ body.appendChild(tag);
536
+ }
537
+ a.appendChild(body);
538
+ grid.appendChild(a);
539
+ });
540
+ band.hidden = false;
541
+ })
542
+ .catch(function () { /* non-200 / offline — the band stays hidden */ });
543
+ }
544
+ function safeHttpUrl(raw) {
545
+ try {
546
+ var u = new URL(String(raw));
547
+ return (u.protocol === 'https:' || u.protocol === 'http:') ? u.href : '';
548
+ } catch (e) { return ''; }
549
+ }
550
+ function hue(s) {
551
+ var h = 0;
552
+ for (var i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;
553
+ return h;
554
+ }
555
+
556
+ var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
557
+
558
+ /* ── the self-typing eyebrow. DECORATION over real text: the markup already
559
+ says "bongos" and stays correct with scripting off or motion reduced. ── */
560
+ if (!reduce) {
561
+ var el = document.getElementById('typed'), words = ['claude','gemini','chatgpt','bongos'], w = 0;
562
+ (function cycle(){
563
+ var word = words[w++ % words.length], i = 0;
564
+ el.textContent = '';
565
+ (function type(){
566
+ if (i <= word.length) { el.textContent = word.slice(0, i++); setTimeout(type, 72); }
567
+ else setTimeout(function erase(){
568
+ if (el.textContent.length) { el.textContent = el.textContent.slice(0, -1); setTimeout(erase, 38); }
569
+ else cycle();
570
+ }, word === 'bongos' ? 2100 : 900);
571
+ })();
572
+ })();
573
+ }
119
574
  })();
120
575
  </script>
121
576
  </body>