bzl

self-hosted ephemeral community engine
Log | Files | Refs | README | LICENSE

index.html (44398B)


      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" />
      6     <title>Bzl - Hives</title>
      7     <link rel="stylesheet" href="/styles.css?v=136" />
      8   </head>
      9   <body>
     10       <div id="bzlSplash" class="bzlSplash">
     11         <div class="bzlSplashFx" aria-hidden="true"></div>
     12         <div class="bzlSplashCore">
     13           <div class="bzlSplashRing"></div>
     14           <img class="bzlSplashLogo" src="/assets/logobzl.png" alt="Bzl logo" />
     15           <div class="bzlSplashWord">Bzl</div>
     16           <div id="bzlSplashTip" class="bzlSplashTag small muted">Loading your hive...</div>
     17           <div class="bzlSplashProgress" aria-hidden="true">
     18             <div id="bzlSplashProgressFill" class="bzlSplashProgressFill"></div>
     19           </div>
     20           <button id="bzlSplashStartBtn" class="primary smallBtn bzlSplashStartBtn hidden" type="button">Click to enter</button>
     21         </div>
     22       </div>
     23       <div class="app">
     24         <button id="showSidebar" class="ghost smallBtn sidebarToggle hidden" type="button" title="Show sidebar">Show</button>
     25         <button id="togglePeople" class="ghost smallBtn peopleToggle" type="button" title="Show members list">Members</button>
     26         <button id="showRightRack" class="ghost smallBtn rightRackToggle hidden" type="button" title="Show members list">Members</button>
     27         <aside class="sidebar">
     28         <div class="sidebarScroll">
     29           <div class="brand">
     30             <div id="instanceTitle" class="logo">Bzl</div>
     31             <div id="instanceSubtitle" class="subtitle">Ephemeral hives + chat</div>
     32           </div>
     33 
     34         <div class="statusBlock">
     35           <div id="connBadge" class="badge badge-warn">Connecting...</div>
     36           <div class="row">
     37             <button id="enableNotifs" class="ghost smallBtn grow" type="button">Enable notifications</button>
     38           </div>
     39           <div id="notifStatus" class="small muted"></div>
     40           <div class="notifOptions">
     41             <label class="checkRow notifCheckRow">
     42               <span>Alert sound (B7)</span>
     43               <input id="notifSoundToggle" type="checkbox" />
     44             </label>
     45             <label class="checkRow notifCheckRow">
     46               <span>Any new hive</span>
     47               <input id="notifNewHiveToggle" type="checkbox" />
     48             </label>
     49             <label class="checkRow notifCheckRow">
     50               <span>Replies and pings</span>
     51               <input id="notifReplyPingToggle" type="checkbox" />
     52             </label>
     53             <label class="checkRow notifCheckRow">
     54               <span>Chats on my hives</span>
     55               <input id="notifMyHiveChatsToggle" type="checkbox" />
     56             </label>
     57             <label class="checkRow notifCheckRow">
     58               <span>Chats where I posted recently</span>
     59               <input id="notifRecentHiveChatsToggle" type="checkbox" />
     60             </label>
     61           </div>
     62         </div>
     63 
     64         <section id="viewPanel" class="panel">
     65           <div class="panelTitle">View</div>
     66           <div class="uiHint">Use layout presets for quick panel setups. Shortcuts: <b>[</b>/<b>]</b> cycle presets, <b>-</b>/<b>=</b> cycle hives/chats in the active panel, <b>R</b> toggles Members list, <b>?</b> opens shortcut help.</div>
     67           <label class="checkRow" style="margin-top:8px;">
     68             <span>Rack layout (experimental)</span>
     69             <input id="toggleRackLayout" type="checkbox" />
     70           </label>
     71           <label style="margin-top:10px;">
     72             <span>Layout preset</span>
     73             <select id="layoutPreset">
     74               <option value="discordLike">Discord-like</option>
     75               <option value="chat">Chat</option>
     76               <option value="browsing">Browsing</option>
     77               <option value="maps">Maps</option>
     78               <option value="moderation">Moderation</option>
     79               <option value="focus">Focus</option>
     80               <option value="clean">Clean</option>
     81               <option value="ops">Ops</option>
     82             </select>
     83           </label>
     84           <label class="checkRow" style="margin-top:8px;">
     85             <span>Side panels</span>
     86             <input id="toggleSideRack" type="checkbox" checked />
     87           </label>
     88           <label class="checkRow" style="margin-top:8px;">
     89             <span>Members list</span>
     90             <input id="toggleRightRack" type="checkbox" checked />
     91           </label>
     92           <label class="checkRow" style="margin-top:8px;">
     93             <span>Show reactions bar</span>
     94             <input id="toggleReactions" type="checkbox" />
     95           </label>
     96           <label class="checkRow" style="margin-top:8px;">
     97             <span>Stay connected</span>
     98             <input id="stayConnected" type="checkbox" />
     99           </label>
    100           <label class="checkRow" style="margin-top:8px;">
    101             <span>Enable hints</span>
    102             <input id="enableHints" type="checkbox" />
    103           </label>
    104           <label style="margin-top:10px;">
    105             <span>Chat send key</span>
    106             <select id="chatEnterMode">
    107               <option value="ctrlEnter">Ctrl/Cmd + Enter sends</option>
    108               <option value="enter">Enter sends (Shift+Enter newline)</option>
    109             </select>
    110           </label>
    111           <div class="row" style="margin-top:8px; gap:8px; flex-wrap:wrap;">
    112             <button id="openShortcutHelp" class="ghost smallBtn" type="button">Shortcut help</button>
    113             <button id="resetCurrentLayout" class="ghost smallBtn" type="button">Reset layout</button>
    114           </div>
    115 
    116           <details style="margin-top:10px;">
    117             <summary class="small muted" style="cursor:pointer;user-select:none;">Advanced display</summary>
    118             <div style="margin-top:10px;">
    119               <label>
    120                 <span>Text size</span>
    121                 <select id="uiScale">
    122                   <option value="auto" selected>Auto</option>
    123                   <option value="xs">Compact</option>
    124                   <option value="sm">Small</option>
    125                   <option value="md">Default</option>
    126                   <option value="lg">Large</option>
    127                 </select>
    128               </label>
    129               <div class="appearanceWorkbench">
    130                 <div class="appearanceWorkbenchHeader">
    131                   <div class="small">Look & feel</div>
    132                   <div class="small muted">Personal (this browser)</div>
    133                 </div>
    134                 <div class="appearanceWorkbenchRow">
    135                   <label class="grow">
    136                     <span>Theme preset</span>
    137                     <select id="appearancePreset">
    138                       <option value="">(choose...)</option>
    139                     </select>
    140                   </label>
    141                   <div class="row" style="align-items:flex-end;gap:8px;">
    142                     <button id="appearanceApplyPreset" class="ghost smallBtn" type="button">Apply</button>
    143                     <button id="appearanceResetPreview" class="ghost smallBtn" type="button">Reset</button>
    144                   </div>
    145                 </div>
    146                 <div class="appearanceColorGrid">
    147                   <label>
    148                     <span>Background</span>
    149                     <input id="appearanceBg" type="color" />
    150                   </label>
    151                   <label>
    152                     <span>Panel</span>
    153                     <input id="appearancePanel" type="color" />
    154                   </label>
    155                   <label>
    156                     <span>Text</span>
    157                     <input id="appearanceText" type="color" />
    158                   </label>
    159                   <label>
    160                     <span>Accent</span>
    161                     <input id="appearanceAccent" type="color" />
    162                   </label>
    163                   <label>
    164                     <span>Accent 2</span>
    165                     <input id="appearanceAccent2" type="color" />
    166                   </label>
    167                   <label>
    168                     <span>Success</span>
    169                     <input id="appearanceGood" type="color" />
    170                   </label>
    171                   <label>
    172                     <span>Danger</span>
    173                     <input id="appearanceBad" type="color" />
    174                   </label>
    175                 </div>
    176                 <div class="appearanceWorkbenchRow">
    177                   <label>
    178                     <span>Muted %</span>
    179                     <input id="appearanceMutedPct" type="number" min="0" max="100" />
    180                   </label>
    181                   <label>
    182                     <span>Divider %</span>
    183                     <input id="appearanceLinePct" type="number" min="0" max="100" />
    184                   </label>
    185                   <label>
    186                     <span>Panel tint %</span>
    187                     <input id="appearancePanel2Pct" type="number" min="0" max="100" />
    188                   </label>
    189                 </div>
    190                 <div class="appearanceWorkbenchRow">
    191                   <label class="grow">
    192                     <span>Body font</span>
    193                     <select id="appearanceFontBody">
    194                       <option value="system">System (sans)</option>
    195                       <option value="clean">Clean sans</option>
    196                       <option value="humanist">Humanist</option>
    197                       <option value="rounded">Rounded</option>
    198                       <option value="condensed">Condensed</option>
    199                       <option value="serif">Serif</option>
    200                       <option value="slab">Slab serif</option>
    201                       <option value="mono">Monospace</option>
    202                       <option value="lcd">LCD / Digital</option>
    203                     </select>
    204                   </label>
    205                   <label class="grow">
    206                     <span>Mono font</span>
    207                     <select id="appearanceFontMono">
    208                       <option value="mono">Monospace</option>
    209                       <option value="system">System</option>
    210                       <option value="clean">Clean sans</option>
    211                       <option value="humanist">Humanist</option>
    212                       <option value="rounded">Rounded</option>
    213                       <option value="lcd">LCD / Digital</option>
    214                     </select>
    215                   </label>
    216                 </div>
    217                 <div class="appearanceWorkbenchActions">
    218                   <button id="appearanceSave" class="primary smallBtn" type="button">Save look</button>
    219                   <button id="appearanceClear" class="ghost smallBtn" type="button">Use server default</button>
    220                 </div>
    221                 <div id="appearanceStatus" class="small muted"></div>
    222               </div>
    223             </div>
    224           </details>
    225         </section>
    226 
    227         <section id="accountPanel" class="panel">
    228           <div class="panelTitle">Account</div>
    229           <div class="small muted" id="authHint">Sign in to post, chat, and boost.</div>
    230           <div class="uiHint">New here: create an account, then open a hive and tap <b>Chat</b> to join a conversation.</div>
    231 
    232           <div class="small muted">Signed in as</div>
    233           <div id="userLabel" class="userLine">Signed out</div>
    234 
    235           <form id="authForm" class="form">
    236             <label>
    237               <span>Username</span>
    238               <input id="authUser" autocomplete="username" />
    239             </label>
    240             <label>
    241               <span>Password</span>
    242               <input id="authPass" type="password" autocomplete="current-password" />
    243             </label>
    244             <label id="codeRow" class="hidden">
    245               <span>Registration code</span>
    246               <input id="authCode" autocomplete="one-time-code" />
    247             </label>
    248             <div class="row">
    249               <button class="primary grow" type="submit">Sign in</button>
    250               <button id="registerBtn" class="ghost" type="button">Create account</button>
    251             </div>
    252             <button id="tourBtn" class="ghost" type="button">Tour</button>
    253             <button id="logoutBtn" class="ghost hidden" type="button">Sign out</button>
    254             <div class="small muted">
    255               Note: this is a prototype; don't reuse important passwords.
    256             </div>
    257           </form>
    258           <div id="onboardingCard" class="onboardingCard hidden">
    259             <div class="panelTitle">Onboarding</div>
    260             <div id="onboardingBody" class="small muted"></div>
    261             <div class="row" style="margin-top:8px;">
    262               <button id="onboardingAccept" class="primary grow hidden" type="button">Accept and continue</button>
    263               <button id="onboardingRefresh" class="ghost" type="button">Refresh</button>
    264             </div>
    265           </div>
    266         </section>
    267 
    268         <section id="profilePanel" class="panel">
    269           <div class="panelTitle">Profile</div>
    270           <div class="small muted">Set how your name appears.</div>
    271           <div class="uiHint">Your profile card appears in People and when someone opens your profile from a post or chat.</div>
    272           <label>
    273             <span>Profile picture</span>
    274             <input id="profileImage" type="file" accept="image/*" />
    275           </label>
    276           <div class="row">
    277             <div class="pfpBox">
    278               <img id="profilePreview" alt="Profile preview" />
    279             </div>
    280             <div class="grow">
    281               <label>
    282                 <span>Color</span>
    283                 <input id="nameColor" type="color" value="#ff3ea5" />
    284               </label>
    285               <button id="removeProfileImage" class="ghost" type="button">Remove picture</button>
    286             </div>
    287           </div>
    288           <button id="saveProfile" class="primary" type="button">Save profile</button>
    289           <div id="profileStatus" class="small muted"></div>
    290         </section>
    291 
    292         </div>
    293 
    294         <div class="sidebarFooter">
    295           <a
    296             id="poweredByTileLink"
    297             class="poweredByTile"
    298             aria-label="Powered by Bzl"
    299             href="https://bzl.one"
    300             target="_blank"
    301             rel="noopener noreferrer"
    302             title="Visit bzl.one"
    303           >
    304             <img class="poweredByLogo" src="/assets/logobzl.png" alt="Bzl logo" />
    305             <div class="poweredByText">
    306               <div class="poweredByTitle">Powered by Bzl</div>
    307               <div class="poweredByByline">by Azakaela Erin Redfire</div>
    308               <div class="poweredByVersion" id="poweredByVersion"></div>
    309             </div>
    310           </a>
    311           <button id="toggleSidebar" class="ghost smallBtn" type="button" title="Hide sidebar">Hide</button>
    312         </div>
    313       </aside>
    314 
    315       <div id="sidebarResizeHandle" class="panelResizeHandle sidebarResizeHandle" title="Drag to resize sidebar" aria-hidden="true"></div>
    316 
    317       <main class="main">
    318         <div id="mainRack" class="mainRack">
    319           <button id="showSideRack" class="ghost smallBtn sideRackToggle hidden" type="button" title="Show side panels">Side</button>
    320           <div id="mainWorkspaceRack" class="workspaceRack" aria-label="Workspace">
    321         <section id="hivesPanel" class="panel panelFill">
    322           <div class="panelHeader">
    323             <div class="panelTitle">Hives</div>
    324             <div class="filters">
    325               <input id="filterAuthor" placeholder="Filter by author (@name)" />
    326               <input id="filterKeywords" placeholder="Filter by keywords (comma separated)" />
    327               <select id="sortBy">
    328                 <option value="activity">Most recent activity</option>
    329                 <option value="popular">Most popular</option>
    330                 <option value="expiring">About to expire</option>
    331               </select>
    332               <button id="mobileHiveSearch" class="ghost smallBtn mobileOnlyHiveControl" type="button" title="Search hives">🔎</button>
    333               <button id="mobileSortCycle" class="ghost smallBtn mobileOnlyHiveControl" type="button" title="Cycle sort">Recent</button>
    334               <button id="clearFilter" type="button">Clear</button>
    335               <button id="toggleComposer" class="mobileComposerToggle" type="button">New Hive</button>
    336             </div>
    337           </div>
    338           <div class="uiHint">Use filters to narrow posts, then tap <b>Chat</b> on a hive card. Cards now show latest chat/typing. Shortcut: <b>-</b>/<b>=</b> cycles collections/views.</div>
    339           <div id="onboardingGateHint" class="onboardingGateHint hidden"></div>
    340           <div class="hiveTabs" id="hiveTabs">
    341             <button type="button" data-hiveview="all" class="primary">All</button>
    342             <button type="button" data-hiveview="starred" class="ghost">Starred</button>
    343             <button type="button" data-hiveview="hidden" class="ghost">Hidden</button>
    344           </div>
    345           <div id="feed" class="feed"></div>
    346         </section>
    347 
    348         <section id="onboardingPanel" class="panel panelFill hidden">
    349           <div class="panelHeader">
    350             <div>
    351               <div class="panelTitle">Onboarding</div>
    352               <div class="small muted">About, rules, and first steps.</div>
    353             </div>
    354           </div>
    355           <div class="panelBody onboardingPanelBody">
    356             <div class="uiHint">Read About and Rules first. If required, accept rules to unlock posting and chat.</div>
    357             <div id="onboardingPanelBody" class="small muted"></div>
    358             <div class="row" style="margin-top:10px;">
    359               <button id="onboardingPanelAccept" class="primary grow hidden" type="button">Accept and continue</button>
    360               <button id="onboardingPanelRefresh" class="ghost" type="button">Refresh</button>
    361             </div>
    362           </div>
    363         </section>
    364 
    365         <section id="profileViewPanel" class="panel panelFill hidden">
    366           <div class="panelHeader">
    367             <div>
    368               <div class="panelTitle" id="profileViewTitle">Profile</div>
    369               <div id="profileViewMeta" class="small muted"></div>
    370             </div>
    371             <div class="row">
    372               <button id="profileBackBtn" class="ghost smallBtn" type="button">Back to hives</button>
    373               <button id="profileEditToggleBtn" class="ghost smallBtn hidden" type="button">Edit profile</button>
    374             </div>
    375           </div>
    376           <div class="uiHint">Tip: open someone from People, a hive card, or a chat message to view their profile here.</div>
    377           <div id="profileViewBody" class="profileViewBody">
    378             <div id="profileCard" class="profileCard"></div>
    379           </div>
    380           <div id="profileEditPanel" class="profileEditPanel hidden">
    381             <label>
    382               <span>Pronouns</span>
    383               <input id="profilePronouns" maxlength="40" placeholder="she/her, they/them, etc" />
    384             </label>
    385             <label>
    386               <span>Theme song</span>
    387               <div class="row">
    388                 <input id="profileThemeSongUrl" placeholder="Upload audio for your theme song" readonly />
    389                 <button id="profileThemeSongUploadBtn" class="ghost" type="button">Upload audio</button>
    390                 <button id="profileThemeSongClearBtn" class="ghost" type="button">Remove</button>
    391               </div>
    392               <audio id="profileThemeSongPreview" controls preload="none" class="hidden"></audio>
    393               <input id="profileThemeSongFile" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    394             </label>
    395             <div class="editorShell">
    396               <div class="toolbar" id="profileBioToolbar" role="toolbar" aria-label="Profile bio formatting">
    397                 <button type="button" data-profilecmd="bold"><b>B</b></button>
    398                 <button type="button" data-profilecmd="italic"><i>I</i></button>
    399                 <button type="button" data-profilecmd="underline"><u>U</u></button>
    400                 <button type="button" data-profilecmd="strikeThrough"><s>S</s></button>
    401                 <span class="sep"></span>
    402                 <button type="button" data-profilecmd="insertUnorderedList">List</button>
    403                 <button type="button" data-profilecmd="insertOrderedList">1. List</button>
    404                 <button type="button" data-profilelink="1">Link</button>
    405                 <button type="button" data-profileimg="1">GIF/Image</button>
    406                 <button type="button" data-profileaudio="1">Audio</button>
    407                 <button type="button" data-profileemoji="1">Emoji</button>
    408                 <button type="button" data-profilecmd="removeFormat">Clear</button>
    409               </div>
    410               <div id="profileBioEditor" class="editor profileBioEditor" contenteditable="true" aria-label="Profile bio editor"></div>
    411               <input id="profileBioImageFile" class="hidden" type="file" accept="image/*,.gif" />
    412               <input id="profileBioAudioFile" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    413             </div>
    414             <div class="profileLinksHead">
    415               <span>Social links</span>
    416               <button id="profileAddLinkBtn" class="ghost smallBtn" type="button">Add link</button>
    417             </div>
    418             <div id="profileLinksEditor" class="profileLinksEditor"></div>
    419             <div class="row">
    420               <button id="profileSaveBtn" class="primary" type="button">Save profile</button>
    421               <button id="profileCancelBtn" class="ghost" type="button">Cancel</button>
    422             </div>
    423           </div>
    424         </section>
    425 
    426         <section id="pollinatePanel" class="panel composerCollapsed">
    427           <div class="panelHeader">
    428             <div class="panelTitle">Create Hive</div>
    429             <button id="toggleComposerInline" class="ghost smallBtn" type="button">Hide</button>
    430           </div>
    431           <div class="uiHint">Keep titles short and clear. Add keywords so others can find your hive faster.</div>
    432           <form id="newPostForm" class="form">
    433             <label>
    434               <span>Title (max 96 chars)</span>
    435               <input id="postTitle" maxlength="96" placeholder="Short title for this hive" />
    436             </label>
    437             <div class="editorShell">
    438               <div class="toolbar" role="toolbar" aria-label="Formatting">
    439                 <button type="button" data-cmd="bold"><b>B</b></button>
    440                 <button type="button" data-cmd="italic"><i>I</i></button>
    441                 <button type="button" data-cmd="underline"><u>U</u></button>
    442                 <button type="button" data-cmd="strikeThrough"><s>S</s></button>
    443                 <span class="sep"></span>
    444                 <button type="button" data-cmd="insertUnorderedList">List</button>
    445                 <button type="button" data-cmd="insertOrderedList">1. List</button>
    446                 <button type="button" data-link="1">Link</button>
    447                 <button type="button" data-postimg="1">GIF/Image</button>
    448                 <button type="button" data-postaudio="1">Audio</button>
    449                 <button type="button" data-postemoji="1">Emoji</button>
    450                 <button type="button" data-cmd="removeFormat">Clear</button>
    451               </div>
    452               <div id="editor" class="editor" contenteditable="true" aria-label="Post editor"></div>
    453               <input id="postImage" class="hidden" type="file" accept="image/*,.gif" />
    454               <input id="postAudio" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    455             </div>
    456 
    457             <div class="row">
    458               <label class="grow">
    459                 <span>Collection</span>
    460                 <select id="postCollection"></select>
    461               </label>
    462               <label class="grow">
    463                 <span>Keywords (comma separated, up to 6)</span>
    464                 <input id="keywords" placeholder="hive, code, idea" />
    465               </label>
    466               <label>
    467                 <span>Post duration</span>
    468                 <input id="ttlMinutes" type="number" min="1" max="2880" value="60" />
    469               </label>
    470               <label>
    471                 <span>Quick duration</span>
    472                 <select id="ttlPreset">
    473                   <option value="5">5 min</option>
    474                   <option value="30">30 min</option>
    475                   <option value="60" selected>1 hour</option>
    476                   <option value="120">2 hours</option>
    477                   <option value="720">12 hours</option>
    478                   <option value="1440">24 hours</option>
    479                 </select>
    480               </label>
    481               <label class="checkRow">
    482                 <span>Keep forever</span>
    483                 <input id="ttlPermanent" type="checkbox" />
    484               </label>
    485             </div>
    486 
    487             <button class="primary" type="submit">Send</button>
    488             <div class="row">
    489               <label class="grow">
    490                 <span>Protected</span>
    491                 <input id="isProtected" type="checkbox" />
    492               </label>
    493               <label class="grow">
    494                 <span>Hive mode</span>
    495                 <select id="postMode">
    496                   <option value="text" selected>Text</option>
    497                   <option value="walkie">Walkie Talkie</option>
    498                   <option value="stream">Stream</option>
    499                 </select>
    500               </label>
    501               <label id="streamKindRow" class="grow hidden">
    502                 <span>Stream type</span>
    503                 <select id="streamKind">
    504                   <option value="screen">Screen share</option>
    505                   <option value="webcam" selected>Webcam</option>
    506                   <option value="audio">Audio only</option>
    507                 </select>
    508               </label>
    509               <label class="grow">
    510                 <span>Post password</span>
    511                 <input id="postPassword" type="password" autocomplete="new-password" placeholder="Required if protected" disabled />
    512               </label>
    513             </div>
    514           </form>
    515         </section>
    516           </div>
    517           <div id="mainSideRack" class="sideRack" aria-label="Side panels"></div>
    518         </div>
    519       </main>
    520 
    521       <div id="chatResizeHandle" class="panelResizeHandle chatResizeHandle" title="Drag to resize chat" aria-hidden="true"></div>
    522 
    523       <aside class="chat">
    524         <div class="panelHeader">
    525           <div>
    526             <div class="panelTitle" id="chatTitle">Chat</div>
    527             <div id="chatMeta" class="small muted">Select a post to chat.</div>
    528           </div>
    529           <div class="row chatHeaderActions">
    530             <select id="chatContextSelect" class="chatContextSelect" aria-label="Open chats"></select>
    531             <button id="chatBackToList" class="ghost smallBtn hidden" type="button">Back</button>
    532           </div>
    533         </div>
    534         <div class="uiHint">Select a hive chat or DM first, then type your message and press Send. Shortcut in Chat: <b>-</b>/<b>=</b> cycles chat list entries.</div>
    535         <section id="streamStage" class="streamStage hidden" aria-label="Live stream stage">
    536           <div class="streamStageHeader">
    537             <div id="streamStageTitle" class="streamStageTitle">Stream</div>
    538             <button id="streamStagePrimary" class="ghost smallBtn" type="button">Join stream</button>
    539           </div>
    540           <div id="streamStageStatus" class="small muted">Stream is offline.</div>
    541           <div id="streamVoiceControls" class="streamVoiceControls hidden" aria-label="Voice controls">
    542             <label class="checkRow streamVoiceToggleRow">
    543               <span>Join voice</span>
    544               <input id="streamVoiceJoinToggle" type="checkbox" />
    545             </label>
    546             <div class="row streamVoiceButtonsRow">
    547               <button id="streamVoiceMuteBtn" class="ghost smallBtn" type="button">Mute mic</button>
    548               <button id="streamVoiceDeafenBtn" class="ghost smallBtn" type="button">Deafen</button>
    549             </div>
    550             <div id="streamVoiceUsers" class="streamVoiceUsers small muted"></div>
    551           </div>
    552           <video id="streamStageVideo" class="streamStageVideo hidden" playsinline autoplay controls muted></video>
    553           <audio id="streamStageAudio" class="streamStageAudio hidden" autoplay controls></audio>
    554           <div id="streamStagePlaceholder" class="streamStagePlaceholder small muted">Open a stream hive to watch, or start a stream if you own the post.</div>
    555         </section>
    556         <div id="chatMessages" class="chatMessages"></div>
    557         <div id="typingIndicator" class="typingIndicator small muted"></div>
    558         <div id="walkieBar" class="walkieBar hidden" aria-label="Walkie talkie controls">
    559           <button id="walkieRecordBtn" class="primary" type="button">Hold to talk</button>
    560           <div class="walkieHint small muted">
    561             Hold <span class="tag">~</span> or press-and-hold this button to record. Release to send.
    562           </div>
    563           <div id="walkieStatus" class="small muted"></div>
    564         </div>
    565         <form id="chatForm" class="chatForm">
    566           <div id="chatReplyBanner" class="chatReplyBanner hidden">
    567             <div class="small">
    568               Replying to <span id="chatReplyWho"></span>
    569               <span id="chatReplyText" class="muted"></span>
    570             </div>
    571             <button id="chatReplyCancel" class="ghost smallBtn" type="button">Cancel</button>
    572           </div>
    573             <div class="chatComposer">
    574             <div class="toolbar" role="toolbar" aria-label="Chat formatting">
    575               <button type="button" data-chatcmd="bold"><b>B</b></button>
    576               <button type="button" data-chatcmd="italic"><i>I</i></button>
    577               <button type="button" data-chatcmd="underline"><u>U</u></button>
    578               <button type="button" data-chatcmd="strikeThrough"><s>S</s></button>
    579               <span class="sep"></span>
    580               <button type="button" data-chatcmd="insertUnorderedList">List</button>
    581               <button type="button" data-chatcmd="insertOrderedList">1. List</button>
    582               <button type="button" data-chatlink="1">Link</button>
    583               <button type="button" data-chatimg="1">GIF/Image</button>
    584               <button type="button" data-chataudio="1">Audio</button>
    585               <button type="button" data-chatemoji="1">Emoji</button>
    586               <button type="button" data-chatcmd="removeFormat">Clear</button>
    587               <label id="chatModToggleWrap" class="checkRow chatModToggle hidden" title="Send as moderator/system message (left rail)">
    588                 <span>Mod</span>
    589                 <input id="chatModToggle" type="checkbox" />
    590               </label>
    591             </div>
    592             <div id="chatEditor" class="editor chatEditor" contenteditable="true" aria-label="Chat editor"></div>
    593             <div id="mentionMenu" class="mentionMenu hidden" role="listbox" aria-label="Mention suggestions"></div>
    594             <input id="chatImage" class="hidden" type="file" accept="image/*" />
    595             <input id="chatAudio" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    596           </div>
    597           <div class="uiHint">Use Link, GIF/Image, and Audio to attach media quickly.</div>
    598           <button class="primary" type="submit">Send</button>
    599         </form>
    600       </aside>
    601 
    602       <div id="mainResizeHandle" class="panelResizeHandle mainResizeHandle" title="Drag to resize moderation panel" aria-hidden="true"></div>
    603 
    604       <aside id="modPanel" class="moderation hidden">
    605         <div class="panelHeader">
    606           <div class="panelTitle">Moderation</div>
    607         </div>
    608         <div class="uiHint">Use tabs to review reports, manage users/hives, configure server settings, and publish onboarding content.</div>
    609         <div class="modTabs">
    610           <button type="button" class="ghost" data-modtab="reports">Reports</button>
    611           <button type="button" class="ghost" data-modtab="users">Users</button>
    612           <button type="button" class="ghost" data-modtab="hives">Hives</button>
    613           <button type="button" class="ghost" data-modtab="server">Server</button>
    614           <button type="button" class="ghost" data-modtab="onboarding">Onboarding</button>
    615           <button type="button" class="ghost" data-modtab="log">Log</button>
    616         </div>
    617         <div class="modFilters">
    618           <select id="modReportStatus">
    619             <option value="open">Open reports</option>
    620             <option value="resolved">Resolved reports</option>
    621             <option value="dismissed">Dismissed reports</option>
    622             <option value="">All reports</option>
    623           </select>
    624           <button id="modRefresh" class="ghost" type="button">Refresh</button>
    625         </div>
    626         <div id="modBody" class="modBody small"></div>
    627       </aside>
    628 
    629       <div id="mobileScreenHost" class="mobileScreenHost" aria-label="Mobile screen host"></div>
    630     </div>
    631     <aside id="peopleDrawer" class="peopleDrawer hidden">
    632       <div id="peopleResizeHandle" class="peopleResizeHandle" title="Drag to resize people panel" aria-hidden="true"></div>
    633       <div class="panelHeader">
    634         <div class="panelTitle">Members list</div>
    635         <button id="closePeople" class="ghost smallBtn" type="button">Close</button>
    636       </div>
    637       <div class="peopleTabs">
    638         <button id="peopleMembersTab" class="primary" type="button">Members</button>
    639         <button id="peopleDmsTab" class="ghost" type="button">DMs</button>
    640       </div>
    641       <div id="peopleMembersView">
    642         <div class="uiHint">Members list lets you open profiles and start DMs. Mods can also send <b>Mod DM</b> from member/profile actions.</div>
    643         <div class="peopleFilters">
    644           <input id="peopleSearch" placeholder="Search members" />
    645         </div>
    646         <div id="peopleList" class="peopleList small"></div>
    647       </div>
    648       <div id="peopleDmsView" class="peopleDms small hidden">
    649         <div class="uiHint">DM requests must be accepted before chat opens. Active threads show <b>Open</b>, and mods get a <b>Mod DM</b> action.</div>
    650         DMs coming soon.
    651       </div>
    652     </aside>
    653     <div id="mobileNav" class="mobileNav hidden" aria-label="Mobile navigation">
    654       <button type="button" data-mobilescreen="account">Account</button>
    655       <button type="button" data-mobilescreen="hives">Hives</button>
    656       <button type="button" data-mobilescreen="chat">Chat</button>
    657       <button id="mobileFourthBtn" type="button" data-mobilescreen="people">Members</button>
    658       <button type="button" data-mobilescreen="profile">Profile</button>
    659       <button type="button" data-mobilescreen="more">More</button>
    660     </div>
    661 
    662     <div id="mobileMoreSheet" class="mobileMoreSheet hidden" role="dialog" aria-modal="true" aria-label="More">
    663       <div class="mobileMoreBackdrop" data-mobilemoreclose="1"></div>
    664       <div class="mobileMoreCard panel">
    665         <div class="panelHeader">
    666           <div class="panelTitle">More</div>
    667           <div class="row">
    668             <button id="mobileMoreClose" class="ghost smallBtn" type="button">Close</button>
    669           </div>
    670         </div>
    671         <div class="mobileMoreBody">
    672           <input id="mobileMoreSearch" placeholder="Search…" />
    673           <div id="mobileMoreList" class="mobileMoreList"></div>
    674         </div>
    675       </div>
    676     </div>
    677 
    678     <div id="editModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Edit content">
    679       <div class="modalBackdrop" data-modalclose="1"></div>
    680       <div class="modalCard panel">
    681         <div class="panelHeader">
    682           <div class="panelTitle" id="editModalTitle">Edit</div>
    683           <div class="row">
    684             <button id="editModalClose" class="ghost smallBtn" type="button">Close</button>
    685           </div>
    686         </div>
    687         <div class="modalBody">
    688           <label id="editModalPostTitleRow" class="hidden">
    689             <span>Title</span>
    690             <input id="editModalPostTitle" maxlength="96" />
    691           </label>
    692 
    693           <div id="editModalPostMeta" class="hidden editMetaGrid" aria-label="Post settings">
    694             <label>
    695               <span>Keywords</span>
    696               <input id="editModalKeywords" maxlength="120" placeholder="comma separated (max 6)" />
    697             </label>
    698             <label>
    699               <span>Collection</span>
    700               <select id="editModalCollection"></select>
    701             </label>
    702             <label class="checkRow">
    703               <span>Password protected</span>
    704               <input id="editModalProtected" type="checkbox" />
    705             </label>
    706             <label>
    707               <span>Mode</span>
    708               <select id="editModalMode">
    709                 <option value="text" selected>Text</option>
    710                 <option value="walkie">Walkie Talkie</option>
    711                 <option value="stream">Stream</option>
    712               </select>
    713             </label>
    714             <label id="editModalStreamKindRow" class="hidden">
    715               <span>Stream type</span>
    716               <select id="editModalStreamKind">
    717                 <option value="screen">Screen share</option>
    718                 <option value="webcam" selected>Webcam</option>
    719                 <option value="audio">Audio only</option>
    720               </select>
    721             </label>
    722             <label id="editModalPasswordRow" class="hidden">
    723               <span>Password</span>
    724               <input
    725                 id="editModalPassword"
    726                 type="password"
    727                 minlength="4"
    728                 maxlength="80"
    729                 placeholder="Leave blank to keep current"
    730                 autocomplete="new-password"
    731               />
    732             </label>
    733           </div>
    734 
    735           <div class="editorShell">
    736             <div class="toolbar" id="editModalToolbar" role="toolbar" aria-label="Edit formatting">
    737               <button type="button" data-editcmd="bold"><b>B</b></button>
    738               <button type="button" data-editcmd="italic"><i>I</i></button>
    739               <button type="button" data-editcmd="underline"><u>U</u></button>
    740               <button type="button" data-editcmd="strikeThrough"><s>S</s></button>
    741               <span class="sep"></span>
    742               <button type="button" data-editcmd="insertUnorderedList">List</button>
    743               <button type="button" data-editcmd="insertOrderedList">1. List</button>
    744               <button type="button" data-editlink="1">Link</button>
    745               <button type="button" data-editimg="1">GIF/Image</button>
    746               <button type="button" data-editaudio="1">Audio</button>
    747               <button type="button" data-editemoji="1">Emoji</button>
    748               <button type="button" data-editcmd="removeFormat">Clear</button>
    749             </div>
    750             <div id="editModalEditor" class="editor editModalEditor" contenteditable="true" aria-label="Edit content"></div>
    751             <input id="editModalImage" class="hidden" type="file" accept="image/*,.gif" />
    752             <input id="editModalAudio" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    753           </div>
    754 
    755           <div class="row modalActions">
    756             <button id="editModalSave" class="primary" type="button">Save</button>
    757             <button id="editModalCancel" class="ghost" type="button">Cancel</button>
    758           </div>
    759           <div id="editModalStatus" class="small muted"></div>
    760         </div>
    761       </div>
    762     </div>
    763 
    764     <div id="modModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Moderation">
    765       <div class="modalBackdrop" data-modmodalclose="1"></div>
    766       <div class="modalCard panel">
    767         <div class="panelHeader">
    768           <div class="panelTitle" id="modModalTitle">Moderation</div>
    769           <div class="row">
    770             <button id="modModalClose" class="ghost smallBtn" type="button">Close</button>
    771           </div>
    772         </div>
    773         <div class="modalBody" id="modModalBody"></div>
    774         <div class="row modalActions">
    775           <button id="modModalPrimary" class="primary" type="button">Save</button>
    776           <button id="modModalCancel" class="ghost" type="button">Cancel</button>
    777         </div>
    778         <div id="modModalStatus" class="small muted" style="padding:0 12px 12px"></div>
    779       </div>
    780     </div>
    781 
    782     <div id="mediaModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Media preview">
    783       <div class="modalBackdrop" data-mediamodalclose="1"></div>
    784       <div class="modalCard panel">
    785         <div class="panelHeader">
    786           <div class="panelTitle" id="mediaModalTitle">Media</div>
    787           <div class="row">
    788             <button id="mediaModalClose" class="ghost smallBtn" type="button">Close</button>
    789           </div>
    790         </div>
    791         <div class="modalBody" id="mediaModalBody">
    792           <img id="mediaModalImg" alt="Expanded media" />
    793           <div class="row modalActions" style="justify-content:flex-start">
    794             <a id="mediaModalOpenLink" class="ghost" href="#" target="_blank" rel="noreferrer">Open original</a>
    795             <button id="mediaModalCopyLink" class="ghost" type="button">Copy link</button>
    796           </div>
    797           <div id="mediaModalStatus" class="small muted"></div>
    798         </div>
    799       </div>
    800     </div>
    801 
    802     <div id="shortcutHelpModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Keyboard shortcuts">
    803       <div class="modalBackdrop" data-shortcutclose="1"></div>
    804       <div class="modalCard panel">
    805         <div class="panelHeader">
    806           <div class="panelTitle">Keyboard Shortcuts</div>
    807           <div class="row">
    808             <button id="shortcutHelpClose" class="ghost smallBtn" type="button">Close</button>
    809           </div>
    810         </div>
    811         <div class="modalBody shortcutHelpBody">
    812           <div><span class="tag">[ / ]</span> Cycle layout presets</div>
    813           <div><span class="tag">- / =</span> Cycle hives or chats in active panel</div>
    814           <div><span class="tag">Arrows</span> Hover panel: Up size, Left/Right move, Down dock. Hover hotbar: Up restore.</div>
    815           <div><span class="tag">R</span> Toggle Members list rail</div>
    816           <div><span class="tag">?</span> Open this shortcut help</div>
    817           <div><span class="tag">`</span> Hold for walkie talkie (when enabled)</div>
    818           <div><span class="tag">Esc</span> Close menus/modals</div>
    819         </div>
    820       </div>
    821     </div>
    822 
    823     <div id="dockHotbar" class="dockHotbar hidden" aria-label="Docked panels"></div>
    824     <div id="authGate" class="authGate hidden" aria-hidden="true">
    825       <div class="authGateInner">
    826         <section class="authGateCard">
    827           <div class="panelTitle">Sign in to continue</div>
    828           <div id="authGateHint" class="small muted">
    829             Create an account or sign in to enter this server.
    830           </div>
    831           <form id="authGateForm" class="form authGateForm">
    832             <label>
    833               <span>Username</span>
    834               <input id="authGateUser" autocomplete="username" />
    835             </label>
    836             <label>
    837               <span>Password</span>
    838               <input id="authGatePass" type="password" autocomplete="current-password" />
    839             </label>
    840             <label id="authGateCodeRow" class="hidden">
    841               <span>Registration code</span>
    842               <input id="authGateCode" autocomplete="one-time-code" />
    843             </label>
    844             <div class="row">
    845               <button class="primary grow" type="submit">Sign in</button>
    846               <button id="authGateRegister" class="ghost" type="button">Create account</button>
    847             </div>
    848           </form>
    849         </section>
    850         <section class="authGateOnboarding">
    851           <div class="panelTitle">Server onboarding</div>
    852           <div class="small muted">Read About and Rules before entering.</div>
    853           <div class="row" style="margin-top:8px;gap:8px;flex-wrap:wrap;">
    854             <button type="button" class="primary smallBtn" data-authgate-tab="about">About</button>
    855             <button type="button" class="ghost smallBtn" data-authgate-tab="rules">Rules</button>
    856             <button type="button" class="ghost smallBtn" data-authgate-tab="roles">Roles</button>
    857           </div>
    858           <div id="authGateOnboardingBody" class="small muted" style="margin-top:8px;"></div>
    859           <div class="row" style="margin-top:10px;">
    860             <button id="authGateAccept" class="primary grow hidden" type="button">Accept rules and continue</button>
    861             <button id="authGateRefresh" class="ghost" type="button">Refresh</button>
    862           </div>
    863         </section>
    864       </div>
    865     </div>
    866     <script src="/app.js?v=158"></script>
    867   </body>
    868 </html>