egregore-artifacts 0.1.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.
@@ -0,0 +1,285 @@
1
+ // Activity report data → React element tree
2
+ import React from 'react';
3
+ import {
4
+ ArtifactHeader, SectionCard, ArtifactFooter,
5
+ } from '../components.js';
6
+ import { colors, fonts } from '../tokens.js';
7
+
8
+ const h = React.createElement;
9
+
10
+ // ── Helpers ─────────────────────────────────────────────────────
11
+
12
+ function MetricRow({ metrics }) {
13
+ return h('div', {
14
+ style: {
15
+ display: 'grid',
16
+ gridTemplateColumns: `repeat(${Math.min(metrics.length, 4)}, 1fr)`,
17
+ gap: '12px',
18
+ marginBottom: '1.5rem',
19
+ },
20
+ },
21
+ ...metrics.map((m, i) =>
22
+ h('div', {
23
+ key: i,
24
+ style: {
25
+ background: 'white',
26
+ border: `1px solid ${colors.border}`,
27
+ borderRadius: '12px',
28
+ padding: '1.25rem',
29
+ textAlign: 'center',
30
+ },
31
+ },
32
+ h('div', {
33
+ style: { fontSize: '28px', fontFamily: fonts.serif, fontWeight: 400, color: colors.black, lineHeight: 1.2 },
34
+ }, m.value),
35
+ h('div', {
36
+ style: { fontSize: '12px', fontFamily: fonts.mono, color: colors.muted, marginTop: '4px', textTransform: 'uppercase', letterSpacing: '0.04em' },
37
+ }, m.label),
38
+ )
39
+ ),
40
+ );
41
+ }
42
+
43
+ function TrendsCard({ trends }) {
44
+ if (!trends || !trends.cadence) return null;
45
+ const weeks = trends.cadence.slice(0, 4);
46
+ const thisWeek = weeks[0]?.sessions || 0;
47
+ const lastWeek = weeks[1]?.sessions || 0;
48
+ const ratio = trends.recentCaptureRatio;
49
+
50
+ const lines = [];
51
+ if (lastWeek > 0) {
52
+ const change = thisWeek > lastWeek ? 'up' : thisWeek < lastWeek ? 'down' : 'flat';
53
+ const verb = change === 'up' ? 'doubled' : change === 'down' ? 'dropped' : 'held steady';
54
+ lines.push(`Session cadence ${verb} this week (${thisWeek} vs ${lastWeek} last week).`);
55
+ } else {
56
+ lines.push(`${thisWeek} sessions this week.`);
57
+ }
58
+ if (ratio != null) {
59
+ const pct = Math.round(ratio * 100);
60
+ lines.push(`Capture ratio at ${pct}% — ${pct < 50 ? 'most sessions lack artifacts.' : 'good artifact coverage.'}`);
61
+ }
62
+
63
+ return h('div', {
64
+ style: {
65
+ background: 'rgba(212, 135, 90, 0.06)',
66
+ border: `1px solid rgba(212, 135, 90, 0.15)`,
67
+ borderRadius: '12px',
68
+ padding: '1.25rem 1.5rem',
69
+ marginBottom: '1.5rem',
70
+ fontSize: '15px',
71
+ lineHeight: 1.6,
72
+ color: colors.dark,
73
+ },
74
+ },
75
+ ...lines.map((line, i) => h('p', { key: i, style: { margin: '0 0 0.25rem' } }, line)),
76
+ );
77
+ }
78
+
79
+ function SessionRow({ date, topic, by, handedTo, muted }) {
80
+ return h('div', {
81
+ style: {
82
+ display: 'flex',
83
+ alignItems: 'baseline',
84
+ gap: '12px',
85
+ padding: '6px 0',
86
+ borderBottom: '1px solid rgba(224, 216, 204, 0.5)',
87
+ fontSize: '14px',
88
+ },
89
+ },
90
+ h('span', {
91
+ style: { fontFamily: fonts.mono, fontSize: '12px', color: colors.muted, minWidth: '72px', flexShrink: 0 },
92
+ }, date),
93
+ by && h('span', {
94
+ style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.blueMuted, background: 'rgba(123, 157, 183, 0.1)', padding: '1px 6px', borderRadius: '3px', flexShrink: 0 },
95
+ }, by),
96
+ h('span', { style: { color: muted ? colors.muted : colors.black, flex: 1, fontStyle: muted ? 'italic' : 'normal' } }, topic),
97
+ handedTo && h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.terracotta } }, `→ ${handedTo}`),
98
+ );
99
+ }
100
+
101
+ function HandoffItem({ handoff, index }) {
102
+ const statusDot = handoff.status === 'pending'
103
+ ? h('span', { style: { color: colors.terracotta, fontWeight: 700 } }, '●')
104
+ : h('span', { style: { color: colors.muted } }, '○');
105
+
106
+ return h('div', {
107
+ style: { padding: '8px 0', borderBottom: '1px solid rgba(224, 216, 204, 0.5)', fontSize: '14px' },
108
+ },
109
+ h('div', { style: { display: 'flex', gap: '8px', alignItems: 'baseline' } },
110
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '12px', color: colors.muted } }, `[${index + 1}]`),
111
+ statusDot,
112
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '12px', color: colors.blueMuted } }, handoff.from),
113
+ h('span', null, '→ you:'),
114
+ h('span', { style: { fontWeight: 500 } }, handoff.topic),
115
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.muted } }, `(${handoff.date})`),
116
+ ),
117
+ handoff.summary && h('p', {
118
+ style: { margin: '4px 0 0 28px', fontSize: '13px', color: colors.muted, lineHeight: 1.4 },
119
+ }, handoff.summary),
120
+ );
121
+ }
122
+
123
+ function QuestRow({ quest }) {
124
+ return h('div', {
125
+ style: {
126
+ display: 'flex',
127
+ alignItems: 'center',
128
+ gap: '16px',
129
+ padding: '8px 0',
130
+ borderBottom: '1px solid rgba(224, 216, 204, 0.5)',
131
+ fontSize: '14px',
132
+ },
133
+ },
134
+ h('span', { style: { fontWeight: 500, flex: 1, minWidth: 0 } }, quest.name),
135
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '12px', color: colors.muted, textAlign: 'right', whiteSpace: 'nowrap' } },
136
+ `${quest.artifacts} artifacts`),
137
+ quest.daysSince > 0 && h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.muted, whiteSpace: 'nowrap' } },
138
+ `· ${quest.daysSince}d ago`),
139
+ );
140
+ }
141
+
142
+ // ── Template ────────────────────────────────────────────────────
143
+
144
+ export function activityTemplate(data) {
145
+ const sections = [];
146
+
147
+ // Header
148
+ sections.push(
149
+ h(ArtifactHeader, {
150
+ key: 'header',
151
+ title: data.title,
152
+ type: 'activity',
153
+ date: data.date,
154
+ author: data.me,
155
+ status: 'active',
156
+ priority: 0,
157
+ projects: [],
158
+ })
159
+ );
160
+
161
+ // Trends summary
162
+ if (data.trends) {
163
+ sections.push(h(TrendsCard, { key: 'trends', trends: data.trends }));
164
+ }
165
+
166
+ // Metrics row
167
+ const activeQuests = data.quests.length;
168
+ const openPrs = data.prs.filter(p => p.state === 'open' || p.state === 'OPEN').length;
169
+ const metrics = [
170
+ { label: 'My Sessions', value: String(data.mySessions.length) },
171
+ { label: 'Team Sessions', value: String(data.teamSessions.length) },
172
+ { label: 'Active Quests', value: String(activeQuests) },
173
+ ];
174
+ if (openPrs > 0) metrics.push({ label: 'Open PRs', value: String(openPrs) });
175
+ if (data.todos.activeTodoCount) metrics.push({ label: 'Active Todos', value: String(data.todos.activeTodoCount) });
176
+ sections.push(h(MetricRow, { key: 'metrics', metrics: metrics.slice(0, 4) }));
177
+
178
+ // Handoffs to me
179
+ if (data.handoffsToMe && data.handoffsToMe.length > 0) {
180
+ sections.push(
181
+ h(SectionCard, { key: 'handoffs', label: `Handoffs & Asks (${data.handoffsToMe.length})` },
182
+ h('div', null,
183
+ ...data.handoffsToMe.map((ho, i) => h(HandoffItem, { key: i, handoff: ho, index: i })),
184
+ ),
185
+ )
186
+ );
187
+ }
188
+
189
+ // Pending questions
190
+ if (data.pendingQuestions && data.pendingQuestions.length > 0) {
191
+ sections.push(
192
+ h(SectionCard, { key: 'questions', label: `Pending Questions (${data.pendingQuestions.length})` },
193
+ h('div', null,
194
+ ...data.pendingQuestions.map((q, i) =>
195
+ h('div', {
196
+ key: i,
197
+ style: { display: 'flex', gap: '10px', padding: '6px 0', borderBottom: '1px solid rgba(224, 216, 204, 0.5)', fontSize: '14px' },
198
+ },
199
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '12px', color: colors.blueMuted, flexShrink: 0 } }, q.from),
200
+ h('span', { style: { flex: 1 } }, q.topic),
201
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.muted } }, q.date),
202
+ )
203
+ ),
204
+ ),
205
+ )
206
+ );
207
+ }
208
+
209
+ // My sessions (grouped)
210
+ if (data.mySessions.length > 0) {
211
+ sections.push(
212
+ h(SectionCard, { key: 'my-sessions', label: `Your Sessions` },
213
+ h('div', null,
214
+ ...data.mySessions.map((s, i) =>
215
+ h(SessionRow, {
216
+ key: i,
217
+ date: s.date,
218
+ topic: s.topic,
219
+ handedTo: s.handedTo,
220
+ muted: s.topic.startsWith('('),
221
+ })
222
+ ),
223
+ ),
224
+ )
225
+ );
226
+ }
227
+
228
+ // Team sessions
229
+ if (data.teamSessions.length > 0) {
230
+ sections.push(
231
+ h(SectionCard, { key: 'team-sessions', label: `Team` },
232
+ h('div', null,
233
+ ...data.teamSessions.map((s, i) =>
234
+ h(SessionRow, { key: i, date: s.date, topic: s.topic, by: s.by })
235
+ ),
236
+ ),
237
+ )
238
+ );
239
+ }
240
+
241
+ // Quests
242
+ if (data.quests && data.quests.length > 0) {
243
+ sections.push(
244
+ h(SectionCard, { key: 'quests', label: `Quests (${data.quests.length} active)` },
245
+ h('div', null,
246
+ ...data.quests.map((q, i) => h(QuestRow, { key: i, quest: q })),
247
+ ),
248
+ )
249
+ );
250
+ }
251
+
252
+ // Knowledge gap / orphans
253
+ const gapItems = [];
254
+ if (data.orphans?.unlinkedArtifacts) gapItems.push(`${data.orphans.unlinkedArtifacts} artifacts unlinked to quests`);
255
+ if (data.knowledgeGap?.gapCount) gapItems.push(`${data.knowledgeGap.gapCount} sessions without captured insights`);
256
+
257
+ if (gapItems.length > 0) {
258
+ sections.push(
259
+ h('div', {
260
+ key: 'gaps',
261
+ style: {
262
+ fontFamily: fonts.mono,
263
+ fontSize: '12px',
264
+ color: colors.muted,
265
+ padding: '1rem 0',
266
+ borderTop: `1px solid ${colors.border}`,
267
+ marginTop: '1rem',
268
+ },
269
+ },
270
+ ...gapItems.map((item, i) => h('div', { key: i, style: { marginBottom: '4px' } }, item)),
271
+ )
272
+ );
273
+ }
274
+
275
+ // Footer
276
+ sections.push(
277
+ h(ArtifactFooter, {
278
+ key: 'footer',
279
+ generatedAt: new Date().toISOString(),
280
+ source: 'bin/activity-data.sh',
281
+ })
282
+ );
283
+
284
+ return h('div', null, ...sections);
285
+ }
@@ -0,0 +1,225 @@
1
+ // Handoff data → React element tree
2
+ import React from 'react';
3
+ import {
4
+ ArtifactHeader, SectionCard, ThreadList, BulletList,
5
+ TextBlock, ArtifactFooter,
6
+ } from '../components.js';
7
+ import { renderMarkdownLite } from '../markdown.js';
8
+ import { colors, fonts } from '../tokens.js';
9
+
10
+ const h = React.createElement;
11
+
12
+ export function handoffTemplate(handoff) {
13
+ const sections = [];
14
+
15
+ // Header
16
+ sections.push(
17
+ h(ArtifactHeader, {
18
+ key: 'header',
19
+ title: handoff.title,
20
+ type: 'handoff',
21
+ date: handoff.date,
22
+ author: handoff.author,
23
+ status: 'active',
24
+ priority: 0,
25
+ projects: handoff.project ? [handoff.project] : [],
26
+ })
27
+ );
28
+
29
+ // Addressed to
30
+ if (handoff.to && handoff.to.length > 0) {
31
+ sections.push(
32
+ h('div', {
33
+ key: 'to',
34
+ style: {
35
+ display: 'flex',
36
+ alignItems: 'center',
37
+ gap: '8px',
38
+ marginBottom: '1.5rem',
39
+ fontFamily: fonts.mono,
40
+ fontSize: '13px',
41
+ color: colors.muted,
42
+ },
43
+ },
44
+ h('span', null, 'To:'),
45
+ ...handoff.to.map((name, i) =>
46
+ h('span', {
47
+ key: i,
48
+ style: {
49
+ padding: '2px 10px',
50
+ background: 'rgba(123, 157, 183, 0.1)',
51
+ borderRadius: '50px',
52
+ color: colors.blueMuted,
53
+ fontSize: '12px',
54
+ fontWeight: 500,
55
+ },
56
+ }, name)
57
+ ),
58
+ )
59
+ );
60
+ }
61
+
62
+ // Source / Branch
63
+ if (handoff.source || handoff.branch) {
64
+ sections.push(
65
+ h('div', {
66
+ key: 'source-meta',
67
+ style: {
68
+ display: 'flex',
69
+ flexWrap: 'wrap',
70
+ gap: '16px',
71
+ marginBottom: '2rem',
72
+ fontFamily: fonts.mono,
73
+ fontSize: '12px',
74
+ color: colors.muted,
75
+ },
76
+ },
77
+ handoff.source && h('span', null, `Source: ${handoff.source}`),
78
+ handoff.branch && h('span', {
79
+ style: {
80
+ padding: '2px 8px',
81
+ background: 'rgba(59, 45, 33, 0.06)',
82
+ borderRadius: '4px',
83
+ fontFamily: fonts.mono,
84
+ },
85
+ }, handoff.branch),
86
+ )
87
+ );
88
+ }
89
+
90
+ // Summary / Briefing
91
+ if (handoff.summary) {
92
+ sections.push(
93
+ h(SectionCard, { key: 'summary', label: 'Summary' },
94
+ h(TextBlock, { text: handoff.summary, variant: 'lead' })
95
+ )
96
+ );
97
+ }
98
+
99
+ // Current State
100
+ if (handoff.currentState) {
101
+ sections.push(
102
+ h(SectionCard, { key: 'state', label: 'Current State' },
103
+ h(TextBlock, { text: handoff.currentState })
104
+ )
105
+ );
106
+ }
107
+
108
+ // Decisions
109
+ if (handoff.decisions && handoff.decisions.length > 0) {
110
+ sections.push(
111
+ h(SectionCard, { key: 'decisions', label: 'Decisions' },
112
+ h(BulletList, { items: handoff.decisions })
113
+ )
114
+ );
115
+ }
116
+
117
+ // Open Threads
118
+ if (handoff.openThreads && handoff.openThreads.length > 0) {
119
+ const doneCount = handoff.openThreads.filter(t => t.done).length;
120
+ const label = `Open Threads (${doneCount}/${handoff.openThreads.length})`;
121
+ sections.push(
122
+ h(SectionCard, { key: 'threads', label },
123
+ h(ThreadList, { threads: handoff.openThreads })
124
+ )
125
+ );
126
+ }
127
+
128
+ // Next Steps
129
+ if (handoff.nextSteps && handoff.nextSteps.length > 0) {
130
+ sections.push(
131
+ h(SectionCard, { key: 'next', label: 'Next Steps' },
132
+ h('ol', {
133
+ style: {
134
+ listStyle: 'none',
135
+ padding: 0,
136
+ counterReset: 'step',
137
+ },
138
+ },
139
+ ...handoff.nextSteps.map((step, i) =>
140
+ h('li', {
141
+ key: i,
142
+ style: {
143
+ display: 'flex',
144
+ gap: '12px',
145
+ padding: '8px 0',
146
+ borderBottom: i < handoff.nextSteps.length - 1 ? '1px solid rgba(224, 216, 204, 0.5)' : 'none',
147
+ fontSize: '15px',
148
+ lineHeight: 1.5,
149
+ },
150
+ },
151
+ h('span', {
152
+ style: {
153
+ flexShrink: 0,
154
+ width: '24px',
155
+ height: '24px',
156
+ borderRadius: '50%',
157
+ background: colors.terracotta,
158
+ color: colors.cream,
159
+ display: 'flex',
160
+ alignItems: 'center',
161
+ justifyContent: 'center',
162
+ fontSize: '12px',
163
+ fontFamily: fonts.mono,
164
+ fontWeight: 600,
165
+ },
166
+ }, String(i + 1)),
167
+ h('span', null, step),
168
+ )
169
+ ),
170
+ )
171
+ )
172
+ );
173
+ }
174
+
175
+ // Entry Points
176
+ if (handoff.entryPoints && handoff.entryPoints.length > 0) {
177
+ sections.push(
178
+ h(SectionCard, { key: 'entry', label: 'Entry Points' },
179
+ h(BulletList, { items: handoff.entryPoints })
180
+ )
181
+ );
182
+ }
183
+
184
+ // Extra sections (non-standard)
185
+ for (const extra of handoff.extraSections || []) {
186
+ sections.push(
187
+ h(SectionCard, { key: `extra-${extra.heading}`, label: extra.heading },
188
+ h(TextBlock, { text: extra.body })
189
+ )
190
+ );
191
+ }
192
+
193
+ // Attachments (referenced files inlined)
194
+ for (const attachment of handoff.attachments || []) {
195
+ // Strip the frontmatter/title from the attachment — render the body
196
+ let body = attachment.content
197
+ .replace(/^#\s+.+\n/, '') // remove # title line
198
+ .replace(/^\*\*\w+\*\*:.+\n/gm, '') // remove **Key**: value metadata lines
199
+ .trim();
200
+
201
+ // Truncate very large attachments (keep first 60 lines for performance)
202
+ const lines = body.split('\n');
203
+ const truncated = lines.length > 60;
204
+ if (truncated) {
205
+ body = lines.slice(0, 60).join('\n') + `\n\n*... ${lines.length - 60} more lines — see full file at \`${attachment.path}\`*`;
206
+ }
207
+
208
+ sections.push(
209
+ h(SectionCard, { key: `att-${attachment.path}`, label: `${attachment.title}` },
210
+ renderMarkdownLite(body)
211
+ )
212
+ );
213
+ }
214
+
215
+ // Footer
216
+ sections.push(
217
+ h(ArtifactFooter, {
218
+ key: 'footer',
219
+ generatedAt: new Date().toISOString(),
220
+ source: null,
221
+ })
222
+ );
223
+
224
+ return h('div', null, ...sections);
225
+ }
@@ -0,0 +1,112 @@
1
+ // Quest data → React element tree
2
+ import React from 'react';
3
+ import {
4
+ ArtifactHeader, SectionCard, ThreadList, ArtifactList,
5
+ ContributorRow, BulletList, TextBlock, SectionDivider, ArtifactFooter,
6
+ } from '../components.js';
7
+
8
+ const h = React.createElement;
9
+
10
+ export function questTemplate(quest) {
11
+ const sections = [];
12
+
13
+ // Header
14
+ sections.push(
15
+ h(ArtifactHeader, {
16
+ key: 'header',
17
+ title: quest.title,
18
+ type: 'quest',
19
+ date: quest.started,
20
+ author: quest.started_by,
21
+ status: quest.status,
22
+ priority: quest.priority,
23
+ projects: quest.projects,
24
+ })
25
+ );
26
+
27
+ // The Question
28
+ if (quest.question) {
29
+ sections.push(
30
+ h(SectionCard, { key: 'question', label: 'The Question' },
31
+ h(TextBlock, { text: quest.question, variant: 'lead' })
32
+ )
33
+ );
34
+ }
35
+
36
+ // Context / Why This Matters
37
+ if (quest.context) {
38
+ sections.push(
39
+ h(SectionCard, { key: 'context', label: 'Context' },
40
+ h(TextBlock, { text: quest.context })
41
+ )
42
+ );
43
+ }
44
+
45
+ // Vision
46
+ if (quest.vision && quest.vision.length > 0) {
47
+ sections.push(
48
+ h(SectionCard, { key: 'vision', label: 'Vision' },
49
+ h(BulletList, { items: quest.vision })
50
+ )
51
+ );
52
+ }
53
+
54
+ // Threads
55
+ if (quest.threads && quest.threads.length > 0) {
56
+ const doneCount = quest.threads.filter(t => t.done).length;
57
+ const label = `Threads (${doneCount}/${quest.threads.length})`;
58
+ sections.push(
59
+ h(SectionCard, { key: 'threads', label },
60
+ h(ThreadList, { threads: quest.threads })
61
+ )
62
+ );
63
+ }
64
+
65
+ // Artifacts
66
+ if (quest.artifacts && quest.artifacts.length > 0) {
67
+ sections.push(
68
+ h(SectionCard, { key: 'artifacts', label: `Artifacts (${quest.artifacts.length})` },
69
+ h(ArtifactList, { artifacts: quest.artifacts })
70
+ )
71
+ );
72
+ }
73
+
74
+ // Technical Notes
75
+ if (quest.technicalNotes && quest.technicalNotes.length > 0) {
76
+ sections.push(
77
+ h(SectionCard, { key: 'tech', label: 'Technical Notes' },
78
+ h(BulletList, { items: quest.technicalNotes })
79
+ )
80
+ );
81
+ }
82
+
83
+ // Entry Points
84
+ if (quest.entryPoints && quest.entryPoints.length > 0) {
85
+ sections.push(
86
+ h(SectionCard, { key: 'entry', label: 'Entry Points' },
87
+ h(BulletList, { items: quest.entryPoints })
88
+ )
89
+ );
90
+ }
91
+
92
+ // Contributors
93
+ if (quest.contributors && quest.contributors.length > 0) {
94
+ sections.push(
95
+ h('div', { key: 'contributors', className: 'eg-section' },
96
+ h('div', { className: 'eg-card-title', style: { marginBottom: '0.75rem' } }, 'Contributors'),
97
+ h(ContributorRow, { contributors: quest.contributors })
98
+ )
99
+ );
100
+ }
101
+
102
+ // Footer
103
+ sections.push(
104
+ h(ArtifactFooter, {
105
+ key: 'footer',
106
+ generatedAt: new Date().toISOString(),
107
+ source: quest.slug ? `memory/quests/${quest.slug}.md` : null,
108
+ })
109
+ );
110
+
111
+ return h('div', null, ...sections);
112
+ }
package/lib/tokens.js ADDED
@@ -0,0 +1,61 @@
1
+ // Egregore design tokens — extracted from egregore-site/app/globals.css
2
+ // Canonical JS source. CSS source: packages/design-system/tokens.css
3
+
4
+ export const colors = {
5
+ cream: '#F5F2ED',
6
+ black: '#16100B',
7
+ terracotta: '#D4875A',
8
+ blueMuted: '#7B9DB7',
9
+ dark: '#2A2A2A',
10
+ border: '#E0D8CC',
11
+ terminalBg: '#1D1611',
12
+ muted: '#8a8578',
13
+ warmGray: '#d4cfc5',
14
+
15
+ // Functional aliases
16
+ bg: '#F5F2ED',
17
+ text: '#16100B',
18
+ accent: '#D4875A',
19
+ secondary: '#7B9DB7',
20
+
21
+ // Terminal syntax
22
+ terminalHighlight: '#E7794B',
23
+ terminalSuccess: '#6BBF6B',
24
+ terminalFlag: '#85C0FC',
25
+ terminalText: 'rgba(255, 255, 255, 0.85)',
26
+ };
27
+
28
+ export const fonts = {
29
+ serif: "'LT Superior Serif', Georgia, 'Times New Roman', serif",
30
+ sans: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
31
+ mono: "'IBM Plex Mono', 'SF Mono', 'Fira Code', monospace",
32
+ };
33
+
34
+ export const typography = {
35
+ hero: { fontSize: '60px', fontFamily: fonts.serif, fontWeight: 400, lineHeight: '54px', letterSpacing: '-0.03em' },
36
+ section: { fontSize: '36px', fontFamily: fonts.serif, fontWeight: 400, lineHeight: 1.2 },
37
+ card: { fontSize: '24px', fontFamily: fonts.serif, fontWeight: 400, lineHeight: 1.3, letterSpacing: '-0.02em' },
38
+ body: { fontSize: '16px', fontFamily: fonts.sans, fontWeight: 400, lineHeight: 1.65 },
39
+ bodyLg: { fontSize: '18px', fontFamily: fonts.sans, fontWeight: 400, lineHeight: 1.6 },
40
+ meta: { fontSize: '13px', fontFamily: fonts.mono, fontWeight: 400, lineHeight: 1.5 },
41
+ small: { fontSize: '12px', fontFamily: fonts.mono, fontWeight: 400, lineHeight: 1.5 },
42
+ };
43
+
44
+ export const spacing = {
45
+ xs: '0.25rem',
46
+ sm: '0.5rem',
47
+ md: '1rem',
48
+ lg: '1.5rem',
49
+ xl: '2rem',
50
+ '2xl': '3rem',
51
+ '3xl': '4rem',
52
+ };
53
+
54
+ export const radius = {
55
+ sm: '4px',
56
+ md: '8px',
57
+ lg: '12px',
58
+ xl: '20px',
59
+ };
60
+
61
+ export const easing = 'cubic-bezier(0.19, 1, 0.22, 1)';