Commit6ec0594unknown_key
Merge pull request #35 from ccantynz-alt/claude/launch-preparation-QmTb6
Merge pull request #35 from ccantynz-alt/claude/launch-preparation-QmTb6 feat(design): Phases 2-4 marketing app-shell polish</title> <parameter name="body">Continues PR #34 (Phase 1). All four phases of the Editorial-Technical overhaul. Phase 2 (b0148e9): new /pricing /features /about pages + footer relink. Phase 3 (debcf27): RepoHeader/RepoNav/components polish, file-table mono, .repo-nav-ai class replaces inline color, new .page-header utility. Phase 4 (e1db10e): branded 404/500 error pages, prose link underlines, print styles. Tests: 1226 pass / 1 unrelated pre-existing fail. GateTest static crawl: clean.
4 files changed+1511−636ec059497af2fecf45a8c59d1df8487390d75033
4 changed files+1511−63
Modifiedsrc/app.tsx+42−12View fileUnifiedSplit
@@ -31,6 +31,7 @@ import healthRoutes from "./routes/health-probe";
3131import healthDashboardRoutes from "./routes/health";
3232import statusRoutes from "./routes/status";
3333import helpRoutes from "./routes/help";
34import marketingRoutes from "./routes/marketing";
3435import seoRoutes from "./routes/seo";
3536import { platformStatus } from "./routes/platform-status";
3637import insightRoutes from "./routes/insights";
@@ -238,6 +239,9 @@ app.route("/", statusRoutes);
238239// /help — quickstart + API cheatsheet
239240app.route("/", helpRoutes);
240241
242// /pricing, /features, /about — marketing surface
243app.route("/", marketingRoutes);
244
241245// SEO: robots.txt + sitemap.xml
242246app.route("/", seoRoutes);
243247
@@ -321,12 +325,23 @@ app.route("/", webRoutes);
321325app.notFound((c) => {
322326 return c.html(
323327 <Layout title="Not Found">
324 <div class="empty-state">
325 <h2>404</h2>
326 <p>Page not found.</p>
327 <a href="/" style="margin-top: 12px; display: inline-block">
328 Go home
329 </a>
328 <div class="error-page">
329 <div class="error-page-code">404</div>
330 <div class="eyebrow">Not found</div>
331 <h1 class="display error-page-title">
332 That page <span class="gradient-text">isn't here.</span>
333 </h1>
334 <p class="error-page-sub">
335 The URL might be wrong, the resource might have moved, or you
336 might not have permission to see it.
337 </p>
338 <div class="error-page-actions">
339 <a href="/" class="btn btn-primary btn-lg">Go home</a>
340 <a href="/explore" class="btn btn-ghost btn-lg">Explore repos</a>
341 </div>
342 <div class="error-page-meta">
343 <span class="meta-mono">{c.req.method} {c.req.path}</span>
344 </div>
330345 </div>
331346 </Layout>,
332347 404
@@ -340,15 +355,30 @@ app.onError((err, c) => {
340355 path: c.req.path,
341356 method: c.req.method,
342357 });
358 const requestId = c.get("requestId" as never) as string | undefined;
343359 return c.html(
344360 <Layout title="Error">
345 <div class="empty-state">
346 <h2>Something went wrong</h2>
347 <p>An unexpected error occurred.</p>
361 <div class="error-page">
362 <div class="error-page-code error-page-code-err">500</div>
363 <div class="eyebrow" style="color:var(--red)">Server error</div>
364 <h1 class="display error-page-title">
365 Something <span class="gradient-text">went wrong.</span>
366 </h1>
367 <p class="error-page-sub">
368 The error has been reported. Try again — if it persists, file
369 an issue with the request ID below.
370 </p>
371 <div class="error-page-actions">
372 <a href={c.req.path} class="btn btn-primary btn-lg">Retry</a>
373 <a href="/" class="btn btn-ghost btn-lg">Go home</a>
374 </div>
375 {requestId && (
376 <div class="error-page-meta">
377 <span class="meta-mono">request-id: {requestId}</span>
378 </div>
379 )}
348380 {process.env.NODE_ENV !== "production" && (
349 <pre style="margin-top: 16px; text-align: left; font-size: 12px; color: var(--red)">
350 {err.message}
351 </pre>
381 <pre class="error-page-trace">{err.message}</pre>
352382 )}
353383 </div>
354384 </Layout>,
Addedsrc/routes/marketing.tsx+1164−0View fileUnifiedSplit
Large file (1,164 lines). Load full file
Modifiedsrc/views/components.tsx+9−11View fileUnifiedSplit
@@ -26,7 +26,7 @@ export const RepoHeader: FC<{
2626}) => (
2727 <div class="repo-header">
2828 <div>
29 <div style="display: flex; align-items: center; gap: 8px; font-size: 20px">
29 <div class="repo-header-title">
3030 <a href={`/${owner}`} class="owner">
3131 {owner}
3232 </a>
@@ -36,8 +36,7 @@ export const RepoHeader: FC<{
3636 </a>
3737 {archived && (
3838 <span
39 class="badge"
40 style="background:var(--bg-secondary);color:var(--text-muted);font-size:11px;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:0.5px"
39 class="repo-header-pill repo-header-pill-archived"
4140 title="Read-only: pushes and new issues/PRs disabled"
4241 >
4342 Archived
@@ -45,8 +44,7 @@ export const RepoHeader: FC<{
4544 )}
4645 {isTemplate && (
4746 <span
48 class="badge"
49 style="background:var(--bg-secondary);color:var(--accent);font-size:11px;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:0.5px"
47 class="repo-header-pill repo-header-pill-template"
5048 title="This repository can be used as a template"
5149 >
5250 Template
@@ -54,7 +52,7 @@ export const RepoHeader: FC<{
5452 )}
5553 </div>
5654 {forkedFrom && (
57 <div style="font-size: 12px; color: var(--text-muted); margin-top: 2px">
55 <div class="repo-header-fork">
5856 forked from <a href={`/${forkedFrom}`}>{forkedFrom}</a>
5957 </div>
6058 )}
@@ -166,24 +164,24 @@ export const RepoNav: FC<{
166164 </a>
167165 <a
168166 href={`/${owner}/${repo}/explain`}
169 class={active === "explain" ? "active" : ""}
170 style="margin-left: auto; color: #bc8cff"
167 class={`repo-nav-ai${active === "explain" ? " active" : ""}`}
168 style="margin-left: auto"
171169 >
172170 {"\u2728"} Explain
173171 </a>
174 <a href={`/${owner}/${repo}/ask`} style="color: #bc8cff">
172 <a href={`/${owner}/${repo}/ask`} class="repo-nav-ai">
175173 {"\u2728"} Ask AI
176174 </a>
177175 <a
178176 href={`/${owner}/${repo}/spec`}
179 style="color: #bc8cff"
177 class="repo-nav-ai"
180178 title="Spec to PR — paste a feature spec, AI opens a draft PR"
181179 >
182180 {"\u2728"} Spec
183181 </a>
184182 <a
185183 href={`/${owner}/${repo}/ai/tests`}
186 style="color: #bc8cff"
184 class="repo-nav-ai"
187185 title="AI Tests \u2014 generate failing test stubs from a source file"
188186 >
189187 {"\u2728"} Tests
Modifiedsrc/views/layout.tsx+296−40View fileUnifiedSplit
@@ -111,20 +111,21 @@ export const Layout: FC<
111111 <div class="footer-links">
112112 <div class="footer-col">
113113 <div class="footer-col-title">Product</div>
114 <a href="/features">Features</a>
115 <a href="/pricing">Pricing</a>
114116 <a href="/explore">Explore</a>
115117 <a href="/marketplace">Marketplace</a>
116 <a href="/help">Quickstart</a>
117 <a href="/shortcuts">Shortcuts</a>
118118 </div>
119119 <div class="footer-col">
120120 <div class="footer-col-title">Platform</div>
121 <a href="/help">Quickstart</a>
121122 <a href="/status">Status</a>
122123 <a href="/api/graphql">GraphQL</a>
123124 <a href="/mcp">MCP server</a>
124 <a href="/sitemap.xml">Sitemap</a>
125125 </div>
126126 <div class="footer-col">
127 <div class="footer-col-title">Legal</div>
127 <div class="footer-col-title">Company</div>
128 <a href="/about">About</a>
128129 <a href="/terms">Terms</a>
129130 <a href="/privacy">Privacy</a>
130131 <a href="/acceptable-use">Acceptable use</a>
@@ -1134,54 +1135,159 @@ const css = `
11341135 .repo-header {
11351136 display: flex;
11361137 align-items: center;
1137 gap: 8px;
1138 margin-bottom: 18px;
1139 font-size: var(--t-lg);
1140 letter-spacing: -0.015em;
1138 gap: 12px;
1139 margin-bottom: 22px;
1140 }
1141 .repo-header-title {
1142 display: flex;
1143 align-items: center;
1144 gap: 10px;
1145 font-family: var(--font-display);
1146 font-size: 24px;
1147 letter-spacing: -0.025em;
1148 flex-wrap: wrap;
1149 }
1150 .repo-header .owner {
1151 color: var(--text-muted);
1152 font-weight: 500;
1153 transition: color var(--t-fast) var(--ease);
1154 }
1155 .repo-header .owner:hover { color: var(--text-link); text-decoration: none; }
1156 .repo-header .separator { color: var(--text-faint); font-weight: 300; }
1157 .repo-header .name {
1158 color: var(--text-strong);
1159 font-weight: 700;
1160 letter-spacing: -0.028em;
11411161 }
1142 .repo-header .owner { color: var(--text-link); font-weight: 500; }
1143 .repo-header .separator { color: var(--text-faint); }
1144 .repo-header .name { color: var(--text); font-weight: 700; }
11451162 .repo-header .name:hover { color: var(--text-link); text-decoration: none; }
1163 .repo-header-fork {
1164 font-family: var(--font-mono);
1165 font-size: 11px;
1166 color: var(--text-muted);
1167 margin-top: 4px;
1168 letter-spacing: 0.01em;
1169 }
1170 .repo-header-fork a { color: var(--text-muted); }
1171 .repo-header-fork a:hover { color: var(--accent); }
1172 .repo-header-pill {
1173 display: inline-flex;
1174 align-items: center;
1175 padding: 2px 10px;
1176 border-radius: var(--r-full);
1177 font-family: var(--font-mono);
1178 font-size: 10px;
1179 font-weight: 600;
1180 letter-spacing: 0.12em;
1181 text-transform: uppercase;
1182 line-height: 1.6;
1183 vertical-align: 4px;
1184 }
1185 .repo-header-pill-archived {
1186 background: rgba(251,191,36,0.10);
1187 color: var(--yellow);
1188 border: 1px solid rgba(251,191,36,0.30);
1189 }
1190 .repo-header-pill-template {
1191 background: var(--accent-gradient-faint);
1192 color: var(--accent);
1193 border: 1px solid rgba(140,109,255,0.30);
1194 }
11461195 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
11471196
11481197 .repo-nav {
11491198 display: flex;
1150 gap: 2px;
1199 gap: 1px;
11511200 border-bottom: 1px solid var(--border);
1152 margin-bottom: 24px;
1201 margin-bottom: 28px;
11531202 overflow-x: auto;
11541203 scrollbar-width: thin;
11551204 }
11561205 .repo-nav::-webkit-scrollbar { height: 0; }
11571206 .repo-nav a {
1158 padding: 10px 14px;
1207 position: relative;
1208 padding: 11px 14px;
11591209 color: var(--text-muted);
11601210 border-bottom: 2px solid transparent;
11611211 font-size: var(--t-sm);
11621212 font-weight: 500;
11631213 margin-bottom: -1px;
1164 transition: all var(--t-fast) var(--ease);
1214 transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
11651215 white-space: nowrap;
11661216 }
1167 .repo-nav a:hover { text-decoration: none; color: var(--text); background: var(--bg-hover); }
1217 .repo-nav a:hover { text-decoration: none; color: var(--text-strong); background: var(--bg-hover); }
11681218 .repo-nav a.active {
1169 color: var(--text);
1170 border-bottom-color: var(--accent);
1219 color: var(--text-strong);
1220 font-weight: 600;
1221 }
1222 .repo-nav a.active::after {
1223 content: '';
1224 position: absolute;
1225 left: 14px;
1226 right: 14px;
1227 bottom: -1px;
1228 height: 2px;
1229 background: var(--accent-gradient);
1230 border-radius: 2px;
1231 }
1232 /* AI links in the right-side cluster — sparkle accent */
1233 .repo-nav-ai {
1234 color: var(--accent) !important;
1235 font-weight: 500;
1236 }
1237 .repo-nav-ai:hover {
1238 color: var(--accent-hover) !important;
1239 background: var(--accent-gradient-faint) !important;
1240 }
1241 .repo-nav-ai.active {
1242 color: var(--accent-hover) !important;
11711243 font-weight: 600;
11721244 }
11731245
11741246 .breadcrumb {
11751247 display: flex;
1176 gap: 4px;
1248 gap: 6px;
11771249 align-items: center;
1178 margin-bottom: 16px;
1250 margin-bottom: 18px;
11791251 color: var(--text-muted);
11801252 font-size: var(--t-sm);
11811253 font-family: var(--font-mono);
1254 font-feature-settings: var(--mono-feat);
11821255 }
11831256 .breadcrumb a { color: var(--text-link); font-weight: 500; }
11841257 .breadcrumb a:hover { color: var(--accent-hover); }
1258 .breadcrumb strong {
1259 color: var(--text-strong);
1260 font-weight: 600;
1261 }
1262
1263 /* Page header — eyebrow + title + optional actions row.
1264 Use on dashboard, settings, admin, any "section landing" page. */
1265 .page-header {
1266 display: flex;
1267 align-items: flex-end;
1268 justify-content: space-between;
1269 gap: 16px;
1270 margin-bottom: 28px;
1271 padding-bottom: 20px;
1272 border-bottom: 1px solid var(--border-subtle);
1273 flex-wrap: wrap;
1274 }
1275 .page-header-text { flex: 1; min-width: 280px; }
1276 .page-header .eyebrow { margin-bottom: var(--s-2); }
1277 .page-header h1 {
1278 font-family: var(--font-display);
1279 font-size: clamp(24px, 3vw, 36px);
1280 line-height: 1.1;
1281 letter-spacing: -0.028em;
1282 margin-bottom: 6px;
1283 }
1284 .page-header p {
1285 color: var(--text-muted);
1286 font-size: var(--t-sm);
1287 line-height: 1.55;
1288 max-width: 640px;
1289 }
1290 .page-header-actions { display: flex; gap: 8px; align-items: center; }
11851291
11861292 /* ============================================================ */
11871293 /* File browser table */
@@ -1192,14 +1298,29 @@ const css = `
11921298 border-radius: var(--r-md);
11931299 overflow: hidden;
11941300 background: var(--bg-elevated);
1301 border-collapse: collapse;
11951302 }
1196 .file-table tr { border-bottom: 1px solid var(--border); }
1303 .file-table tr { border-bottom: 1px solid var(--border-subtle); transition: background var(--t-fast) var(--ease); }
11971304 .file-table tr:last-child { border-bottom: none; }
1198 .file-table td { padding: 10px 16px; font-size: var(--t-sm); }
1305 .file-table td {
1306 padding: 9px 16px;
1307 font-size: var(--t-sm);
1308 font-family: var(--font-mono);
1309 font-feature-settings: var(--mono-feat);
1310 }
11991311 .file-table tr:hover { background: var(--bg-hover); }
1200 .file-icon { width: 22px; color: var(--text-faint); font-family: var(--font-mono); font-size: var(--t-sm); }
1201 .file-name a { color: var(--text); font-weight: 500; }
1202 .file-name a:hover { color: var(--text-link); text-decoration: none; }
1312 .file-icon {
1313 width: 22px;
1314 color: var(--text-faint);
1315 font-size: 13px;
1316 text-align: center;
1317 }
1318 .file-name a {
1319 color: var(--text);
1320 font-weight: 500;
1321 transition: color var(--t-fast) var(--ease);
1322 }
1323 .file-name a:hover { color: var(--accent); text-decoration: none; }
12031324
12041325 /* ============================================================ */
12051326 /* Blob view */
@@ -1253,26 +1374,45 @@ const css = `
12531374 display: flex;
12541375 justify-content: space-between;
12551376 align-items: center;
1256 padding: 14px 16px;
1257 border-bottom: 1px solid var(--border);
1377 padding: 14px 18px;
1378 border-bottom: 1px solid var(--border-subtle);
12581379 transition: background var(--t-fast) var(--ease);
12591380 }
12601381 .commit-item:last-child { border-bottom: none; }
12611382 .commit-item:hover { background: var(--bg-hover); }
1262 .commit-message { font-size: var(--t-sm); font-weight: 500; line-height: 1.45; }
1263 .commit-meta { font-size: var(--t-xs); color: var(--text-muted); margin-top: 4px; }
1383 .commit-message {
1384 font-size: var(--t-sm);
1385 font-weight: 500;
1386 line-height: 1.45;
1387 color: var(--text-strong);
1388 letter-spacing: -0.005em;
1389 }
1390 .commit-meta {
1391 font-family: var(--font-mono);
1392 font-size: 11px;
1393 color: var(--text-muted);
1394 margin-top: 5px;
1395 letter-spacing: 0.01em;
1396 }
12641397 .commit-sha {
12651398 font-family: var(--font-mono);
1266 font-size: var(--t-xs);
1267 padding: 3px 8px;
1399 font-feature-settings: var(--mono-feat);
1400 font-size: 11px;
1401 padding: 4px 9px;
12681402 background: var(--bg-tertiary);
12691403 border: 1px solid var(--border);
12701404 border-radius: var(--r-sm);
1271 color: var(--text-link);
1272 font-weight: 500;
1405 color: var(--accent);
1406 font-weight: 600;
1407 letter-spacing: 0.02em;
12731408 transition: all var(--t-fast) var(--ease);
12741409 }
1275 .commit-sha:hover { border-color: var(--border-strong); color: var(--accent-hover); }
1410 .commit-sha:hover {
1411 border-color: rgba(140,109,255,0.40);
1412 background: var(--accent-gradient-faint);
1413 color: var(--accent-hover);
1414 text-decoration: none;
1415 }
12761416
12771417 .diff-view { margin-top: 16px; }
12781418 .diff-file {
@@ -1629,14 +1769,21 @@ const css = `
16291769 .state-open { color: var(--green); }
16301770 .state-closed { color: #b69dff; }
16311771 .issue-title {
1632 font-size: var(--t-base);
1772 font-family: var(--font-display);
1773 font-size: var(--t-md);
16331774 font-weight: 600;
1634 line-height: 1.4;
1635 letter-spacing: -0.005em;
1775 line-height: 1.35;
1776 letter-spacing: -0.012em;
1777 }
1778 .issue-title a { color: var(--text-strong); transition: color var(--t-fast) var(--ease); }
1779 .issue-title a:hover { color: var(--accent); text-decoration: none; }
1780 .issue-meta {
1781 font-family: var(--font-mono);
1782 font-size: 11px;
1783 color: var(--text-muted);
1784 margin-top: 5px;
1785 letter-spacing: 0.01em;
16361786 }
1637 .issue-title a { color: var(--text); }
1638 .issue-title a:hover { color: var(--text-link); text-decoration: none; }
1639 .issue-meta { font-size: var(--t-xs); color: var(--text-muted); margin-top: 4px; }
16401787
16411788 .issue-badge {
16421789 display: inline-flex;
@@ -1878,6 +2025,115 @@ const css = `
18782025 .cmdk-item:hover { background: var(--bg-hover) !important; }
18792026 .cmdk-active { background: var(--accent-gradient-faint) !important; border-left: 2px solid var(--accent) !important; }
18802027
2028 /* ============================================================ */
2029 /* Error pages (404 / 500) */
2030 /* ============================================================ */
2031 .error-page {
2032 text-align: center;
2033 padding: 96px 24px 80px;
2034 max-width: 720px;
2035 margin: 0 auto;
2036 position: relative;
2037 }
2038 .error-page::before {
2039 content: '';
2040 position: absolute;
2041 top: 0; left: 50%;
2042 transform: translateX(-50%);
2043 width: 80%; height: 60%;
2044 background: radial-gradient(ellipse at center, rgba(140,109,255,0.10), transparent 65%);
2045 z-index: -1;
2046 pointer-events: none;
2047 }
2048 .error-page-code {
2049 font-family: var(--font-display);
2050 font-size: clamp(80px, 14vw, 160px);
2051 font-weight: 700;
2052 line-height: 0.95;
2053 letter-spacing: -0.05em;
2054 background: linear-gradient(180deg, var(--text) 0%, var(--text-faint) 100%);
2055 -webkit-background-clip: text;
2056 background-clip: text;
2057 -webkit-text-fill-color: transparent;
2058 margin-bottom: var(--s-4);
2059 opacity: 0.85;
2060 }
2061 .error-page-code-err {
2062 background: linear-gradient(180deg, var(--red) 0%, rgba(248,113,113,0.4) 100%);
2063 -webkit-background-clip: text;
2064 background-clip: text;
2065 -webkit-text-fill-color: transparent;
2066 }
2067 .error-page .eyebrow { justify-content: center; margin: 0 auto var(--s-3); }
2068 .error-page-title {
2069 font-size: clamp(28px, 4.5vw, 48px);
2070 line-height: 1.05;
2071 letter-spacing: -0.03em;
2072 margin-bottom: var(--s-4);
2073 }
2074 .error-page-sub {
2075 color: var(--text-muted);
2076 font-size: var(--t-md);
2077 line-height: 1.55;
2078 max-width: 480px;
2079 margin: 0 auto var(--s-8);
2080 }
2081 .error-page-actions {
2082 display: flex;
2083 gap: 12px;
2084 justify-content: center;
2085 flex-wrap: wrap;
2086 margin-bottom: var(--s-7);
2087 }
2088 .error-page-meta {
2089 color: var(--text-faint);
2090 font-size: 11px;
2091 }
2092 .error-page-trace {
2093 margin-top: var(--s-5);
2094 padding: 16px 20px;
2095 background: var(--bg-secondary);
2096 border: 1px solid rgba(248,113,113,0.30);
2097 border-radius: var(--r-md);
2098 font-family: var(--font-mono);
2099 font-size: 12px;
2100 color: var(--red);
2101 text-align: left;
2102 overflow-x: auto;
2103 white-space: pre-wrap;
2104 word-break: break-all;
2105 }
2106
2107 /* Animated underline on inline content links inside markdown / prose */
2108 .prose a, .markdown-body a {
2109 position: relative;
2110 transition: color var(--t-fast) var(--ease);
2111 }
2112 .prose a::after, .markdown-body a::after {
2113 content: '';
2114 position: absolute;
2115 left: 0; right: 0; bottom: -1px;
2116 height: 1px;
2117 background: currentColor;
2118 opacity: 0.35;
2119 transform-origin: left;
2120 transition: opacity var(--t-fast) var(--ease), transform var(--t-base) var(--ease);
2121 }
2122 .prose a:hover::after, .markdown-body a:hover::after {
2123 opacity: 1;
2124 transform: scaleX(1.02);
2125 }
2126
2127 /* Print: keep it readable */
2128 @media print {
2129 body::before, body::after { display: none; }
2130 header, footer, .prelaunch-banner, .repo-nav, .repo-header-actions,
2131 .nav-search, .nav-right, .btn { display: none !important; }
2132 body { background: #fff; color: #000; }
2133 a { color: #000; text-decoration: underline; }
2134 main { max-width: 100%; padding: 0; }
2135 }
2136
18812137 /* Reduced motion preference */
18822138 @media (prefers-reduced-motion: reduce) {
18832139 *, *::before, *::after {
18842140