mobile bluesky app made with flutter
lazurite.stormlightlabs.org/
mobile
bluesky
flutter
1<!doctype html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
6 <title>Thread - Lazurite</title>
7 <link rel="preconnect" href="https://fonts.googleapis.com" />
8 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9 <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet" />
10 <link
11 href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap"
12 rel="stylesheet" />
13 <link rel="stylesheet" href="styles.css" />
14 <style>
15 .thread-container {
16 padding-bottom: 88px;
17 }
18
19 /* Thread line connecting parent posts to the focused post */
20 .thread-line-wrapper {
21 position: relative;
22 }
23
24 .thread-line-wrapper::after {
25 content: "";
26 position: absolute;
27 left: 39px; /* center of avatar (16px pad + 24px half of 48px avatar) - 1px */
28 top: 0;
29 bottom: 0;
30 width: 2px;
31 background-color: var(--border);
32 }
33
34 .thread-line-wrapper .post-card {
35 position: relative;
36 z-index: 1;
37 }
38
39 /* Parent post: smaller, muted, with thread line below */
40 .thread-parent {
41 border-bottom: none;
42 padding-bottom: 0;
43 }
44
45 .thread-parent .post-content {
46 color: var(--text-secondary);
47 }
48
49 .thread-parent .post-actions {
50 display: none;
51 }
52
53 .thread-parent-connector {
54 padding: 0 16px 0 39px;
55 height: 16px;
56 position: relative;
57 }
58
59 .thread-parent-connector::before {
60 content: "";
61 position: absolute;
62 left: 39px;
63 top: 0;
64 bottom: 0;
65 width: 2px;
66 background-color: var(--border);
67 }
68
69 /* Focused post: the main post in the thread */
70 .thread-focused {
71 padding: 16px;
72 border-bottom: 1px solid var(--border);
73 background-color: var(--bg);
74 }
75
76 .thread-focused .post-header {
77 display: flex;
78 gap: 12px;
79 margin-bottom: 16px;
80 }
81
82 .thread-focused .post-content {
83 font-size: 17px;
84 line-height: 1.6;
85 color: var(--text-primary);
86 margin-bottom: 16px;
87 }
88
89 .thread-focused-timestamp {
90 font-size: 14px;
91 color: var(--text-muted);
92 padding-bottom: 16px;
93 border-bottom: 1px solid var(--border);
94 margin-bottom: 12px;
95 }
96
97 .thread-focused-stats {
98 display: flex;
99 gap: 20px;
100 padding-bottom: 12px;
101 border-bottom: 1px solid var(--border);
102 margin-bottom: 4px;
103 }
104
105 .thread-stat {
106 font-size: 14px;
107 color: var(--text-secondary);
108 }
109
110 .thread-stat strong {
111 font-weight: 600;
112 color: var(--text-primary);
113 }
114
115 .thread-focused .post-actions {
116 padding-top: 8px;
117 justify-content: space-around;
118 }
119
120 .thread-focused .post-action svg {
121 width: 22px;
122 height: 22px;
123 }
124
125 .thread-focused .post-action {
126 font-size: 0; /* hide counts in focused action bar, they're shown in stats */
127 }
128
129 /* Active action states */
130 .post-action.active-like {
131 color: var(--accent-error);
132 }
133
134 .post-action.active-repost {
135 color: var(--accent-success);
136 }
137
138 .post-action.active-bookmark {
139 color: var(--accent-primary);
140 }
141
142 /* Replies section */
143 .thread-replies-header {
144 padding: 12px 16px 8px;
145 font-size: 13px;
146 font-weight: 600;
147 color: var(--text-muted);
148 text-transform: uppercase;
149 letter-spacing: 0.5px;
150 border-bottom: 1px solid var(--border);
151 }
152
153 /* Nested reply indentation via thread line */
154 .thread-reply {
155 position: relative;
156 }
157
158 .thread-reply-nested {
159 padding-left: 32px;
160 position: relative;
161 }
162
163 .thread-reply-nested::before {
164 content: "";
165 position: absolute;
166 left: 39px;
167 top: 0;
168 height: 28px;
169 width: 2px;
170 background-color: var(--border);
171 }
172
173 /* Reply composer fixed at bottom, above nav */
174 .thread-reply-bar {
175 position: fixed;
176 bottom: 76px;
177 left: 50%;
178 transform: translateX(-50%);
179 width: 100%;
180 max-width: 414px;
181 background-color: var(--surface);
182 border-top: 1px solid var(--border);
183 padding: 10px 16px;
184 display: flex;
185 align-items: center;
186 gap: 12px;
187 z-index: 90;
188 }
189
190 .thread-reply-bar .avatar {
191 width: 32px;
192 height: 32px;
193 font-size: 12px;
194 }
195
196 .thread-reply-input {
197 flex: 1;
198 border: 1px solid var(--border);
199 border-radius: 20px;
200 padding: 8px 16px;
201 font-size: 14px;
202 background-color: var(--bg);
203 color: var(--text-primary);
204 outline: none;
205 font-family: inherit;
206 }
207
208 .thread-reply-input::placeholder {
209 color: var(--text-muted);
210 }
211
212 .thread-reply-input:focus {
213 border-color: var(--accent-primary);
214 }
215
216 .thread-reply-send {
217 width: 36px;
218 height: 36px;
219 border-radius: 50%;
220 border: none;
221 background-color: var(--accent-primary);
222 color: white;
223 cursor: pointer;
224 display: flex;
225 align-items: center;
226 justify-content: center;
227 transition: background-color 0.2s ease;
228 flex-shrink: 0;
229 }
230
231 .thread-reply-send:hover {
232 background-color: var(--accent-primary-hover);
233 }
234
235 .thread-reply-send svg {
236 width: 18px;
237 height: 18px;
238 }
239
240 /* Quoted post embed */
241 .thread-quote {
242 margin-top: 12px;
243 border: 1px solid var(--border);
244 border-radius: 12px;
245 padding: 12px;
246 }
247
248 .thread-quote-header {
249 display: flex;
250 align-items: center;
251 gap: 8px;
252 margin-bottom: 8px;
253 }
254
255 .thread-quote-avatar {
256 width: 20px;
257 height: 20px;
258 border-radius: 50%;
259 background-color: var(--surface-variant);
260 display: flex;
261 align-items: center;
262 justify-content: center;
263 font-size: 9px;
264 font-weight: 600;
265 color: var(--text-secondary);
266 }
267
268 .thread-quote-author {
269 font-size: 13px;
270 font-weight: 600;
271 color: var(--text-primary);
272 }
273
274 .thread-quote-text {
275 font-size: 13px;
276 color: var(--text-secondary);
277 line-height: 1.4;
278 }
279
280 /* Loading & error states */
281 .thread-loading {
282 display: flex;
283 flex-direction: column;
284 align-items: center;
285 justify-content: center;
286 padding: 64px 24px;
287 gap: 16px;
288 }
289
290 .thread-spinner {
291 width: 32px;
292 height: 32px;
293 border: 3px solid var(--border);
294 border-top-color: var(--accent-primary);
295 border-radius: 50%;
296 animation: spin 0.8s linear infinite;
297 }
298
299 @keyframes spin {
300 to {
301 transform: rotate(360deg);
302 }
303 }
304
305 .thread-error {
306 display: flex;
307 flex-direction: column;
308 align-items: center;
309 padding: 64px 24px;
310 text-align: center;
311 gap: 12px;
312 }
313
314 .thread-error-icon {
315 width: 48px;
316 height: 48px;
317 color: var(--text-muted);
318 }
319
320 .thread-error-title {
321 font-size: 16px;
322 font-weight: 600;
323 color: var(--text-primary);
324 }
325
326 .thread-error-text {
327 font-size: 14px;
328 color: var(--text-secondary);
329 max-width: 260px;
330 }
331
332 /* Extra bottom padding to account for reply bar */
333 .thread-container {
334 padding-bottom: 140px;
335 }
336
337 /* Back button */
338 .header-back {
339 background: none;
340 border: none;
341 color: var(--text-primary);
342 cursor: pointer;
343 padding: 8px;
344 margin-left: -8px;
345 display: flex;
346 align-items: center;
347 }
348
349 .header-back svg {
350 width: 24px;
351 height: 24px;
352 }
353
354 .header-left {
355 display: flex;
356 align-items: center;
357 gap: 8px;
358 }
359 </style>
360 </head>
361 <body>
362 <div class="mobile-container">
363 <!-- Header -->
364 <header class="header">
365 <div class="header-left">
366 <button class="header-back" title="Back">
367 <svg
368 viewBox="0 0 24 24"
369 fill="none"
370 stroke="currentColor"
371 stroke-width="2"
372 stroke-linecap="round"
373 stroke-linejoin="round">
374 <line x1="19" y1="12" x2="5" y2="12" />
375 <polyline points="12 19 5 12 12 5" />
376 </svg>
377 </button>
378 <h1 class="header-title" style="font-family: var(--font-heading)">Thread</h1>
379 </div>
380 <button class="header-action">
381 <svg
382 viewBox="0 0 24 24"
383 width="20"
384 height="20"
385 fill="none"
386 stroke="currentColor"
387 stroke-width="2"
388 stroke-linecap="round"
389 stroke-linejoin="round">
390 <circle cx="12" cy="12" r="1" />
391 <circle cx="19" cy="12" r="1" />
392 <circle cx="5" cy="12" r="1" />
393 </svg>
394 </button>
395 </header>
396
397 <div class="thread-container">
398 <!-- Parent Post (with thread line) -->
399 <div class="thread-line-wrapper">
400 <article class="post-card thread-parent">
401 <div class="post-header">
402 <div class="avatar">BJ</div>
403 <div class="post-author">
404 <div class="post-author-name">Bob Johnson</div>
405 <div class="post-author-handle">@bob.bsky.social · <span class="post-timestamp">6h</span></div>
406 </div>
407 </div>
408 <div class="post-content">
409 What are everyone's thoughts on the latest changes to the AT Protocol? Particularly interested in how
410 federation is being handled.
411 </div>
412 </article>
413
414 <div class="thread-parent-connector"></div>
415 </div>
416
417 <!-- Focused Post (the main post being viewed) -->
418 <article class="thread-focused">
419 <div class="post-header">
420 <div class="avatar">AS</div>
421 <div class="post-author">
422 <div class="post-author-name">Alice Smith</div>
423 <div class="post-author-handle">@alice.bsky.social</div>
424 </div>
425 </div>
426
427 <div class="post-content">
428 The federation work has been really impressive! The PDS model means you truly own your data. I've been
429 running my own server for a week now and it's been rock solid.
430 <br /><br />
431 Key things I've noticed:<br />
432 • Identity is portable across servers<br />
433 • The relay infrastructure scales well<br />
434 • App views make custom feeds possible
435 </div>
436
437 <!-- Quoted post embed -->
438 <div class="thread-quote">
439 <div class="thread-quote-header">
440 <div class="thread-quote-avatar">AT</div>
441 <span class="thread-quote-author">AT Protocol · @atproto.com</span>
442 </div>
443 <div class="thread-quote-text">
444 Federation is now live! Self-host your PDS and take control of your social data. Read more at
445 atproto.com/blog/federation
446 </div>
447 </div>
448
449 <div style="height: 16px"></div>
450
451 <div class="thread-focused-timestamp">2:34 PM · Mar 17, 2026</div>
452
453 <div class="thread-focused-stats">
454 <span class="thread-stat"><strong>24</strong> replies</span>
455 <span class="thread-stat"><strong>12</strong> reposts</span>
456 <span class="thread-stat"><strong>156</strong> likes</span>
457 <span class="thread-stat"><strong>8</strong> saves</span>
458 </div>
459
460 <div class="post-actions">
461 <!-- Reply -->
462 <button class="post-action">
463 <svg
464 viewBox="0 0 24 24"
465 fill="none"
466 stroke="currentColor"
467 stroke-width="2"
468 stroke-linecap="round"
469 stroke-linejoin="round">
470 <path
471 d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
472 </svg>
473 </button>
474 <!-- Repost (active) -->
475 <button class="post-action active-repost">
476 <svg
477 viewBox="0 0 24 24"
478 fill="none"
479 stroke="currentColor"
480 stroke-width="2"
481 stroke-linecap="round"
482 stroke-linejoin="round">
483 <polyline points="17 1 21 5 17 9" />
484 <path d="M3 11V9a4 4 0 0 1 4-4h14" />
485 <polyline points="7 23 3 19 7 15" />
486 <path d="M21 13v2a4 4 0 0 1-4 4H3" />
487 </svg>
488 </button>
489 <!-- Like (active) -->
490 <button class="post-action active-like">
491 <svg viewBox="0 0 24 24" fill="currentColor" stroke="none">
492 <path
493 d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
494 </svg>
495 </button>
496 <!-- Bookmark (active) -->
497 <button class="post-action active-bookmark">
498 <svg viewBox="0 0 24 24" fill="currentColor" stroke="none">
499 <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
500 </svg>
501 </button>
502 <!-- Share -->
503 <button class="post-action">
504 <svg
505 viewBox="0 0 24 24"
506 fill="none"
507 stroke="currentColor"
508 stroke-width="2"
509 stroke-linecap="round"
510 stroke-linejoin="round">
511 <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
512 <polyline points="16 6 12 2 8 6" />
513 <line x1="12" y1="2" x2="12" y2="15" />
514 </svg>
515 </button>
516 </div>
517 </article>
518
519 <!-- Replies -->
520 <div class="thread-replies-header">Replies</div>
521
522 <!-- Reply 1 -->
523 <article class="post-card thread-reply">
524 <div class="post-header">
525 <div class="avatar">CW</div>
526 <div class="post-author">
527 <div class="post-author-name">Carol White</div>
528 <div class="post-author-handle">@carol.dev · <span class="post-timestamp">4h</span></div>
529 </div>
530 </div>
531 <div class="post-content">
532 This is spot on! The portability aspect is what sold me. Being able to migrate my entire social presence
533 without losing followers is a game changer.
534 </div>
535 <div class="post-actions">
536 <button class="post-action">
537 <svg
538 viewBox="0 0 24 24"
539 fill="none"
540 stroke="currentColor"
541 stroke-width="2"
542 stroke-linecap="round"
543 stroke-linejoin="round">
544 <path
545 d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
546 </svg>
547 3
548 </button>
549 <button class="post-action">
550 <svg
551 viewBox="0 0 24 24"
552 fill="none"
553 stroke="currentColor"
554 stroke-width="2"
555 stroke-linecap="round"
556 stroke-linejoin="round">
557 <polyline points="17 1 21 5 17 9" />
558 <path d="M3 11V9a4 4 0 0 1 4-4h14" />
559 <polyline points="7 23 3 19 7 15" />
560 <path d="M21 13v2a4 4 0 0 1-4 4H3" />
561 </svg>
562 1
563 </button>
564 <button class="post-action active-like">
565 <svg viewBox="0 0 24 24" fill="currentColor" stroke="none">
566 <path
567 d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
568 </svg>
569 18
570 </button>
571 <button class="post-action">
572 <svg
573 viewBox="0 0 24 24"
574 fill="none"
575 stroke="currentColor"
576 stroke-width="2"
577 stroke-linecap="round"
578 stroke-linejoin="round">
579 <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
580 <polyline points="16 6 12 2 8 6" />
581 <line x1="12" y1="2" x2="12" y2="15" />
582 </svg>
583 </button>
584 </div>
585 </article>
586
587 <!-- Reply 2 (with nested sub-reply) -->
588 <article class="post-card thread-reply">
589 <div class="post-header">
590 <div class="avatar">DM</div>
591 <div class="post-author">
592 <div class="post-author-name">David Miller</div>
593 <div class="post-author-handle">@davidm.bsky.social · <span class="post-timestamp">3h</span></div>
594 </div>
595 </div>
596 <div class="post-content">
597 Have you run into any performance issues with your PDS? I'm considering self-hosting but worried about the
598 resource requirements.
599 </div>
600 <div class="post-actions">
601 <button class="post-action">
602 <svg
603 viewBox="0 0 24 24"
604 fill="none"
605 stroke="currentColor"
606 stroke-width="2"
607 stroke-linecap="round"
608 stroke-linejoin="round">
609 <path
610 d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
611 </svg>
612 1
613 </button>
614 <button class="post-action">
615 <svg
616 viewBox="0 0 24 24"
617 fill="none"
618 stroke="currentColor"
619 stroke-width="2"
620 stroke-linecap="round"
621 stroke-linejoin="round">
622 <polyline points="17 1 21 5 17 9" />
623 <path d="M3 11V9a4 4 0 0 1 4-4h14" />
624 <polyline points="7 23 3 19 7 15" />
625 <path d="M21 13v2a4 4 0 0 1-4 4H3" />
626 </svg>
627 </button>
628 <button class="post-action">
629 <svg
630 viewBox="0 0 24 24"
631 fill="none"
632 stroke="currentColor"
633 stroke-width="2"
634 stroke-linecap="round"
635 stroke-linejoin="round">
636 <path
637 d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
638 </svg>
639 5
640 </button>
641 <button class="post-action">
642 <svg
643 viewBox="0 0 24 24"
644 fill="none"
645 stroke="currentColor"
646 stroke-width="2"
647 stroke-linecap="round"
648 stroke-linejoin="round">
649 <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
650 <polyline points="16 6 12 2 8 6" />
651 <line x1="12" y1="2" x2="12" y2="15" />
652 </svg>
653 </button>
654 </div>
655 </article>
656
657 <!-- Reply 3: Author's own reply in thread -->
658 <article class="post-card thread-reply thread-reply-nested">
659 <div class="post-header">
660 <div class="avatar">AS</div>
661 <div class="post-author">
662 <div class="post-author-name">Alice Smith</div>
663 <div class="post-author-handle">@alice.bsky.social · <span class="post-timestamp">2h</span></div>
664 </div>
665 </div>
666 <div class="post-content">
667 Runs great on a small VPS! I'm using a 2GB instance and it handles everything smoothly. The docs have a good
668 guide on minimal setups.
669 </div>
670 <div class="post-actions">
671 <button class="post-action">
672 <svg
673 viewBox="0 0 24 24"
674 fill="none"
675 stroke="currentColor"
676 stroke-width="2"
677 stroke-linecap="round"
678 stroke-linejoin="round">
679 <path
680 d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
681 </svg>
682 </button>
683 <button class="post-action">
684 <svg
685 viewBox="0 0 24 24"
686 fill="none"
687 stroke="currentColor"
688 stroke-width="2"
689 stroke-linecap="round"
690 stroke-linejoin="round">
691 <polyline points="17 1 21 5 17 9" />
692 <path d="M3 11V9a4 4 0 0 1 4-4h14" />
693 <polyline points="7 23 3 19 7 15" />
694 <path d="M21 13v2a4 4 0 0 1-4 4H3" />
695 </svg>
696 </button>
697 <button class="post-action">
698 <svg
699 viewBox="0 0 24 24"
700 fill="none"
701 stroke="currentColor"
702 stroke-width="2"
703 stroke-linecap="round"
704 stroke-linejoin="round">
705 <path
706 d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
707 </svg>
708 12
709 </button>
710 <button class="post-action">
711 <svg
712 viewBox="0 0 24 24"
713 fill="none"
714 stroke="currentColor"
715 stroke-width="2"
716 stroke-linecap="round"
717 stroke-linejoin="round">
718 <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
719 <polyline points="16 6 12 2 8 6" />
720 <line x1="12" y1="2" x2="12" y2="15" />
721 </svg>
722 </button>
723 </div>
724 </article>
725
726 <!-- Reply 4 -->
727 <article class="post-card thread-reply">
728 <div class="post-header">
729 <div class="avatar">EL</div>
730 <div class="post-author">
731 <div class="post-author-name">Eva Lee</div>
732 <div class="post-author-handle">@eva.bsky.social · <span class="post-timestamp">1h</span></div>
733 </div>
734 </div>
735 <div class="post-content">
736 The custom feeds through app views are my favourite part. Being able to build your own algorithmic timeline
737 without depending on the platform is so empowering.
738 <a href="#" class="post-facet-hashtag">#atproto</a> <a href="#" class="post-facet-hashtag">#openweb</a>
739 </div>
740 <div class="post-actions">
741 <button class="post-action">
742 <svg
743 viewBox="0 0 24 24"
744 fill="none"
745 stroke="currentColor"
746 stroke-width="2"
747 stroke-linecap="round"
748 stroke-linejoin="round">
749 <path
750 d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
751 </svg>
752 2
753 </button>
754 <button class="post-action">
755 <svg
756 viewBox="0 0 24 24"
757 fill="none"
758 stroke="currentColor"
759 stroke-width="2"
760 stroke-linecap="round"
761 stroke-linejoin="round">
762 <polyline points="17 1 21 5 17 9" />
763 <path d="M3 11V9a4 4 0 0 1 4-4h14" />
764 <polyline points="7 23 3 19 7 15" />
765 <path d="M21 13v2a4 4 0 0 1-4 4H3" />
766 </svg>
767 4
768 </button>
769 <button class="post-action">
770 <svg
771 viewBox="0 0 24 24"
772 fill="none"
773 stroke="currentColor"
774 stroke-width="2"
775 stroke-linecap="round"
776 stroke-linejoin="round">
777 <path
778 d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
779 </svg>
780 31
781 </button>
782 <button class="post-action">
783 <svg
784 viewBox="0 0 24 24"
785 fill="none"
786 stroke="currentColor"
787 stroke-width="2"
788 stroke-linecap="round"
789 stroke-linejoin="round">
790 <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
791 <polyline points="16 6 12 2 8 6" />
792 <line x1="12" y1="2" x2="12" y2="15" />
793 </svg>
794 </button>
795 </div>
796 </article>
797 </div>
798
799 <!-- Reply Composer Bar -->
800 <div class="thread-reply-bar">
801 <div class="avatar" style="width: 32px; height: 32px; font-size: 12px">JD</div>
802 <input type="text" class="thread-reply-input" placeholder="Reply to Alice..." />
803 <button class="thread-reply-send" title="Send reply">
804 <svg
805 viewBox="0 0 24 24"
806 fill="none"
807 stroke="currentColor"
808 stroke-width="2"
809 stroke-linecap="round"
810 stroke-linejoin="round">
811 <line x1="22" y1="2" x2="11" y2="13" />
812 <polygon points="22 2 15 22 11 13 2 9 22 2" />
813 </svg>
814 </button>
815 </div>
816
817 <!-- Bottom Navigation -->
818 <nav class="nav-bar">
819 <a href="home.html" class="nav-item">
820 <svg
821 viewBox="0 0 24 24"
822 fill="none"
823 stroke="currentColor"
824 stroke-width="2"
825 stroke-linecap="round"
826 stroke-linejoin="round">
827 <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
828 <polyline points="9 22 9 12 15 12 15 22" />
829 </svg>
830 <span>Home</span>
831 </a>
832
833 <a href="search.html" class="nav-item">
834 <svg
835 viewBox="0 0 24 24"
836 fill="none"
837 stroke="currentColor"
838 stroke-width="2"
839 stroke-linecap="round"
840 stroke-linejoin="round">
841 <circle cx="11" cy="11" r="8" />
842 <line x1="21" y1="21" x2="16.65" y2="16.65" />
843 </svg>
844 <span>Search</span>
845 </a>
846
847 <a href="notifications.html" class="nav-item">
848 <svg
849 viewBox="0 0 24 24"
850 fill="none"
851 stroke="currentColor"
852 stroke-width="2"
853 stroke-linecap="round"
854 stroke-linejoin="round">
855 <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
856 <path d="M13.73 21a2 2 0 0 1-3.46 0" />
857 </svg>
858 <span>Alerts</span>
859 </a>
860
861 <a href="messages.html" class="nav-item">
862 <svg
863 viewBox="0 0 24 24"
864 fill="none"
865 stroke="currentColor"
866 stroke-width="2"
867 stroke-linecap="round"
868 stroke-linejoin="round">
869 <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
870 </svg>
871 <span>Chat</span>
872 </a>
873
874 <a href="profile.html" class="nav-item">
875 <svg
876 viewBox="0 0 24 24"
877 fill="none"
878 stroke="currentColor"
879 stroke-width="2"
880 stroke-linecap="round"
881 stroke-linejoin="round">
882 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
883 <circle cx="12" cy="7" r="4" />
884 </svg>
885 <span>Profile</span>
886 </a>
887 </nav>
888 </div>
889
890 <script>
891 if (localStorage.getItem("theme")) {
892 const t = localStorage.getItem("theme");
893 if (t !== "light") document.documentElement.setAttribute("data-theme", t);
894 }
895 </script>
896 </body>
897</html>