@gmickel/gno 0.3.4 → 0.4.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.
Files changed (57) hide show
  1. package/README.md +194 -53
  2. package/assets/badges/license.svg +12 -0
  3. package/assets/badges/npm.svg +13 -0
  4. package/assets/badges/twitter.svg +22 -0
  5. package/assets/badges/website.svg +22 -0
  6. package/package.json +30 -1
  7. package/src/cli/commands/ask.ts +11 -186
  8. package/src/cli/commands/models/pull.ts +9 -4
  9. package/src/cli/commands/serve.ts +19 -0
  10. package/src/cli/program.ts +28 -0
  11. package/src/llm/registry.ts +3 -1
  12. package/src/pipeline/answer.ts +191 -0
  13. package/src/serve/CLAUDE.md +91 -0
  14. package/src/serve/bunfig.toml +2 -0
  15. package/src/serve/context.ts +181 -0
  16. package/src/serve/index.ts +7 -0
  17. package/src/serve/public/app.tsx +56 -0
  18. package/src/serve/public/components/ai-elements/code-block.tsx +176 -0
  19. package/src/serve/public/components/ai-elements/conversation.tsx +98 -0
  20. package/src/serve/public/components/ai-elements/inline-citation.tsx +285 -0
  21. package/src/serve/public/components/ai-elements/loader.tsx +96 -0
  22. package/src/serve/public/components/ai-elements/message.tsx +443 -0
  23. package/src/serve/public/components/ai-elements/prompt-input.tsx +1421 -0
  24. package/src/serve/public/components/ai-elements/sources.tsx +75 -0
  25. package/src/serve/public/components/ai-elements/suggestion.tsx +51 -0
  26. package/src/serve/public/components/preset-selector.tsx +403 -0
  27. package/src/serve/public/components/ui/badge.tsx +46 -0
  28. package/src/serve/public/components/ui/button-group.tsx +82 -0
  29. package/src/serve/public/components/ui/button.tsx +62 -0
  30. package/src/serve/public/components/ui/card.tsx +92 -0
  31. package/src/serve/public/components/ui/carousel.tsx +244 -0
  32. package/src/serve/public/components/ui/collapsible.tsx +31 -0
  33. package/src/serve/public/components/ui/command.tsx +181 -0
  34. package/src/serve/public/components/ui/dialog.tsx +141 -0
  35. package/src/serve/public/components/ui/dropdown-menu.tsx +255 -0
  36. package/src/serve/public/components/ui/hover-card.tsx +42 -0
  37. package/src/serve/public/components/ui/input-group.tsx +167 -0
  38. package/src/serve/public/components/ui/input.tsx +21 -0
  39. package/src/serve/public/components/ui/progress.tsx +28 -0
  40. package/src/serve/public/components/ui/scroll-area.tsx +56 -0
  41. package/src/serve/public/components/ui/select.tsx +188 -0
  42. package/src/serve/public/components/ui/separator.tsx +26 -0
  43. package/src/serve/public/components/ui/table.tsx +114 -0
  44. package/src/serve/public/components/ui/textarea.tsx +18 -0
  45. package/src/serve/public/components/ui/tooltip.tsx +59 -0
  46. package/src/serve/public/globals.css +226 -0
  47. package/src/serve/public/hooks/use-api.ts +112 -0
  48. package/src/serve/public/index.html +13 -0
  49. package/src/serve/public/pages/Ask.tsx +442 -0
  50. package/src/serve/public/pages/Browse.tsx +270 -0
  51. package/src/serve/public/pages/Dashboard.tsx +202 -0
  52. package/src/serve/public/pages/DocView.tsx +302 -0
  53. package/src/serve/public/pages/Search.tsx +335 -0
  54. package/src/serve/routes/api.ts +763 -0
  55. package/src/serve/server.ts +249 -0
  56. package/src/store/sqlite/adapter.ts +47 -0
  57. package/src/store/types.ts +10 -0
