/* shell.css — the constant shell chrome (header + RHSB dock) loaded on EVERY page,
   so the persistent header (with the <audio> stream) is always styled and Turbo can
   never drop its CSS on navigation. Page-specific body layout lives in each page's
   own stylesheet (style.css for the public column; inline <style> for the reader). */

:root{--hdr-h:64px;--rhsb-w:33vw}   /* canvas frame: header height (top) + RHSB rail (right) ≈ ⅓ viewport so content isn't lost in the expansiveness */

/* === unified 3-block shell header — fixed top bar (the canvas' top edge) === */
.hdr{position:fixed;top:0;left:0;right:0;z-index:50;display:flex;align-items:stretch;gap:.7rem;background:#13110a;border-bottom:2px solid var(--gold);padding:.5rem 1rem;height:var(--hdr-h);box-sizing:border-box}
.hL{display:flex;align-items:center;gap:.7rem;flex:1 1 0;min-width:0}
.hlogo{width:48px;height:48px;object-fit:cover;border:1px solid var(--gold);flex:0 0 auto;display:block}
.hmp{min-width:0}
.hnow{color:var(--bone);font-size:.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hshow{color:var(--gold);font-size:.72rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hplay{background:var(--gold);color:#0A0A0F;border:0;width:48px;height:48px;font-size:1.1rem;cursor:pointer;flex:0 0 auto}
.hplay:hover{filter:brightness(1.25)}
.hC{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:0 0 auto}
.hbrand{font-size:2rem;line-height:1;color:#fff;font-weight:700}
.honair{color:var(--toxic);font-size:.68rem;letter-spacing:.22em;margin-top:.2rem;text-decoration:none}
.hR{display:flex;align-items:center;gap:.7rem;flex:1 1 0;min-width:0;justify-content:flex-end}
.hchat{background:transparent;border:1px solid var(--smoke);color:var(--cyan);font:inherit;font-size:.72rem;letter-spacing:.1em;padding:.4rem .6rem;cursor:pointer}
.hchat:hover{border-color:var(--cyan)}
.huser{text-align:right;min-width:0;line-height:1.25;text-decoration:none}
.hun{color:var(--cyan);font-size:.78rem;white-space:nowrap;display:block}
.htier{color:var(--mute);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;display:block}
.havatar{width:48px;height:48px;border:1px solid var(--smoke);background:#0d0d13;display:flex;align-items:center;justify-content:center;font-size:1.3rem;color:#3a3a48;flex:0 0 auto}
@media(max-width:640px){.hdr{gap:.5rem;padding:.45rem .6rem}.hbrand{font-size:1.4rem}.hnow,.hshow{font-size:.7rem}.hchat{display:none}}

/* === radio dock (RHSB) — JS-injected on pages with column room, styled here === */
#n8dock{width:100%;background:var(--void);border-top:1px solid var(--smoke);padding:.75rem;box-sizing:border-box;font-size:13px;line-height:1.45}
#n8dock a{color:var(--cyan);text-decoration:none}
#n8dock .n8subs{display:flex;align-items:center;gap:.4rem;border:1px solid var(--smoke);padding:.4rem .5rem;font-size:11px;flex-wrap:wrap}
#n8dock .n8subs .l{color:var(--toxic);letter-spacing:.18em}
#n8dock .n8subs a{border:1px solid var(--smoke);padding:.1rem .4rem}
#n8dock .n8about{display:block;text-align:center;border:1px solid var(--smoke);padding:.5rem;margin:.5rem 0;letter-spacing:.1em}
#n8dock .n8card{border:1px solid var(--smoke);background:var(--panel);padding:.7rem;overflow:hidden}
#n8dock .n8card img{float:left;width:64px;height:64px;object-fit:cover;border:1px solid var(--cyan);margin:0 .6rem .4rem 0}
#n8dock .n8tag{color:var(--magenta);font-size:10px;letter-spacing:.2em}
#n8dock .n8show{color:var(--cyan);font-size:14px;margin:.1rem 0}
#n8dock .n8dj{color:var(--bone);font-size:12px}
#n8dock .n8blurb{clear:both;color:var(--mute);font-size:11px;margin-top:.4rem}
#n8dock .n8h{color:var(--cyan);font-size:11px;letter-spacing:.2em;margin:.9rem 0 .3rem}
#n8dock .n8row{display:flex;justify-content:space-between;gap:.5rem;padding:.25rem .4rem;border-bottom:1px solid var(--smoke);font-size:11px;color:var(--mute)}
#n8dock .n8row.live{background:var(--panel);color:var(--cyan)}
#n8dock .n8qbpm{color:var(--gold);flex:0 0 2.2em;text-align:right}
@media(min-width:1280px){#n8dock{position:fixed;top:var(--hdr-h);right:0;bottom:calc(var(--hdr-h) * 2);width:var(--rhsb-w);overflow:auto;border-top:0;border-left:1px solid var(--smoke);z-index:40}}

/* === signed-in avatar (initial) === */
.havatar{text-decoration:none}
.havatar-on{background:var(--gold);color:#0A0A0F;font-weight:700;text-transform:uppercase;font-size:1.15rem;border-color:var(--gold)}
.havatar-on:hover{filter:brightness(1.15)}

/* === backstage-access modal (popped from the header; no-JS falls back to /login) === */
.lm{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;padding:1rem}
.lm[hidden]{display:none}
.lm-back{position:absolute;inset:0;background:rgba(5,5,9,.78)}
.lm-card{position:relative;width:min(94vw,380px);background:#13110a;border:2px solid var(--gold);padding:1.6rem 1.5rem 1.4rem;box-sizing:border-box;box-shadow:0 0 0 1px #000,0 20px 60px rgba(0,0,0,.6)}
.lm-x{position:absolute;top:.35rem;right:.5rem;background:none;border:0;color:var(--mute);font-size:1.5rem;line-height:1;cursor:pointer}
.lm-x:hover{color:var(--magenta)}
.lm-title{color:var(--cyan);font-size:.7rem;letter-spacing:.24em;text-transform:uppercase}
.lm-title::before{content:"\276F\00a0";color:var(--toxic)}
.lm-sub{margin:.5rem 0 1.1rem;color:var(--mute);font-size:.78rem;line-height:1.45}
.lm-form label{display:block;font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;color:var(--mute);margin:.7rem 0 .3rem}
.lm-form input{width:100%;background:#0d0d13;border:1px solid var(--smoke);color:var(--bone);padding:.55rem .6rem;font:14px ui-monospace,"JetBrains Mono",monospace;box-sizing:border-box}
.lm-form input:focus{outline:none;border-color:var(--magenta)}
.lm-userrow{display:flex;align-items:stretch}
.lm-userrow input{flex:1;min-width:0}
.lm-suffix{display:flex;align-items:center;padding:0 .5rem;background:#0d0d13;border:1px solid var(--smoke);border-left:0;color:var(--mute);font-size:.72rem;white-space:nowrap}
.lm-go{width:100%;margin-top:1.1rem;background:var(--cyan);color:var(--void);border:0;padding:.6rem;font:700 .72rem ui-monospace,"JetBrains Mono",monospace;letter-spacing:.14em;text-transform:uppercase;cursor:pointer}
.lm-go:hover{background:var(--bone)}
.lm-hint{margin-top:1rem;color:var(--dim);font-size:.68rem;line-height:1.5}
.lm-hint a{color:var(--cyan)}
.lm-hint b{color:var(--mute)}
.lm-soon{margin-top:.9rem;border-left:2px solid var(--gold);padding:.5rem .7rem;color:var(--gold);font-size:.7rem;line-height:1.45}
.lm-soon code{color:var(--cyan)}

/* image avatar (real photo) + tidy long JIDs */
.havatar-img{padding:0;overflow:hidden}
.havatar-img img{width:100%;height:100%;object-fit:cover;display:block}
.hun{max-width:12rem;overflow:hidden;text-overflow:ellipsis}

/* === Town Square — co-presence footer (M12 G02). A thin strip the SAME height as the
   header (--hdr-h), per Nathan's "contained within the same size space as the header." The
   strip is the resting state; figures/ball/chat rise ABOVE it (overflow:visible), so the
   chat/comments pop up over the page rather than fattening the bar. Stops short of the RHSB
   radio rail so the two don't overlap; desktop-only. JS-injected mount lives in #townsquare-root. */
#townsquare-foot{position:fixed;left:0;right:0;bottom:0;height:calc(var(--hdr-h) * 2);z-index:45;background:var(--void);border-top:2px solid var(--gold);box-sizing:border-box;overflow:visible}
@media(max-width:640px){#townsquare-foot{display:none}} /* desktop-only */
#townsquare-foot .townsquare{min-height:0;overflow:visible} /* the widget root clips (overflow:hidden); let speech bubbles rise above the footer's gold divider into the page */
#townsquare-foot .townsquare__stage{height:calc(var(--hdr-h) * 2);transform:translateY(-26px) scale(.78);transform-origin:bottom center} /* twice the header; scaled + shifted up so a clean band opens beneath the floor for the chat field; loft/chat overflow above */
/* The shared soccer ball — mount.js sets only left/bottom (position); the size + look live
   here. Without this it's an invisible 0x0 div. (Restored from the reverted shell.css block.) */
#ts-ball{position:absolute;bottom:49px;left:50%;width:13px;height:13px;border-radius:50%;transform:translateX(-50%);transition:left 60ms linear,bottom 60ms linear;z-index:4;border:1px solid #222;box-shadow:0 1px 2px rgba(0,0,0,.45);background:radial-gradient(circle at 50% 50%,#111 0 2px,transparent 2.6px),radial-gradient(circle at 26% 32%,#111 0 1.3px,transparent 1.7px),radial-gradient(circle at 74% 32%,#111 0 1.3px,transparent 1.7px),radial-gradient(circle at 30% 74%,#111 0 1.3px,transparent 1.7px),radial-gradient(circle at 72% 72%,#111 0 1.3px,transparent 1.7px),#fff}
/* Every figure's nameplate (+ the self's "Say something…" chat bubble) sits ABOVE the
   figure, not beneath — in the short strip a below-the-figure plate clips off the bottom,
   so flip them all up where they're readable. They pop above the strip via overflow:visible. */
#townsquare-foot .townsquare-avatar__below{top:auto;bottom:calc(100% + 6px);z-index:46}
/* Ring signposts at the strip's west/east edges — the neighbor squares (JS-injected by
   mount.js since the vendored widget has no connections feature). On the footer, not the
   scaled stage, so they sit at the true edges. */
.ts-signpost{position:absolute;top:50%;transform:translateY(-50%);z-index:46;color:var(--gold);text-decoration:none;font:.7rem/1 ui-monospace,monospace;letter-spacing:.05em;padding:.28rem .5rem;background:rgba(10,10,15,.72);border:1px solid var(--smoke)}
.ts-signpost:hover{border-color:var(--gold);color:var(--toxic)}
.ts-signpost--left{left:.5rem}
.ts-signpost--right{right:.5rem}
/* Nightclub — it's a club, not a park (hide the seating props). The light rig + dance floor
   draw color + tempo from /radio.json's now-playing: --club-color = the track's DPN key-color,
   --club-beat = 60/bpm, re-synced each track (defaults below until the first sync). A truss of
   lights up top, most washing the track color, two white, staggered into a beat chase. */
#townsquare-foot{--club-color:#00F5FF;--club-beat:.5s}
#townsquare-foot .prop{display:none}
.club-rig{position:absolute;top:6px;left:8%;right:8%;display:flex;justify-content:space-between;z-index:3;pointer-events:none}
.club-light{position:relative;width:9px;height:7px;background:var(--club-color);border-radius:0 0 4px 4px;opacity:.4;animation:club-pulse var(--club-beat) infinite}
.club-light::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);border-left:8px solid transparent;border-right:8px solid transparent;border-top:46px solid var(--club-color);opacity:.16;filter:blur(2px)}
.club-light--white{--club-color:#fff}
@keyframes club-pulse{0%,100%{opacity:.06}50%{opacity:1;box-shadow:0 0 22px 5px var(--club-color),0 0 48px 10px var(--club-color)}}
.club-floor{position:absolute;left:0;right:0;bottom:0;height:34%;z-index:1;pointer-events:none;background:radial-gradient(120% 80% at 50% 130%,var(--club-color),transparent 65%);opacity:.2;animation:club-floor var(--club-beat) infinite}
@keyframes club-floor{0%,100%{opacity:.08}50%{opacity:.34}}
/* Sweeper cones — come UP from the floor (bottom-anchored), sweeping (one white). */
.club-sweep{position:absolute;bottom:24%;width:0;height:0;border-left:18px solid transparent;border-right:18px solid transparent;border-bottom:96px solid var(--club-color);opacity:.16;transform-origin:bottom center;animation:club-sweep 3.4s ease-in-out infinite;filter:blur(2px);z-index:2;pointer-events:none}
.club-sweep--white{--club-color:#fff;opacity:.12}
@keyframes club-sweep{0%,100%{transform:rotate(-34deg)}50%{transform:rotate(34deg)}}
/* The room: clips the lasers to the strip so the fans + reflection stop dead at the ceiling
   and floor instead of spilling into the page or below. */
.club-room{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:4}
/* Two projector FANS crossing streams (M12), 5 beams each. Left at 35% shoots right almost
   to the far wall; right at 65% shoots left. The fan rakes floor↔ceiling on a 4-beat count
   (club-rake, small angle so the far ends stop at the ceiling/floor) and flickers at 6× the
   bpm (club-flicker); the right fan is offset half a cycle so they scissor. */
.club-fan{position:absolute;top:50%;width:62%;height:0;z-index:4;pointer-events:none;animation:club-rake calc(var(--club-beat) * 4) ease-in-out infinite,club-flicker calc(var(--club-beat) / 6) infinite}
.club-fan--l{left:35%;transform-origin:left center}
.club-fan--r{left:3%;transform-origin:right center;animation-delay:calc(var(--club-beat) * -2),0s}
.club-fan .club-beam{position:absolute;top:0;left:0;width:100%;height:1px;background:var(--club-color);box-shadow:0 0 3px var(--club-color);transform-origin:left center;transform:rotate(calc(var(--i) * 3deg))}
.club-fan--r .club-beam{transform-origin:right center}
@keyframes club-rake{0%,100%{transform:rotate(-4.5deg)}50%{transform:rotate(4.5deg)}}
@keyframes club-flicker{0%,49%{opacity:.5}50%,100%{opacity:.1}}
/* Disco ball at the ceiling centre + the reflected fan it scatters back down over the floor —
   the beams "bounce" off it. The ball spins; the scatter sways and twinkles on the beat. */
.club-ball{position:absolute;top:-7px;left:50%;width:15px;height:15px;margin-left:-7px;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fff,var(--club-color) 55%,#1a1a22);box-shadow:0 0 10px 2px var(--club-color);z-index:6;animation:club-spin 3.2s linear infinite}
@keyframes club-spin{to{filter:hue-rotate(360deg)}}
.club-reflect{position:absolute;top:6px;left:50%;width:0;height:0;z-index:3;pointer-events:none;transform-origin:top center;animation:club-sway 5s ease-in-out infinite}
@keyframes club-sway{0%,100%{transform:rotate(-13deg)}50%{transform:rotate(13deg)}}
.club-reflect-beam{position:absolute;top:0;left:0;width:1px;height:130px;background:linear-gradient(var(--club-color),transparent 85%);box-shadow:0 0 3px var(--club-color);transform-origin:top center;transform:rotate(calc(var(--i) * 15deg));opacity:.32;animation:club-twinkle calc(var(--club-beat) / 3) infinite;animation-delay:calc(var(--i) * 0.04s)}
@keyframes club-twinkle{0%,40%{opacity:.5}50%,100%{opacity:.08}}
/* It's a club: no birds, no ball (he's got the ball in the PR if he wants it). */
#townsquare-foot .townsquare__birds{display:none}
#ts-ball{display:none}
/* The self's chat is ONE stationary text field at the bottom of the strip, not a per-figure
   bubble: hide the self's floating nameplate, and keep the docked composer always visible as
   the persistent entry field. position:fixed escapes the scaled stage + rides the footer
   bottom. (Peers keep their nameplates above them so you can still tell who's who.) */
#townsquare-foot .townsquare-avatar--self .townsquare-avatar__below{display:none}
#townsquare-foot .townsquare-avatar__composer--docked{display:flex !important;position:fixed;left:50%;right:auto;bottom:3px;transform:translateX(-50%);width:min(300px,46vw);min-width:0;max-width:none;z-index:47;gap:3px;padding:0;background:transparent;border:0;border-radius:0;box-shadow:none}
/* DPN: a thin squared bar that sits down in the dark band beneath the floor line (figures
   walk at ~41px scaled; this rides the bottom ~26px so it clears their feet). */
#townsquare-foot .townsquare-avatar__composer--docked .townsquare-avatar__input{height:1.4rem;min-height:0;padding:.15rem .45rem;font-size:.74rem;border-radius:0;background:#0d0d13;border:1px solid var(--smoke);color:var(--bone)}
#townsquare-foot .townsquare-avatar__composer--docked .townsquare-avatar__send{height:1.4rem;width:1.4rem;min-height:0;min-width:0;padding:0;border-radius:0;flex:0 0 auto}
#ts-keys{position:absolute;right:.6rem;bottom:.3rem;font:.62rem/1 ui-monospace,monospace;color:var(--mute);letter-spacing:.04em;pointer-events:none}
#ts-keys .k{color:var(--cyan)} #ts-keys .sep{color:var(--smoke)}

