docs: add animated portfolio cover (SVG) (#25)

Swadhin Biswas committed 1 months ago
73cdc6d
1 parent 0bb3187
commit 73cdc6d
1 file changed
+180 -0
Showing 1 changed file+180-0
▼assets/opengrammar-cover.svg
+180-0
1
+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="t d">
2
+ <title id="t">OpenGrammar pipeline</title>
3
+ <desc id="d">Text from Gmail, Google Docs, Notion and Reddit runs through an offline dictionary and rule engine, then through a BYOK AI router for what local rules miss. The same backend deploys to Cloudflare, Vercel, Netlify, Docker or NPM, with no database and keys kept in the browser.</desc>
4
+ <defs>
5
+ <radialGradient id="bgGlow" cx="18%" cy="0%" r="95%">
6
+ <stop offset="0%" stop-color="#171327"/>
7
+ <stop offset="55%" stop-color="#0C0C10"/>
8
+ <stop offset="100%" stop-color="#09090B"/>
9
+ </radialGradient>
10
+ <pattern id="grid" width="28" height="28" patternUnits="userSpaceOnUse">
11
+ <circle cx="1" cy="1" r="1" fill="#1C1C23"/>
12
+ </pattern>
13
+ <linearGradient id="titleGrad" x1="0" y1="0" x2="1" y2="0">
14
+ <stop offset="0%" stop-color="#FFFFFF"/>
15
+ <stop offset="60%" stop-color="#D6D6DC"/>
16
+ <stop offset="100%" stop-color="#9A9AA4"/>
17
+ </linearGradient>
18
+ <marker id="ah" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
19
+ <path d="M0 0 L10 5 L0 10 z" fill="#5A5A66"/>
20
+ </marker>
21
+ <style>
22
+ .f { font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
23
+ .card { fill: #121218; stroke: #2E2E37; stroke-width: 1; }
24
+ .card-title { fill: #ECECF1; font-size: 12.5px; font-weight: 700; letter-spacing: .5px; }
25
+ .sub { fill: #8C8C97; font-size: 10px; }
26
+ .micro { fill: #67677A; font-size: 9px; letter-spacing: 1.8px; }
27
+ .item { fill: #A9A9B4; font-size: 10px; }
28
+ .chip { fill: #17171E; stroke: #33333D; }
29
+ .chip-t { fill: #BCBCC7; font-size: 9.5px; }
30
+ .node { fill: #13131A; stroke: #34343F; }
31
+ .node-t { fill: #E4E4EA; font-size: 11px; font-weight: 700; }
32
+ .node-s { fill: #7F7F8B; font-size: 8.8px; }
33
+ .flow { stroke: #565663; stroke-width: 1.4; fill: none; stroke-dasharray: 5 7; animation: dash 1.15s linear infinite; }
34
+ .live { fill: #34D399; animation: blink 2s ease-in-out infinite; }
35
+ .stage { animation: stagePulse 7.6s linear infinite; }
36
+ .bar { transform-box: fill-box; transform-origin: left center; animation: grow 1.1s cubic-bezier(.2,.7,.2,1) both; }
37
+ .track { fill: #1B1B22; }
38
+ .lbl { fill: #A9A9B4; font-size: 9.5px; }
39
+ .val { fill: #8C8C97; font-size: 9px; }
40
+ @keyframes dash { to { stroke-dashoffset: -24; } }
41
+ @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: .18; } }
42
+ @keyframes stagePulse { 0%, 100% { stroke: #34343F; } 8% { stroke: #A78BFA; } 20% { stroke: #34343F; } }
43
+ @keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
44
+ @media (prefers-reduced-motion: reduce) {
45
+ .flow, .live, .stage, .bar { animation: none; }
46
+ }
47
+ </style>
48
+ </defs>
49
+
50
+ <rect width="1200" height="630" fill="url(#bgGlow)"/>
51
+ <rect width="1200" height="630" fill="url(#grid)"/>
52
+ <rect x="12" y="12" width="1176" height="606" rx="22" fill="none" stroke="#24242C" stroke-width="1.2"/>
53
+
54
+ <g transform="translate(48,41) scale(1.05)">
55
+ <path d="M12.67 19a2 2 0 0 0 1.416-.588l6.154-6.172a6 6 0 0 0-8.49-8.49L5.586 9.914A2 2 0 0 0 5 11.328V18a1 1 0 0 0 1 1z" fill="none" stroke="#E8E8EE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
56
+ <path d="M16 8 2 22" fill="none" stroke="#A78BFA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
57
+ <path d="M17.5 15H9" fill="none" stroke="#E8E8EE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
58
+ </g>
59
+ <text class="f" x="84" y="62" font-size="25" font-weight="800" letter-spacing="3" fill="url(#titleGrad)">OPENGRAMMAR</text>
60
+ <text class="f sub" x="86" y="86">privacy-first, self-hosted writing assistant</text>
61
+
62
+ <rect x="958" y="38" width="194" height="32" rx="16" fill="#111118" stroke="#2E2E37"/>
63
+ <circle class="live" cx="978" cy="54" r="3.5"/>
64
+ <text class="f chip-t" x="992" y="58">Apache-2.0 · offline-first</text>
65
+
66
+ <text class="f micro" x="48" y="122">01 · WRITE ANYWHERE</text>
67
+ <text class="f micro" x="48" y="292">02 · SELF-HOST</text>
68
+ <text class="f micro" x="48" y="458">03 · SCORE &amp; PRIVACY</text>
69
+
70
+ <rect class="card" x="48" y="132" width="328" height="118" rx="14"/>
71
+ <text class="f card-title" x="64" y="160">Where text lives</text>
72
+ <text class="f sub" x="64" y="178">extension content script</text>
73
+ <circle cx="66" cy="197" r="1.8" fill="#67E8F9"/>
74
+ <text class="f item" x="76" y="200">Gmail · Google Docs</text>
75
+ <circle cx="66" cy="215" r="1.8" fill="#67E8F9"/>
76
+ <text class="f item" x="76" y="218">Notion · Reddit</text>
77
+ <circle cx="66" cy="233" r="1.8" fill="#67E8F9"/>
78
+ <text class="f item" x="76" y="236">any rich-text editor</text>
79
+
80
+ <path id="pa" class="flow" d="M380 191 H428" marker-end="url(#ah)"/>
81
+ <path id="pb" class="flow" d="M768 191 H818" marker-end="url(#ah)"/>
82
+
83
+ <rect class="card" x="436" y="132" width="328" height="118" rx="14"/>
84
+ <text class="f card-title" x="452" y="160">Local engine</text>
85
+ <text class="f sub" x="452" y="178">offline, in the browser</text>
86
+ <circle cx="454" cy="197" r="1.8" fill="#A78BFA"/>
87
+ <text class="f item" x="464" y="200">156,000-word dictionary</text>
88
+ <circle cx="454" cy="215" r="1.8" fill="#A78BFA"/>
89
+ <text class="f item" x="464" y="218">40+ regex rule patterns</text>
90
+ <circle cx="454" cy="233" r="1.8" fill="#A78BFA"/>
91
+ <text class="f item" x="464" y="236">instant, zero requests</text>
92
+
93
+ <rect class="card" x="824" y="132" width="328" height="118" rx="14"/>
94
+ <text class="f card-title" x="840" y="160">AI router (BYOK)</text>
95
+ <text class="f sub" x="840" y="178">only what local rules miss</text>
96
+ <circle cx="842" cy="197" r="1.8" fill="#FCD34D"/>
97
+ <text class="f item" x="852" y="200">OpenAI · Groq · OpenRouter</text>
98
+ <circle cx="842" cy="215" r="1.8" fill="#FCD34D"/>
99
+ <text class="f item" x="852" y="218">Together · Ollama</text>
100
+ <circle cx="842" cy="233" r="1.8" fill="#34D399"/>
101
+ <text class="f item" x="852" y="236">deduped against local findings</text>
102
+
103
+ <path id="pc" class="flow" d="M988 250 V296" marker-end="url(#ah)"/>
104
+
105
+ <rect class="card" x="48" y="302" width="1104" height="128" rx="14"/>
106
+ <text class="f card-title" x="64" y="330">Deploy it anywhere</text>
107
+ <text class="f sub" x="1136" y="330" text-anchor="end">one REST API · /analyze · /autocomplete · /providers</text>
108
+
109
+ <rect class="node stage" x="60" y="348" width="196" height="62" rx="10" style="animation-delay:0s"/>
110
+ <text class="f node-t" x="158" y="376" text-anchor="middle">Cloudflare Workers</text>
111
+ <text class="f node-s" x="158" y="392" text-anchor="middle">edge runtime</text>
112
+
113
+ <rect class="node stage" x="281" y="348" width="196" height="62" rx="10" style="animation-delay:1.5s"/>
114
+ <text class="f node-t" x="379" y="376" text-anchor="middle">Vercel Edge</text>
115
+ <text class="f node-s" x="379" y="392" text-anchor="middle">edge runtime</text>
116
+
117
+ <rect class="node stage" x="502" y="348" width="196" height="62" rx="10" style="animation-delay:3s"/>
118
+ <text class="f node-t" x="600" y="376" text-anchor="middle">Netlify Functions</text>
119
+ <text class="f node-s" x="600" y="392" text-anchor="middle">serverless</text>
120
+
121
+ <rect class="node stage" x="723" y="348" width="196" height="62" rx="10" style="animation-delay:4.5s"/>
122
+ <text class="f node-t" x="821" y="376" text-anchor="middle">Docker</text>
123
+ <text class="f node-s" x="821" y="392" text-anchor="middle">multi-arch image</text>
124
+
125
+ <rect class="node stage" x="944" y="348" width="196" height="62" rx="10" style="animation-delay:6s"/>
126
+ <text class="f node-t" x="1042" y="376" text-anchor="middle">NPM / Node</text>
127
+ <text class="f node-s" x="1042" y="392" text-anchor="middle">npx opengrammar-server</text>
128
+
129
+ <path class="flow" d="M260 379 H275" marker-end="url(#ah)"/>
130
+ <path class="flow" d="M481 379 H496" marker-end="url(#ah)"/>
131
+ <path class="flow" d="M702 379 H717" marker-end="url(#ah)"/>
132
+ <path class="flow" d="M923 379 H938" marker-end="url(#ah)"/>
133
+ <circle r="3" fill="#A78BFA" opacity="0.9"><animateMotion dur="7.6s" repeatCount="indefinite"><mpath href="#pl"/></animateMotion></circle>
134
+ <path id="pl" d="M72 379 H1128" fill="none" stroke="none"/>
135
+
136
+ <path id="pd" class="flow" d="M600 434 V462" marker-end="url(#ah)"/>
137
+ <path id="pe" class="flow" d="M672 522 H718" marker-end="url(#ah)"/>
138
+
139
+ <rect class="card" x="48" y="468" width="620" height="110" rx="14"/>
140
+ <text class="f card-title" x="64" y="496">Writing score</text>
141
+ <text class="f sub" x="636" y="496" text-anchor="end">0-100 · four dimensions</text>
142
+
143
+ <text class="f lbl" x="64" y="514">correctness</text>
144
+ <rect class="track" x="150" y="508" width="320" height="8" rx="4"/>
145
+ <rect class="bar" x="150" y="508" width="282" height="8" rx="4" fill="#67E8F9"/>
146
+ <text class="f val" x="482" y="516">88</text>
147
+
148
+ <text class="f lbl" x="64" y="533">readability</text>
149
+ <rect class="track" x="150" y="527" width="320" height="8" rx="4"/>
150
+ <rect class="bar" x="150" y="527" width="243" height="8" rx="4" fill="#A78BFA" style="animation-delay:.18s"/>
151
+ <text class="f val" x="482" y="535">76</text>
152
+
153
+ <text class="f lbl" x="64" y="552">engagement</text>
154
+ <rect class="track" x="150" y="546" width="320" height="8" rx="4"/>
155
+ <rect class="bar" x="150" y="546" width="205" height="8" rx="4" fill="#FCD34D" style="animation-delay:.36s"/>
156
+ <text class="f val" x="482" y="554">64</text>
157
+
158
+ <text class="f lbl" x="64" y="571">delivery</text>
159
+ <rect class="track" x="150" y="565" width="320" height="8" rx="4"/>
160
+ <rect class="bar" x="150" y="565" width="262" height="8" rx="4" fill="#34D399" style="animation-delay:.54s"/>
161
+ <text class="f val" x="482" y="573">82</text>
162
+
163
+ <rect class="card" x="724" y="468" width="428" height="110" rx="14"/>
164
+ <text class="f card-title" x="740" y="496">Privacy by default</text>
165
+ <rect class="chip" x="740" y="504" width="85" height="22" rx="11"/>
166
+ <text class="f chip-t" x="782" y="519" text-anchor="middle">no database</text>
167
+ <rect class="chip" x="833" y="504" width="157" height="22" rx="11"/>
168
+ <text class="f chip-t" x="911" y="519" text-anchor="middle">keys stay in the browser</text>
169
+ <rect class="chip" x="740" y="536" width="123" height="22" rx="11"/>
170
+ <text class="f chip-t" x="801" y="551" text-anchor="middle">stateless AI calls</text>
171
+ <rect class="chip" x="871" y="536" width="78" height="22" rx="11"/>
172
+ <text class="f chip-t" x="910" y="551" text-anchor="middle">Apache-2.0</text>
173
+ <text class="f sub" x="740" y="572">text is never stored on a server</text>
174
+
175
+ <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite"><mpath href="#pa"/></animateMotion></circle>
176
+ <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.4s"><mpath href="#pb"/></animateMotion></circle>
177
+ <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.2s"><mpath href="#pc"/></animateMotion></circle>
178
+ <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.1s"><mpath href="#pd"/></animateMotion></circle>
179
+ <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.6s"><mpath href="#pe"/></animateMotion></circle>
180
+</svg>
0
181