···
.hidden { display: none; }
.loading { color: rgba(255, 255, 255, 0.5); font-size: 0.9rem; }
+
background: transparent;
+
border: 1px solid rgba(255, 255, 255, 0.15);
+
color: rgba(255, 255, 255, 0.6);
+
background: rgba(10, 10, 15, 0.5);
+
border-color: rgba(255, 255, 255, 0.3);
+
color: rgba(255, 255, 255, 0.8);
+
color: rgba(255, 255, 255, 0.3);
+
background: rgba(255, 255, 255, 0.1);
+
color: rgba(255, 255, 255, 0.5);
+
transition: color 0.2s;
+
text-decoration: underline;
+
text-underline-offset: 2px;
+
color: rgba(255, 255, 255, 0.8);
+
transition: max-height 0.3s ease;
+
.info-content.expanded {
+
background: rgba(10, 10, 15, 0.6);
+
border: 1px solid rgba(255, 255, 255, 0.1);
+
margin-bottom: 0.75rem;
+
color: rgba(255, 255, 255, 0.9);
+
color: rgba(255, 255, 255, 0.6);
+
.info-section p:last-child {
+
color: rgba(255, 255, 255, 0.85);
+
color: rgba(255, 255, 255, 0.8);
+
text-decoration: underline;
+
text-underline-offset: 2px;
+
.info-section a:hover {
+
color: rgba(255, 255, 255, 1);
···
<div class="subtitle">explore the atmosphere</div>
<input type="text" name="handle" placeholder="handle.bsky.social" required autofocus>
<button type="submit">enter</button>
+
<div class="divider">or</div>
+
<button type="button" class="demo-btn" id="demoBtn">explore demo</button>
+
<button type="button" class="info-toggle" id="infoToggle">what is this?</button>
+
<div class="info-content" id="infoContent">
+
<div class="info-section">
+
<h3>visualize your atproto identity</h3>
+
<p>see all the apps writing to your <strong>Personal Data Server</strong> and explore the records they've created. your content, your server, your control.</p>
+
<h3>the problem with silos</h3>
+
<p>traditional social platforms lock your content in. switching means starting over, losing your network and history. you build their platform, they control everything.</p>
+
<h3>the atproto solution</h3>
+
<p>on <a href="https://atproto.com" target="_blank" rel="noopener noreferrer">atproto</a>, you own your data. it lives on <strong>your</strong> server. apps like bluesky, whitewind, and frontpage just read and write to your space. switch apps anytime, take everything with you.</p>
+
<h3>see it yourself</h3>
+
<p>this isn't just theory. click "explore demo" to see a real atproto account, or log in to visualize your own identity.</p>
···
+
pub fn app_page(did: &str, demo_mode: bool, demo_handle: Option<&str>) -> String {
+
let demo_banner = if demo_mode && demo_handle.is_some() {
+
<div class="demo-banner" id="demoBanner">
+
<span>demo mode - viewing <strong>{}</strong></span>
+
<a href="/demo/exit" class="demo-exit">exit demo</a>
+
</div>"#, demo_handle.unwrap())
···
+
background: rgba(255, 165, 0, 0.15);
+
border-bottom: 1px solid rgba(255, 165, 0, 0.3);
+
justify-content: center;
+
color: var(--text-light);
+
border: 1px solid var(--border);
+
padding: 0.25rem 0.75rem;
+
transition: all 0.2s ease;
+
background: var(--surface);
+
border-color: var(--text-light);
+
@media (prefers-color-scheme: dark) {{
+
background: rgba(255, 165, 0, 0.1);
+
border-bottom-color: rgba(255, 165, 0, 0.25);
+
/* Adjust elements when demo banner is present */
+
.demo-banner ~ .info {{
+
top: calc(clamp(1rem, 2vmin, 1.5rem) + 2.5rem);
+
.demo-banner ~ .watch-live-btn {{
+
top: calc(clamp(1rem, 2vmin, 1.5rem) + 2.5rem);
+
.demo-banner ~ .logout {{
+
top: calc(clamp(1rem, 2vmin, 1.5rem) + 2.5rem);
+
@media (max-width: 768px) {{
+
.demo-banner ~ .watch-live-btn {{
+
top: calc(clamp(4rem, 8vmin, 5rem) + 2.5rem);
<div class="info" id="infoBtn">?</div>
<button class="watch-live-btn" id="watchLiveBtn">
<span class="watch-indicator"></span>
···
<script src="/static/onboarding.js"></script>