@@ -0,0 +1,56 @@
1
+ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
2
+ import type * as React from 'react';
3
+
4
+ import { cn } from '../../lib/utils';
5
+
6
+ function ScrollArea({
7
+ className,
8
+ children,
9
+ ...props
10
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
11
+ return (
12
+ <ScrollAreaPrimitive.Root
13
+ className={cn('relative', className)}
14
+ data-slot="scroll-area"
15
+ {...props}
16
+ >
17
+ <ScrollAreaPrimitive.Viewport
18
+ className="size-full rounded-[inherit] outline-none transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] focus-visible:ring-ring/50"
19
+ data-slot="scroll-area-viewport"
20
+ >
21
+ {children}
22
+ </ScrollAreaPrimitive.Viewport>
23
+ <ScrollBar />
24
+ <ScrollAreaPrimitive.Corner />
25
+ </ScrollAreaPrimitive.Root>
26
+ );
27
+ }
28
+
29
+ function ScrollBar({
30
+ className,
31
+ orientation = 'vertical',
32
+ ...props
33
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
34
+ return (
35
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
36
+ className={cn(
37
+ 'flex touch-none select-none p-px transition-colors',
38
+ orientation === 'vertical' &&
39
+ 'h-full w-2.5 border-l border-l-transparent',
40
+ orientation === 'horizontal' &&
41
+ 'h-2.5 flex-col border-t border-t-transparent',
42
+ className
43
+ )}
44
+ data-slot="scroll-area-scrollbar"
45
+ orientation={orientation}
46
+ {...props}
47
+ >
48
+ <ScrollAreaPrimitive.ScrollAreaThumb
49
+ className="relative flex-1 rounded-full bg-border"
50
+ data-slot="scroll-area-thumb"
51
+ />
52
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
53
+ );
54
+ }
55
+
56
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,188 @@
1
+ import * as SelectPrimitive from '@radix-ui/react-select';
2
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
3
+ import type * as React from 'react';
4
+
5
+ import { cn } from '../../lib/utils';
6
+
7
+ function Select({
8
+ ...props
9
+ }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10
+ return <SelectPrimitive.Root data-slot="select" {...props} />;
11
+ }
12
+
13
+ function SelectGroup({
14
+ ...props
15
+ }: React.ComponentProps<typeof SelectPrimitive.Group>) {
16
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
17
+ }
18
+
19
+ function SelectValue({
20
+ ...props
21
+ }: React.ComponentProps<typeof SelectPrimitive.Value>) {
22
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
23
+ }
24
+
25
+ function SelectTrigger({
26
+ className,
27
+ size = 'default',
28
+ children,
29
+ ...props
30
+ }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
31
+ size?: 'sm' | 'default';
32
+ }) {
33
+ return (
34
+ <SelectPrimitive.Trigger
35
+ className={cn(
36
+ "flex w-fit items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[size=default]:h-9 data-[size=sm]:h-8 data-[placeholder]:text-muted-foreground *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:hover:bg-input/50 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0",
37
+ className
38
+ )}
39
+ data-size={size}
40
+ data-slot="select-trigger"
41
+ {...props}
42
+ >
43
+ {children}
44
+ <SelectPrimitive.Icon asChild>
45
+ <ChevronDownIcon className="size-4 opacity-50" />
46
+ </SelectPrimitive.Icon>
47
+ </SelectPrimitive.Trigger>
48
+ );
49
+ }
50
+
51
+ function SelectContent({
52
+ className,
53
+ children,
54
+ position = 'item-aligned',
55
+ align = 'center',
56
+ ...props
57
+ }: React.ComponentProps<typeof SelectPrimitive.Content>) {
58
+ return (
59
+ <SelectPrimitive.Portal>
60
+ <SelectPrimitive.Content
61
+ align={align}
62
+ className={cn(
63
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
64
+ position === 'popper' &&
65
+ 'data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1',
66
+ className
67
+ )}
68
+ data-slot="select-content"
69
+ position={position}
70
+ {...props}
71
+ >
72
+ <SelectScrollUpButton />
73
+ <SelectPrimitive.Viewport
74
+ className={cn(
75
+ 'p-1',
76
+ position === 'popper' &&
77
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1'
78
+ )}
79
+ >
80
+ {children}
81
+ </SelectPrimitive.Viewport>
82
+ <SelectScrollDownButton />
83
+ </SelectPrimitive.Content>
84
+ </SelectPrimitive.Portal>
85
+ );
86
+ }
87
+
88
+ function SelectLabel({
89
+ className,
90
+ ...props
91
+ }: React.ComponentProps<typeof SelectPrimitive.Label>) {
92
+ return (
93
+ <SelectPrimitive.Label
94
+ className={cn('px-2 py-1.5 text-muted-foreground text-xs', className)}
95
+ data-slot="select-label"
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ function SelectItem({
102
+ className,
103
+ children,
104
+ ...props
105
+ }: React.ComponentProps<typeof SelectPrimitive.Item>) {
106
+ return (
107
+ <SelectPrimitive.Item
108
+ className={cn(
109
+ "relative flex w-full cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
110
+ className
111
+ )}
112
+ data-slot="select-item"
113
+ {...props}
114
+ >
115
+ <span
116
+ className="absolute right-2 flex size-3.5 items-center justify-center"
117
+ data-slot="select-item-indicator"
118
+ >
119
+ <SelectPrimitive.ItemIndicator>
120
+ <CheckIcon className="size-4" />
121
+ </SelectPrimitive.ItemIndicator>
122
+ </span>
123
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
124
+ </SelectPrimitive.Item>
125
+ );
126
+ }
127
+
128
+ function SelectSeparator({
129
+ className,
130
+ ...props
131
+ }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
132
+ return (
133
+ <SelectPrimitive.Separator
134
+ className={cn('pointer-events-none -mx-1 my-1 h-px bg-border', className)}
135
+ data-slot="select-separator"
136
+ {...props}
137
+ />
138
+ );
139
+ }
140
+
141
+ function SelectScrollUpButton({
142
+ className,
143
+ ...props
144
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
145
+ return (
146
+ <SelectPrimitive.ScrollUpButton
147
+ className={cn(
148
+ 'flex cursor-default items-center justify-center py-1',
149
+ className
150
+ )}
151
+ data-slot="select-scroll-up-button"
152
+ {...props}
153
+ >
154
+ <ChevronUpIcon className="size-4" />
155
+ </SelectPrimitive.ScrollUpButton>
156
+ );
157
+ }
158
+
159
+ function SelectScrollDownButton({
160
+ className,
161
+ ...props
162
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
163
+ return (
164
+ <SelectPrimitive.ScrollDownButton
165
+ className={cn(
166
+ 'flex cursor-default items-center justify-center py-1',
167
+ className
168
+ )}
169
+ data-slot="select-scroll-down-button"
170
+ {...props}
171
+ >
172
+ <ChevronDownIcon className="size-4" />
173
+ </SelectPrimitive.ScrollDownButton>
174
+ );
175
+ }
176
+
177
+ export {
178
+ Select,
179
+ SelectContent,
180
+ SelectGroup,
181
+ SelectItem,
182
+ SelectLabel,
183
+ SelectScrollDownButton,
184
+ SelectScrollUpButton,
185
+ SelectSeparator,
186
+ SelectTrigger,
187
+ SelectValue,
188
+ };
@@ -0,0 +1,26 @@
1
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
2
+ import type * as React from 'react';
3
+
4
+ import { cn } from '../../lib/utils';
5
+
6
+ function Separator({
7
+ className,
8
+ orientation = 'horizontal',
9
+ decorative = true,
10
+ ...props
11
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
12
+ return (
13
+ <SeparatorPrimitive.Root
14
+ className={cn(
15
+ 'shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px',
16
+ className
17
+ )}
18
+ data-slot="separator"
19
+ decorative={decorative}
20
+ orientation={orientation}
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ export { Separator };
@@ -0,0 +1,114 @@
1
+ import type * as React from 'react';
2
+
3
+ import { cn } from '../../lib/utils';
4
+
5
+ function Table({ className, ...props }: React.ComponentProps<'table'>) {
6
+ return (
7
+ <div
8
+ className="relative w-full overflow-x-auto"
9
+ data-slot="table-container"
10
+ >
11
+ <table
12
+ className={cn('w-full caption-bottom text-sm', className)}
13
+ data-slot="table"
14
+ {...props}
15
+ />
16
+ </div>
17
+ );
18
+ }
19
+
20
+ function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
21
+ return (
22
+ <thead
23
+ className={cn('[&_tr]:border-b', className)}
24
+ data-slot="table-header"
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
31
+ return (
32
+ <tbody
33
+ className={cn('[&_tr:last-child]:border-0', className)}
34
+ data-slot="table-body"
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
41
+ return (
42
+ <tfoot
43
+ className={cn(
44
+ 'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
45
+ className
46
+ )}
47
+ data-slot="table-footer"
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
54
+ return (
55
+ <tr
56
+ className={cn(
57
+ 'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
58
+ className
59
+ )}
60
+ data-slot="table-row"
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
67
+ return (
68
+ <th
69
+ className={cn(
70
+ 'h-10 whitespace-nowrap px-2 text-left align-middle font-medium text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
71
+ className
72
+ )}
73
+ data-slot="table-head"
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
80
+ return (
81
+ <td
82
+ className={cn(
83
+ 'whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
84
+ className
85
+ )}
86
+ data-slot="table-cell"
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ function TableCaption({
93
+ className,
94
+ ...props
95
+ }: React.ComponentProps<'caption'>) {
96
+ return (
97
+ <caption
98
+ className={cn('mt-4 text-muted-foreground text-sm', className)}
99
+ data-slot="table-caption"
100
+ {...props}
101
+ />
102
+ );
103
+ }
104
+
105
+ export {
106
+ Table,
107
+ TableHeader,
108
+ TableBody,
109
+ TableFooter,
110
+ TableHead,
111
+ TableRow,
112
+ TableCell,
113
+ TableCaption,
114
+ };
@@ -0,0 +1,18 @@
1
+ import type * as React from 'react';
2
+
3
+ import { cn } from '../../lib/utils';
4
+
5
+ function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
6
+ return (
7
+ <textarea
8
+ className={cn(
9
+ 'field-sizing-content flex min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40',
10
+ className
11
+ )}
12
+ data-slot="textarea"
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ export { Textarea };
@@ -0,0 +1,59 @@
1
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
2
+ import type * as React from 'react';
3
+
4
+ import { cn } from '../../lib/utils';
5
+
6
+ function TooltipProvider({
7
+ delayDuration = 0,
8
+ ...props
9
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
10
+ return (
11
+ <TooltipPrimitive.Provider
12
+ data-slot="tooltip-provider"
13
+ delayDuration={delayDuration}
14
+ {...props}
15
+ />
16
+ );
17
+ }
18
+
19
+ function Tooltip({
20
+ ...props
21
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
+ return (
23
+ <TooltipProvider>
24
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
+ </TooltipProvider>
26
+ );
27
+ }
28
+
29
+ function TooltipTrigger({
30
+ ...props
31
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
32
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
33
+ }
34
+
35
+ function TooltipContent({
36
+ className,
37
+ sideOffset = 0,
38
+ children,
39
+ ...props
40
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
41
+ return (
42
+ <TooltipPrimitive.Portal>
43
+ <TooltipPrimitive.Content
44
+ className={cn(
45
+ 'fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-foreground px-3 py-1.5 text-background text-xs data-[state=closed]:animate-out',
46
+ className
47
+ )}
48
+ data-slot="tooltip-content"
49
+ sideOffset={sideOffset}
50
+ {...props}
51
+ >
52
+ {children}
53
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
54
+ </TooltipPrimitive.Content>
55
+ </TooltipPrimitive.Portal>
56
+ );
57
+ }
58
+
59
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
@@ -0,0 +1,226 @@
1
+ @import "tailwindcss";
2
+
3
+ /* =============================================================================
4
+ GNO Serve - "Scholarly Dusk" Design System
5
+ Matching the website design language for consistency
6
+ ============================================================================= */
7
+
8
+ :root {
9
+ /* Dark theme (Default) - "Library at Night" */
10
+ --background: 0 0% 2%; /* #050505 */
11
+ --foreground: 0 0% 93%; /* #ededed */
12
+ --card: 220 14% 7%; /* #0f1115 */
13
+ --card-foreground: 0 0% 93%;
14
+ --popover: 220 14% 7%;
15
+ --popover-foreground: 0 0% 93%;
16
+ --primary: 169 41% 51%; /* #4db8a8 - Alchemical Teal */
17
+ --primary-foreground: 0 0% 100%;
18
+ --secondary: 39 56% 58%; /* #d4a053 - Old Gold */
19
+ --secondary-foreground: 0 0% 100%;
20
+ --muted: 217 14% 11%; /* #181b21 */
21
+ --muted-foreground: 214 7% 61%; /* #949ba3 */
22
+ --accent: 169 41% 51%;
23
+ --accent-foreground: 0 0% 100%;
24
+ --destructive: 0 84% 60%;
25
+ --destructive-foreground: 0 0% 100%;
26
+ --border: 216 12% 14%; /* #1f2329 */
27
+ --input: 216 12% 14%;
28
+ --ring: 169 41% 51%;
29
+ --radius: 0.5rem;
30
+
31
+ /* Extended palette */
32
+ --surface: 217 14% 11%;
33
+ --hover: 217 12% 16%;
34
+ --accent-glow: 169 41% 51%;
35
+ --warm: 39 56% 58%;
36
+ }
37
+
38
+ [data-theme="light"] {
39
+ /* Light theme - "Antique Paper" */
40
+ --background: 40 33% 98%; /* #fcfbf9 */
41
+ --foreground: 30 9% 13%; /* #24221f */
42
+ --card: 38 24% 95%; /* #f5f2ee */
43
+ --card-foreground: 30 9% 13%;
44
+ --popover: 38 24% 95%;
45
+ --popover-foreground: 30 9% 13%;
46
+ --primary: 191 66% 32%; /* #1a6b7d */
47
+ --primary-foreground: 0 0% 100%;
48
+ --secondary: 39 62% 35%; /* #9a7225 */
49
+ --secondary-foreground: 0 0% 100%;
50
+ --muted: 35 17% 91%;
51
+ --muted-foreground: 30 8% 33%;
52
+ --border: 35 12% 85%;
53
+ --input: 35 12% 85%;
54
+ --ring: 191 66% 32%;
55
+ }
56
+
57
+ /* Base styles */
58
+ * {
59
+ border-color: hsl(var(--border));
60
+ }
61
+
62
+ html {
63
+ font-size: 16px;
64
+ scroll-behavior: smooth;
65
+ -webkit-font-smoothing: antialiased;
66
+ }
67
+
68
+ body {
69
+ /* System fonts for local-first/offline operation */
70
+ font-family:
71
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
72
+ background-color: hsl(var(--background));
73
+ color: hsl(var(--foreground));
74
+ line-height: 1.65;
75
+ margin: 0;
76
+ min-height: 100vh;
77
+
78
+ /* Subtle grid pattern */
79
+ background-image:
80
+ linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
81
+ linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
82
+ background-size: 40px 40px;
83
+ }
84
+
85
+ ::selection {
86
+ background-color: hsl(var(--primary) / 0.15);
87
+ color: hsl(var(--primary));
88
+ }
89
+
90
+ /* Typography */
91
+ h1,
92
+ h2,
93
+ h3,
94
+ h4,
95
+ h5,
96
+ h6 {
97
+ /* System serif for local-first/offline operation */
98
+ font-family: Georgia, "Times New Roman", serif;
99
+ line-height: 1.25;
100
+ font-weight: 600;
101
+ letter-spacing: -0.01em;
102
+ }
103
+
104
+ code,
105
+ pre {
106
+ font-family:
107
+ "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
108
+ monospace;
109
+ }
110
+
111
+ /* Search highlight */
112
+ mark {
113
+ @apply bg-yellow-500/30 text-inherit rounded-sm px-0.5;
114
+ }
115
+
116
+ /* Focus styles */
117
+ :focus-visible {
118
+ outline: 2px solid hsl(var(--primary));
119
+ outline-offset: 2px;
120
+ }
121
+
122
+ :focus:not(:focus-visible) {
123
+ outline: none;
124
+ }
125
+
126
+ /* Custom scrollbar */
127
+ ::-webkit-scrollbar {
128
+ width: 8px;
129
+ height: 8px;
130
+ }
131
+
132
+ ::-webkit-scrollbar-track {
133
+ background: transparent;
134
+ }
135
+
136
+ ::-webkit-scrollbar-thumb {
137
+ background: hsl(var(--border));
138
+ border-radius: 4px;
139
+ }
140
+
141
+ ::-webkit-scrollbar-thumb:hover {
142
+ background: hsl(var(--muted-foreground) / 0.5);
143
+ }
144
+
145
+ /* Animation utilities */
146
+ @keyframes fade-in {
147
+ from {
148
+ opacity: 0;
149
+ transform: translateY(10px);
150
+ }
151
+ to {
152
+ opacity: 1;
153
+ transform: translateY(0);
154
+ }
155
+ }
156
+
157
+ @keyframes pulse-glow {
158
+ 0%,
159
+ 100% {
160
+ box-shadow: 0 0 20px hsl(var(--primary) / 0.2);
161
+ }
162
+ 50% {
163
+ box-shadow: 0 0 40px hsl(var(--primary) / 0.4);
164
+ }
165
+ }
166
+
167
+ .animate-fade-in {
168
+ animation: fade-in 0.5s ease-out forwards;
169
+ }
170
+
171
+ .animate-pulse-glow {
172
+ animation: pulse-glow 3s ease-in-out infinite;
173
+ }
174
+
175
+ /* Stagger animation delays */
176
+ .stagger-1 {
177
+ animation-delay: 0.1s;
178
+ }
179
+ .stagger-2 {
180
+ animation-delay: 0.2s;
181
+ }
182
+ .stagger-3 {
183
+ animation-delay: 0.3s;
184
+ }
185
+ .stagger-4 {
186
+ animation-delay: 0.4s;
187
+ }
188
+
189
+ /* Glassmorphism utility */
190
+ .glass {
191
+ background: hsl(var(--background) / 0.8);
192
+ backdrop-filter: blur(16px);
193
+ -webkit-backdrop-filter: blur(16px);
194
+ border: 1px solid hsl(var(--border) / 0.5);
195
+ }
196
+
197
+ /* Aurora glow effect */
198
+ .aurora-glow {
199
+ position: relative;
200
+ }
201
+
202
+ .aurora-glow::before {
203
+ content: "";
204
+ position: absolute;
205
+ inset: -50%;
206
+ background: radial-gradient(
207
+ ellipse at center,
208
+ hsl(var(--primary) / 0.15),
209
+ transparent 70%
210
+ );
211
+ z-index: -1;
212
+ pointer-events: none;
213
+ filter: blur(60px);
214
+ }
215
+
216
+ /* Reduced motion support */
217
+ @media (prefers-reduced-motion: reduce) {
218
+ *,
219
+ *::before,
220
+ *::after {
221
+ animation-duration: 0.01ms !important;
222
+ animation-iteration-count: 1 !important;
223
+ transition-duration: 0.01ms !important;
224
+ scroll-behavior: auto !important;
225
+ }
226
+ }