helixevo 0.6.0 → 0.7.0

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.
@@ -5,6 +5,9 @@ import { ConsolePanel } from '@/components/console-panel'
5
5
  import { MetricCard } from '@/components/metric-card'
6
6
  import { PageHero } from '@/components/page-hero'
7
7
  import { SectionFrame } from '@/components/section-frame'
8
+ import { OperatorLoopTrail } from '@/components/operator-loop-trail'
9
+ import { SurfaceJumpLinks } from '@/components/surface-jump-links'
10
+ import { NextStepEmptyState } from '@/components/next-step-empty-state'
8
11
 
9
12
  interface Discovery {
10
13
  id: string
@@ -190,8 +193,20 @@ export default function ResearchClient({ buffer, projects, coevolution }: Props)
190
193
  { label: `${buffer.drafts.length} drafts`, tone: 'yellow' },
191
194
  { label: `${nearPass.length} near-pass drafts`, tone: 'green' },
192
195
  ]}
196
+ actions={
197
+ <div style={{ display: 'grid', gap: 12 }}>
198
+ <div className="hero-note-card">
199
+ <div className="hero-note-label">Discovery handoff</div>
200
+ <div className="hero-note-title">Observe gaps → research patterns → draft reusable skills</div>
201
+ <div className="hero-note-copy">Use Research when recurring project demand needs external evidence before it can become a stronger response or semantic pattern.</div>
202
+ </div>
203
+ <SurfaceJumpLinks surface="research" variant="compact" title="Discovery handoffs" />
204
+ </div>
205
+ }
193
206
  />
194
207
 
208
+ <OperatorLoopTrail surface="research" />
209
+
195
210
  <SectionFrame
196
211
  eyebrow="Why research now"
197
212
  title="Current pressure handoff"
@@ -371,10 +386,13 @@ export default function ResearchClient({ buffer, projects, coevolution }: Props)
371
386
  tone="blue"
372
387
  >
