interactive intro to open social at-me.zzstoatzz.io

fix: add home button for navigation back to landing page

- added home button at top right corner
- positioned to the right of watch-live button
- provides navigation back to search/landing page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+96 -150
src
+96 -150
src/templates.rs
··· 1 - pub fn login_page() -> &'static str { 1 + pub fn landing_page() -> &'static str { 2 2 r#" 3 3 <!DOCTYPE html> 4 4 <html> ··· 102 102 z-index: 10; 103 103 } 104 104 105 - .login-card { 105 + .search-card { 106 106 background: transparent; 107 107 border: 1px solid rgba(255, 255, 255, 0.1); 108 108 padding: 2.5rem 3rem; 109 109 border-radius: 8px; 110 110 backdrop-filter: blur(2px); 111 111 text-align: center; 112 - box-shadow: none; 113 - max-width: min(450px, 90vw); 112 + max-width: min(500px, 90vw); 114 113 } 115 114 116 115 h1 { ··· 168 167 border-color: rgba(255, 255, 255, 0.4); 169 168 } 170 169 171 - .hidden { display: none; } 172 - .loading { color: rgba(255, 255, 255, 0.5); font-size: 0.9rem; } 173 - 174 - .demo-btn { 175 - font-family: inherit; 176 - font-size: 0.9rem; 177 - padding: 0.75rem 2rem; 178 - cursor: pointer; 179 - background: transparent; 180 - border: 1px solid rgba(255, 255, 255, 0.15); 181 - border-radius: 4px; 182 - color: rgba(255, 255, 255, 0.6); 183 - transition: all 0.2s; 184 - width: 100%; 185 - margin-top: 0.75rem; 186 - } 187 - 188 - .demo-btn:hover { 189 - background: rgba(10, 10, 15, 0.5); 190 - border-color: rgba(255, 255, 255, 0.3); 191 - color: rgba(255, 255, 255, 0.8); 192 - } 193 - 194 170 .divider { 195 171 display: flex; 196 172 align-items: center; ··· 208 184 background: rgba(255, 255, 255, 0.1); 209 185 } 210 186 187 + .suggestions { 188 + display: flex; 189 + gap: 0.75rem; 190 + flex-wrap: wrap; 191 + justify-content: center; 192 + } 193 + 194 + .suggestion-btn { 195 + font-family: inherit; 196 + font-size: 0.8rem; 197 + padding: 0.5rem 1rem; 198 + cursor: pointer; 199 + background: transparent; 200 + border: 1px solid rgba(255, 255, 255, 0.15); 201 + border-radius: 4px; 202 + color: rgba(255, 255, 255, 0.6); 203 + transition: all 0.2s; 204 + width: auto; 205 + } 206 + 207 + .suggestion-btn:hover { 208 + background: rgba(10, 10, 15, 0.5); 209 + border-color: rgba(255, 255, 255, 0.3); 210 + color: rgba(255, 255, 255, 0.8); 211 + } 212 + 211 213 .info-toggle { 212 214 margin-top: 1.5rem; 213 215 color: rgba(255, 255, 255, 0.5); ··· 301 303 <div class="atmosphere" id="atmosphere"></div> 302 304 303 305 <div class="container"> 304 - <div class="login-card"> 305 - <div id="restoring" class="loading hidden">restoring session...</div> 306 - <form id="loginForm" method="post" action="/login"> 307 - <h1>@me</h1> 308 - <div class="subtitle">explore the atmosphere</div> 309 - <input type="text" name="handle" placeholder="handle.bsky.social" required autofocus> 310 - <button type="submit">enter</button> 306 + <div class="search-card"> 307 + <h1>@me</h1> 308 + <div class="subtitle">explore the atmosphere</div> 309 + <form id="searchForm" onsubmit="event.preventDefault(); handleSearch();"> 310 + <input type="text" id="handleInput" placeholder="enter any handle" autofocus> 311 + <button type="submit">explore</button> 312 + </form> 311 313 312 - <div class="divider">or</div> 313 - <button type="button" class="demo-btn" id="demoBtn">explore demo</button> 314 + <div class="divider">try these</div> 315 + <div class="suggestions"> 316 + <button class="suggestion-btn" onclick="viewHandle('pfrazee.com')">pfrazee.com</button> 317 + <button class="suggestion-btn" onclick="viewHandle('jay.bsky.team')">jay.bsky.team</button> 318 + <button class="suggestion-btn" onclick="viewHandle('why.bsky.team')">why.bsky.team</button> 319 + </div> 314 320 315 - <button type="button" class="info-toggle" id="infoToggle">what is this?</button> 321 + <button type="button" class="info-toggle" onclick="toggleInfo()">what is this?</button> 316 322 317 - <div class="info-content" id="infoContent"> 318 - <div class="info-section"> 319 - <h3>your posts should be yours</h3> 320 - <p>built 10k followers? wrote years of posts? on most platforms, if you leave, you lose it all. they own your network, not you.</p> 323 + <div class="info-content" id="infoContent"> 324 + <div class="info-section"> 325 + <h3>your posts should be yours</h3> 326 + <p>built 10k followers? wrote years of posts? on most platforms, if you leave, you lose it all. they own your network, not you.</p> 321 327 322 - <h3>what if social media worked like email?</h3> 323 - <p>with email, you can switch from gmail to protonmail and keep your contacts. same idea here: your posts and followers live on your own server (called a <a href="https://atproto.com/guides/data-repos" target="_blank" rel="noopener noreferrer">Personal Data Server</a>). apps like bluesky just connect to it.</p> 328 + <h3>what if social media worked like email?</h3> 329 + <p>with email, you can switch from gmail to protonmail and keep your contacts. same idea here: your posts and followers live on your own server (called a <a href="https://atproto.com/guides/data-repos" target="_blank" rel="noopener noreferrer">Personal Data Server</a>). apps like bluesky just connect to it.</p> 324 330 325 - <h3>see it in action</h3> 326 - <p>click "explore demo" to see how <a href="https://atproto.com" target="_blank" rel="noopener noreferrer">atproto</a> stores someone's real social data, or log in to explore your own.</p> 327 - </div> 331 + <h3>see it in action</h3> 332 + <p>enter any handle above to see how <a href="https://atproto.com" target="_blank" rel="noopener noreferrer">atproto</a> stores their social data.</p> 328 333 </div> 329 - </form> 334 + </div> 330 335 </div> 331 336 </div> 332 337 ··· 334 339 by <a href="https://bsky.app/profile/zzstoatzz.io" target="_blank" rel="noopener noreferrer">@zzstoatzz.io</a> 335 340 </div> 336 341 337 - <script src="/static/login.js"></script> 342 + <script> 343 + function handleSearch() { 344 + const handle = document.getElementById('handleInput').value.trim(); 345 + if (handle) { 346 + viewHandle(handle); 347 + } 348 + } 349 + 350 + function viewHandle(handle) { 351 + window.location.href = '/view?handle=' + encodeURIComponent(handle); 352 + } 353 + 354 + function toggleInfo() { 355 + document.getElementById('infoContent').classList.toggle('expanded'); 356 + } 357 + </script> 338 358 </body> 339 359 </html> 340 360 "# 341 361 } 342 362 343 - pub fn app_page(did: &str, demo_mode: bool, demo_handle: Option<&str>) -> String { 344 - let demo_banner = if demo_mode && demo_handle.is_some() { 345 - format!(r#" 346 - <div class="demo-banner" id="demoBanner"> 347 - <span>demo mode - viewing <strong>{}</strong></span> 348 - <a href="/demo/exit" class="demo-exit">exit demo</a> 349 - </div>"#, demo_handle.unwrap()) 350 - } else { 351 - String::new() 352 - }; 353 - 363 + pub fn app_page(did: &str) -> String { 354 364 format!(r#" 355 365 <!DOCTYPE html> 356 366 <html> ··· 417 427 display: flex; 418 428 align-items: center; 419 429 justify-content: center; 420 - }} 421 - 422 - .logout {{ 423 - position: fixed; 424 - top: clamp(1rem, 2vmin, 1.5rem); 425 - right: clamp(1rem, 2vmin, 1.5rem); 426 - font-size: clamp(0.65rem, 1.4vmin, 0.75rem); 427 - color: var(--text-light); 428 - text-decoration: none; 429 - border: 1px solid var(--border); 430 - padding: clamp(0.4rem, 1vmin, 0.5rem) clamp(0.8rem, 2vmin, 1rem); 431 - transition: all 0.2s ease; 432 - z-index: 100; 433 - -webkit-tap-highlight-color: transparent; 434 - cursor: pointer; 435 - border-radius: 2px; 436 - display: flex; 437 - align-items: center; 438 - }} 439 - 440 - .logout:hover, .logout:active {{ 441 - background: var(--surface); 442 - color: var(--text); 443 - border-color: var(--text-light); 444 430 }} 445 431 446 432 .info {{ ··· 1325 1311 color: var(--text); 1326 1312 }} 1327 1313 1314 + .home-btn {{ 1315 + position: fixed; 1316 + top: clamp(1rem, 2vmin, 1.5rem); 1317 + right: clamp(1rem, 2vmin, 1.5rem); 1318 + font-family: inherit; 1319 + font-size: clamp(0.65rem, 1.4vmin, 0.75rem); 1320 + color: var(--text-light); 1321 + border: 1px solid var(--border); 1322 + background: var(--bg); 1323 + padding: clamp(0.4rem, 1vmin, 0.5rem) clamp(0.8rem, 2vmin, 1rem); 1324 + transition: all 0.2s ease; 1325 + z-index: 100; 1326 + cursor: pointer; 1327 + border-radius: 2px; 1328 + text-decoration: none; 1329 + display: inline-flex; 1330 + align-items: center; 1331 + }} 1332 + 1333 + .home-btn:hover {{ 1334 + background: var(--surface); 1335 + color: var(--text); 1336 + border-color: var(--text-light); 1337 + }} 1338 + 1328 1339 .watch-live-btn {{ 1329 1340 position: fixed; 1330 1341 top: clamp(1rem, 2vmin, 1.5rem); ··· 1438 1449 max-width: none; 1439 1450 }} 1440 1451 }} 1441 - 1442 - .demo-banner {{ 1443 - position: fixed; 1444 - top: 0; 1445 - left: 0; 1446 - right: 0; 1447 - background: rgba(255, 165, 0, 0.15); 1448 - border-bottom: 1px solid rgba(255, 165, 0, 0.3); 1449 - padding: 0.5rem 1rem; 1450 - display: flex; 1451 - align-items: center; 1452 - justify-content: center; 1453 - gap: 1rem; 1454 - z-index: 200; 1455 - font-size: 0.7rem; 1456 - color: var(--text); 1457 - }} 1458 - 1459 - .demo-banner strong {{ 1460 - color: var(--text); 1461 - font-weight: 600; 1462 - }} 1463 - 1464 - .demo-exit {{ 1465 - color: var(--text-light); 1466 - text-decoration: none; 1467 - border: 1px solid var(--border); 1468 - padding: 0.25rem 0.75rem; 1469 - border-radius: 2px; 1470 - transition: all 0.2s ease; 1471 - font-size: 0.65rem; 1472 - }} 1473 - 1474 - .demo-exit:hover {{ 1475 - background: var(--surface); 1476 - border-color: var(--text-light); 1477 - color: var(--text); 1478 - }} 1479 - 1480 - @media (prefers-color-scheme: dark) {{ 1481 - .demo-banner {{ 1482 - background: rgba(255, 165, 0, 0.1); 1483 - border-bottom-color: rgba(255, 165, 0, 0.25); 1484 - }} 1485 - }} 1486 - 1487 - /* Adjust elements when demo banner is present */ 1488 - .demo-banner ~ .info {{ 1489 - top: calc(clamp(1rem, 2vmin, 1.5rem) + 2.5rem); 1490 - }} 1491 - 1492 - .demo-banner ~ .watch-live-btn {{ 1493 - top: calc(clamp(1rem, 2vmin, 1.5rem) + 2.5rem); 1494 - }} 1495 - 1496 - .demo-banner ~ .logout {{ 1497 - top: calc(clamp(1rem, 2vmin, 1.5rem) + 2.5rem); 1498 - }} 1499 - 1500 - @media (max-width: 768px) {{ 1501 - .demo-banner ~ .watch-live-btn {{ 1502 - top: calc(clamp(4rem, 8vmin, 5rem) + 2.5rem); 1503 - }} 1504 - }} 1505 1452 </style> 1506 1453 </head> 1507 1454 <body> 1508 - {} 1509 1455 <div class="info" id="infoBtn">?</div> 1456 + <a href="/" class="home-btn">home</a> 1510 1457 <button class="watch-live-btn" id="watchLiveBtn"> 1511 1458 <span class="watch-indicator"></span> 1512 1459 <span class="watch-label">watch live</span> 1513 1460 </button> 1514 - <a href="javascript:void(0)" id="logoutBtn" class="logout">logout</a> 1515 1461 1516 1462 <div class="firehose-toast" id="firehoseToast"> 1517 1463 <div class="firehose-toast-action"></div> ··· 1556 1502 <script src="/static/onboarding.js"></script> 1557 1503 </body> 1558 1504 </html> 1559 - "#, demo_banner, did) 1505 + "#, did) 1560 1506 }