373
388
  {buffer.discoveries.length === 0 ? (
374
- <div className="empty-state" style={{ padding: 24 }}>
375
- <div className="empty-state-title">No discoveries yet</div>
376
- <div className="empty-state-desc">Run research to search the web for techniques your skill network does not cover yet.</div>
377
- </div>
389
+ <NextStepEmptyState
390
+ title="No discoveries yet"
391
+ description="Run research to search the web for techniques your skill network does not cover yet, then compare those discoveries against the current routed pressure picture."
392
+ command="helixevo research"
393
+ pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'green' }}
394
+ guideLink={{ label: 'Guide · Adaptation Loop', anchor: 'loop', tone: 'purple' }}
395
+ />
378
396
  ) : (
379
397
  <div className="summary-list">
380
398
  {buffer.discoveries.sort((a, b) => b.score - a.score).map((discovery) => (
@@ -400,10 +418,13 @@ export default function ResearchClient({ buffer, projects, coevolution }: Props)
400
418
  tone="yellow"
401
419
  >
402
420
  {buffer.drafts.length === 0 ? (
403
- <div className="empty-state" style={{ padding: 24 }}>
404
- <div className="empty-state-title">No drafts yet</div>
405
- <div className="empty-state-desc">Run research to generate the first draft skills and evaluate them against the current thresholds.</div>
406
- </div>
421
+ <NextStepEmptyState
422
+ title="No drafts yet"
423
+ description="Once research has enough evidence, it will generate the first draft skills and evaluate them against the current thresholds before they enter the wider loop."
424
+ command="helixevo research --verbose"
425
+ pageLink={{ label: 'Open Projects', href: '/projects', tone: 'blue' }}
426
+ guideLink={{ label: 'Guide · Quick Start', anchor: 'quickstart', tone: 'purple' }}
427
+ />
407
428
  ) : (
408
429
  <div className="summary-list">
409
430
  {buffer.drafts.sort((a, b) => b.avgScore - a.avgScore).map((draft) => (
@@ -6,7 +6,11 @@ import { ConsolePanel } from '@/components/console-panel'
6
6
  import { MetricCard } from '@/components/metric-card'
7
7
  import { PageHero } from '@/components/page-hero'
8
8
  import { SectionFrame } from '@/components/section-frame'
9
+ import { OperatorLoopTrail } from '@/components/operator-loop-trail'
10
+ import { SurfaceJumpLinks } from '@/components/surface-jump-links'
11
+ import { NextStepEmptyState } from '@/components/next-step-empty-state'
9
12
  import type { GovernanceModeName, GovernanceState, TopologyDashboardSummary, TopologyReviewDecisionStatus, TopologyReviewStatus } from '@/lib/data'
13
+ import type { ProofDashboardSummary } from '@/lib/proof'
10
14
 
11
15
  type RunState = 'idle' | 'running' | 'success' | 'error' | 'stopped'
12
16
 
@@ -62,9 +66,11 @@ function consoleTone(state: RunState): 'neutral' | 'green' | 'red' | 'yellow' {
62
66
  export default function TopologyClient({
63
67
  initialDashboard,
64
68
  initialGovernanceState,
69
+ proof,
65
70
  }: {
66
71
  initialDashboard: TopologyDashboardSummary
67
72
  initialGovernanceState: GovernanceState
73
+ proof: ProofDashboardSummary
68
74
  }) {
69
75
  const [dashboard, setDashboard] = useState(initialDashboard)
70
76
  const [governanceState, setGovernanceState] = useState(initialGovernanceState)
@@ -241,6 +247,7 @@ export default function TopologyClient({
241
247
  { label: `${dashboard.summary.open} open reviews`, tone: dashboard.summary.open > 0 ? 'yellow' : 'green' },
242
248
  { label: `${dashboard.execution.prepared} prepared`, tone: dashboard.execution.prepared > 0 ? 'blue' : 'neutral' },
243
249
  { label: `${dashboard.execution.applied} applied`, tone: dashboard.execution.applied > 0 ? 'green' : 'neutral' },
250
+ { label: `${proof.summary.reviewOpen} proof review`, tone: proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral' },
244
251
  { label: formatMode(dashboard.governance.activeMode), tone: toneForMode(dashboard.governance.activeMode) },
245
252
  { label: dashboard.governance.source === 'operator-selected' ? 'operator-steered' : 'derived mode', tone: dashboard.governance.source === 'operator-selected' ? 'purple' : 'neutral' },
246
253
  ]}
@@ -250,22 +257,29 @@ export default function TopologyClient({
250
257
  <div className="hero-note-label">Active governance profile</div>
251
258
  <div className="hero-note-title">{formatMode(dashboard.governance.activeMode)}</div>
252
259
  <div className="hero-note-copy">{dashboard.governance.profile.explanation}</div>
260
+ <div style={{ marginTop: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
261
+ <span className="badge badge-gray">{proof.summary.effective} effective records</span>
262
+ <Link href="/proof" className="badge badge-gray" style={{ textDecoration: 'none' }}>open proof</Link>
263
+ </div>
253
264
  </div>
254
- <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
255
- <button onClick={handleOptimize} disabled={runState === 'running'} className="badge badge-blue" style={{ border: 'none', cursor: 'pointer' }}>Run graph optimize</button>
256
- <Link href="/ontology" className="badge badge-gray">Open ontology</Link>
257
- <Link href="/coevolution" className="badge badge-gray">Open co-evolution</Link>
258
- <Link href="/network" className="badge badge-gray">Open network</Link>
265
+ <div style={{ display: 'grid', gap: 10 }}>
266
+ <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
267
+ <button onClick={handleOptimize} disabled={runState === 'running'} className="badge badge-blue" style={{ border: 'none', cursor: 'pointer' }}>Run graph optimize</button>
268
+ </div>
269
+ <SurfaceJumpLinks surface="topology" variant="compact" title="Structural handoffs" />
259
270
  </div>
260
271
  </div>
261
272
  }
262
273
  />
263
274
 
275
+ <OperatorLoopTrail surface="topology" />
276
+
264
277
  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
265
278
  <MetricCard label="Open review items" value={dashboard.summary.open} sublabel={`${dashboard.summary.deferred} deferred • ${dashboard.summary.accepted} accepted`} tone={dashboard.summary.open > 0 ? 'yellow' : 'green'} icon="⇄" />
266
279
  <MetricCard label="Accepted ready" value={dashboard.acceptedReady.length} sublabel="accepted candidates not yet prepared for execution" tone={dashboard.acceptedReady.length > 0 ? 'blue' : 'neutral'} icon="✓" />
267
280
  <MetricCard label="Prepared plans" value={dashboard.execution.prepared} sublabel={`${dashboard.execution.safeToApply} safe-to-apply • ${dashboard.execution.prepareOnly} prepare-only`} tone={dashboard.execution.prepared > 0 ? 'blue' : 'neutral'} icon="◇" />
268
281
  <MetricCard label="Applied plans" value={dashboard.execution.applied} sublabel={`${dashboard.execution.rolledBack} rolled back • ${dashboard.execution.artifacts} artifacts`} tone={dashboard.execution.applied > 0 ? 'green' : 'neutral'} icon="↑" />
282
+ <MetricCard label="Structural proof" value={proof.summary.reviewOpen} sublabel={`${proof.summary.effective} effective • ${proof.summary.regressed} regressed`} tone={proof.summary.reviewOpen > 0 ? 'yellow' : proof.summary.effective > 0 ? 'green' : 'neutral'} icon="◇" />
269
283
  <MetricCard label="Manual-route backlog" value={dashboard.summary.generatedFromManualReview} sublabel="pressure routed into actual operator review" tone="purple" icon="!" />
270
284
  <MetricCard label="Snapshots" value={dashboard.execution.snapshots} sublabel="before/after state preserved for reviewed execution" tone="purple" icon="◎" />
271
285
  </div>
@@ -300,10 +314,13 @@ export default function TopologyClient({
300
314
  </div>
301
315
  </div>
302
316
  )) : (
303
- <div className="empty-state" style={{ padding: 24 }}>
304
- <div className="empty-state-title">No accepted candidates waiting for preparation</div>
305
- <div className="empty-state-desc">Accept a topology review item first, then it will enter the reviewed execution pipeline here.</div>
306
- </div>
317
+ <NextStepEmptyState
318
+ title="No accepted candidates waiting for preparation"
319
+ description="Accept a topology review item first, then it can enter the reviewed execution pipeline and become a prepared apply plan."
320
+ command="helixevo graph --optimize"
321
+ pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'purple' }}
322
+ guideLink={{ label: 'Guide · Maturity & Safety', anchor: 'maturity', tone: 'yellow' }}
323
+ />
307
324
  )}
308
325
  </div>
309
326
 
@@ -342,10 +359,13 @@ export default function TopologyClient({
342
359
  </div>
343
360
  </div>
344
361
  )) : (
345
- <div className="empty-state" style={{ padding: 24 }}>
346
- <div className="empty-state-title">No prepared plans yet</div>
347
- <div className="empty-state-desc">Prepared plans will appear here after you convert an accepted review candidate into an explicit apply plan.</div>
348
- </div>
362
+ <NextStepEmptyState
363
+ title="No prepared plans yet"
364
+ description="Prepared plans will appear here after an accepted review candidate is converted into an explicit apply plan with snapshot-backed safety."
365
+ command="helixevo topology --prepare <candidateId>"
366
+ pageLink={{ label: 'Open Ontology', href: '/ontology', tone: 'green' }}
367
+ guideLink={{ label: 'Guide · Maturity & Safety', anchor: 'maturity', tone: 'yellow' }}
368
+ />
349
369
  )}
350
370
  </div>
351
371
  </div>
@@ -426,10 +446,13 @@ export default function TopologyClient({
426
446
  </div>
427
447
  </div>
428
448
  )) : (
429
- <div className="empty-state" style={{ padding: 24 }}>
430
- <div className="empty-state-title">No topology decisions yet</div>
431
- <div className="empty-state-desc">Run graph optimize to populate the queue, then accept, defer, or reject structural candidates from here.</div>
432
- </div>
449
+ <NextStepEmptyState
450
+ title="No topology decisions yet"
451
+ description="Run graph optimize to populate the queue, then accept, defer, or reject structural candidates from this page so the review memory becomes explicit."
452
+ command="helixevo graph --optimize"
453
+ pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'purple' }}
454
+ guideLink={{ label: 'Guide · Surface Map', anchor: 'surfaces', tone: 'blue' }}
455
+ />
433
456
  )}
434
457
  </div>
435
458
  </SectionFrame>
@@ -491,10 +514,13 @@ export default function TopologyClient({
491
514
  </div>
492
515
  </div>
493
516
  )) : (
494
- <div className="empty-state" style={{ padding: 28 }}>
495
- <div className="empty-state-title">No topology backlog yet</div>
496
- <div className="empty-state-desc">Run <code>graph --optimize</code> from this page to refresh structural review candidates and turn manual-review into a populated operator lane.</div>
497
- </div>
517
+ <NextStepEmptyState
518
+ title="No topology backlog yet"
519
+ description="Refresh structural review candidates from this page to turn manual-review into a populated operator lane instead of a placeholder route label."
520
+ command="helixevo graph --optimize"
521
+ pageLink={{ label: 'Open Ontology', href: '/ontology', tone: 'green' }}
522
+ guideLink={{ label: 'Guide · Surface Map', anchor: 'surfaces', tone: 'blue' }}
523
+ />
498
524
  )}
499
525
  </div>
500
526
  </SectionFrame>
@@ -539,10 +565,12 @@ export default function TopologyClient({
539
565
  </div>
540
566
  </div>
541
567
  )) : (
542
- <div className="empty-state" style={{ padding: 24 }}>
543
- <div className="empty-state-title">No applied topology history yet</div>
544
- <div className="empty-state-desc">Once a prepared safe plan is applied, it will appear here with rollback continuity.</div>
545
- </div>
568
+ <NextStepEmptyState
569
+ title="No applied topology history yet"
570
+ description="Applied topology history will appear here once a prepared safe plan is executed and snapshot-backed rollback continuity becomes relevant."
571
+ command="helixevo topology --apply <planId>"
572
+ guideLink={{ label: 'Guide · Maturity & Safety', anchor: 'maturity', tone: 'yellow' }}
573
+ />
546
574
  )}
547
575
  </div>
548
576
  </SectionFrame>
@@ -567,10 +595,13 @@ export default function TopologyClient({
567
595
  </div>
568
596
  </div>
569
597
  )) : (
570
- <div className="empty-state" style={{ padding: 24 }}>
571
- <div className="empty-state-title">No structural candidates stored yet</div>
572
- <div className="empty-state-desc">The ledger will populate after the first topology refresh.</div>
573
- </div>
598
+ <NextStepEmptyState
599
+ title="No structural candidates stored yet"
600
+ description="The structural review ledger will populate after the first topology refresh and then persist reviewed intent across decisions and execution."
601
+ command="helixevo graph --optimize"
602
+ pageLink={{ label: 'Open Co-Evolution', href: '/coevolution', tone: 'purple' }}
603
+ guideLink={{ label: 'Guide · Surface Map', anchor: 'surfaces', tone: 'blue' }}
604
+ />
574
605
  )}
575
606
  </div>
576
607
  </SectionFrame>
@@ -1,4 +1,5 @@
1
1
  import { loadGovernanceState, loadTopologyDashboardSummary } from '@/lib/data'
2
+ import { loadProofDashboardSummary } from '@/lib/proof'
2
3
  import TopologyClient from './client'
3
4
 
4
5
  export const dynamic = 'force-dynamic'
@@ -6,5 +7,6 @@ export const dynamic = 'force-dynamic'
6
7
  export default function TopologyPage() {
7
8
  const dashboard = loadTopologyDashboardSummary()
8
9
  const governanceState = loadGovernanceState()
9
- return <TopologyClient initialDashboard={dashboard} initialGovernanceState={governanceState} />
10
+ const proof = loadProofDashboardSummary()
11
+ return <TopologyClient initialDashboard={dashboard} initialGovernanceState={governanceState} proof={proof} />
10
12
  }
@@ -0,0 +1,22 @@
1
+ import Link from 'next/link'
2
+ import { guideHref, type GuideAnchorId, type SurfaceTone } from '@/lib/loop-map'
3
+
4
+ function toneClass(tone: SurfaceTone) {
5
+ return `hero-chip hero-chip-${tone}`
6
+ }
7
+
8
+ export function GuideDeepLink({
9
+ anchor,
10
+ label,
11
+ tone = 'neutral',
12
+ }: {
13
+ anchor: GuideAnchorId
14
+ label: string
15
+ tone?: SurfaceTone
16
+ }) {
17
+ return (
18
+ <Link href={guideHref(anchor)} className={toneClass(tone)} style={{ textDecoration: 'none' }}>
19
+ {label}
20
+ </Link>
21
+ )
22
+ }
@@ -0,0 +1,53 @@
1
+ import Link from 'next/link'
2
+ import { GuideDeepLink } from '@/components/guide-deep-link'
3
+ import type { GuideAnchorId, SurfaceTone } from '@/lib/loop-map'
4
+
5
+ export function NextStepEmptyState({
6
+ title,
7
+ description,
8
+ command,
9
+ pageLink,
10
+ guideLink,
11
+ }: {
12
+ title: string
13
+ description: string
14
+ command?: string
15
+ pageLink?: { label: string; href: string; tone?: SurfaceTone }
16
+ guideLink?: { label: string; anchor: GuideAnchorId; tone?: SurfaceTone }
17
+ }) {
18
+ return (
19
+ <div className="empty-state" style={{ padding: 24 }}>
20
+ <div className="empty-state-title">{title}</div>
21
+ <div className="empty-state-desc" style={{ maxWidth: 680 }}>{description}</div>
22
+ {command ? (
23
+ <div
24
+ style={{
25
+ marginTop: 14,
26
+ padding: '12px 14px',
27
+ borderRadius: 16,
28
+ border: '1px solid var(--border)',
29
+ background: 'rgba(255,255,255,0.76)',
30
+ display: 'inline-grid',
31
+ gap: 6,
32
+ textAlign: 'left',
33
+ }}
34
+ >
35
+ <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.9 }}>Try next</div>
36
+ <code style={{ fontFamily: 'var(--font-mono)', fontSize: 11.5, color: 'var(--text)' }}>$ {command}</code>
37
+ </div>
38
+ ) : null}
39
+ {(pageLink || guideLink) ? (
40
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'center', marginTop: 14 }}>
41
+ {pageLink ? (
42
+ <Link href={pageLink.href} className={`hero-chip hero-chip-${pageLink.tone ?? 'blue'}`} style={{ textDecoration: 'none' }}>
43
+ {pageLink.label}
44
+ </Link>
45
+ ) : null}
46
+ {guideLink ? (
47
+ <GuideDeepLink anchor={guideLink.anchor} label={guideLink.label} tone={guideLink.tone ?? 'purple'} />
48
+ ) : null}
49
+ </div>
50
+ ) : null}
51
+ </div>
52
+ )
53
+ }
@@ -0,0 +1,46 @@
1
+ import Link from 'next/link'
2
+ import { LOOP_STAGES, getSurfaceLoopConfig, type SurfaceKey } from '@/lib/loop-map'
3
+
4
+ export function OperatorLoopTrail({ surface }: { surface: SurfaceKey }) {
5
+ const config = getSurfaceLoopConfig(surface)
6
+
7
+ return (
8
+ <div
9
+ style={{
10
+ padding: '14px 16px',
11
+ borderRadius: 20,
12
+ border: '1px solid var(--border)',
13
+ background: 'linear-gradient(180deg, rgba(255,255,255,0.82), rgba(245,241,236,0.72))',
14
+ boxShadow: 'var(--shadow-xs)',
15
+ display: 'grid',
16
+ gap: 10,
17
+ }}
18
+ >
19
+ <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
20
+ <div>
21
+ <div className="section-label">Operator loop</div>
22
+ <div style={{ fontSize: 12.5, color: 'var(--text-dim)', marginTop: 4, lineHeight: 1.55 }}>{config.context}</div>
23
+ </div>
24
+ <span className="badge badge-gray">
25
+ {config.stage ? `Current stage · ${LOOP_STAGES.find((stage) => stage.id === config.stage)?.label}` : 'Cockpit view'}
26
+ </span>
27
+ </div>
28
+
29
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
30
+ {LOOP_STAGES.map((stage) => {
31
+ const active = config.stage === stage.id
32
+ return (
33
+ <Link
34
+ key={stage.id}
35
+ href={stage.href}
36
+ className={`hero-chip hero-chip-${active ? stage.tone : 'neutral'}`}
37
+ style={{ textDecoration: 'none' }}
38
+ >
39
+ {stage.label}
40
+ </Link>
41
+ )
42
+ })}
43
+ </div>
44
+ </div>
45
+ )
46
+ }
@@ -14,6 +14,7 @@ const OPERATE_NAV: NavItem[] = [
14
14
  { href: '/projects', label: 'Projects', icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z' },
15
15
  { href: '/research', label: 'Research', icon: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z' },
16
16
  { href: '/coevolution', label: 'Co-Evolution', icon: 'M4 12h4m8 0h4M12 4v4m0 8v4M7.05 7.05l2.83 2.83m4.24 4.24l2.83 2.83m0-9.9l-2.83 2.83m-4.24 4.24l-2.83 2.83' },
17
+ { href: '/proof', label: 'Proof', icon: 'M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z' },
17
18
  ]
18
19
 
19
20
  const STRUCTURE_NAV: NavItem[] = [
@@ -0,0 +1,69 @@
1
+ import Link from 'next/link'
2
+ import { getSurfaceLoopConfig, guideHref, type SurfaceKey } from '@/lib/loop-map'
3
+
4
+ export function SurfaceJumpLinks({
5
+ surface,
6
+ title = 'Next hops',
7
+ variant = 'compact',
8
+ }: {
9
+ surface: SurfaceKey
10
+ title?: string
11
+ variant?: 'compact' | 'panel'
12
+ }) {
13
+ const config = getSurfaceLoopConfig(surface)
14
+
15
+ if (variant === 'compact') {
16
+ return (
17
+ <div style={{ display: 'grid', gap: 8 }}>
18
+ <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--text-muted)', letterSpacing: 0.9, textTransform: 'uppercase' }}>{title}</div>
19
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
20
+ {config.adjacentLinks.map((link) => (
21
+ <Link key={link.href} href={link.href} className={`badge badge-${link.tone === 'neutral' ? 'gray' : link.tone}`} style={{ textDecoration: 'none' }}>
22
+ {link.label}
23
+ </Link>
24
+ ))}
25
+ {config.guideLinks.map((link) => (
26
+ <Link key={link.anchor} href={guideHref(link.anchor)} className={`hero-chip hero-chip-${link.tone}`} style={{ textDecoration: 'none' }}>
27
+ {link.label}
28
+ </Link>
29
+ ))}
30
+ </div>
31
+ </div>
32
+ )
33
+ }
34
+
35
+ return (
36
+ <div style={{ display: 'grid', gap: 12 }}>
37
+ <div>
38
+ <div className="section-label">{title}</div>
39
+ <div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 6, lineHeight: 1.6 }}>
40
+ Move deliberately into the next most relevant surface instead of backing out to Overview every time.
41
+ </div>
42
+ </div>
43
+
44
+ <div className="grid-2" style={{ gap: 12 }}>
45
+ {config.adjacentLinks.map((link) => (
46
+ <Link
47
+ key={link.href}
48
+ href={link.href}
49
+ className="card"
50
+ style={{ padding: '14px 16px', textDecoration: 'none', display: 'grid', gap: 6 }}
51
+ >
52
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
53
+ <span className={`hero-chip hero-chip-${link.tone}`}>{link.label}</span>
54
+ </div>
55
+ <div style={{ fontSize: 12, color: 'var(--text-dim)', lineHeight: 1.6 }}>{link.description}</div>
56
+ </Link>
57
+ ))}
58
+ </div>
59
+
60
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
61
+ {config.guideLinks.map((link) => (
62
+ <Link key={link.anchor} href={guideHref(link.anchor)} className={`hero-chip hero-chip-${link.tone}`} style={{ textDecoration: 'none' }}>
63
+ {link.label}
64
+ </Link>
65
+ ))}
66
+ </div>
67
+ </div>
68
+ )
69
+ }