/* UI SCALE (Oct 4): every size in this file is `N*var(--u)` instead of N px (hairlines 1px and 0 stay literal).
   --u = 1px × the device's UI scale, set by core.js from the screen size (auto) and CFG → Comms → DISPLAY (manual).
   Pointer math is unaffected: everything stays in real CSS px, only the sizes change. */
:root{--u:1px}
 html{-webkit-text-size-adjust:100%;text-size-adjust:100%}   /* no Android font boosting (guard rail) */

 :root{color-scheme:dark}
 *{box-sizing:border-box}
 html,body{margin:0;height:100%;background:#000;font:calc(14*var(--u)) system-ui,sans-serif;color:#eaeaea;overflow:hidden}
 #stage{position:fixed;inset:0;background:#000}
 video{width:100%;height:100%;object-fit:contain;background:#000}
 /* ================= TOP BAR: one unified button style, three groups =================
    #tl (top-left)  : LOCAL things  → CAP (browser capture) · REC (RPi onboard, auto+override) · TGT
    #tc (top-center): page nav — always in the middle, easy to find
    #tr (top-right) : GIMBAL things → ◉ TRIP capture · INJECT arm
    Every button is a .tb rounded rectangle with the same base styling; role colours are modifiers. */
 .tbar{position:fixed;top:calc(8*var(--u));z-index:11;display:flex;gap:calc(6*var(--u));align-items:center}
 #tl{left:calc(10*var(--u))}
 #tc{left:50%;transform:translateX(-50%);white-space:nowrap}
 #tr{right:calc(10*var(--u))}
 body.tb2 #tc{top:calc(48*var(--u))}                     /* narrow screen: nav on a second row */
 body.tb2 #ticker{top:calc(118*var(--u))}
 .tb{display:inline-flex;align-items:center;gap:calc(6*var(--u));padding:calc(6*var(--u)) calc(12*var(--u));border-radius:calc(6*var(--u));
     background:rgba(20,24,28,.78);border:1px solid #3a4550;color:#cfe3cf;
     font-weight:700;font-size:calc(12*var(--u));cursor:pointer;user-select:none;white-space:nowrap;
     text-decoration:none;line-height:1.2;transition:border-color .1s,background .1s}
 .tb:hover{border-color:#5a9c63}
 .tb.warn{background:#c99a1e;color:#1a1400;border-color:#ffd24d}
 /* nav links share the .tb look but calmer */
 #tc .tb{font-weight:600;color:#8fbce8;border-color:#2e3a46}
 #tc .tb:hover{color:#eaf3ff;border-color:#6ba4de}
 /* red = capture/record family (browser CAP + gimbal ◉) */
 .tb.red{background:rgba(150,20,26,.92);border-color:#ff5560;color:#fff;box-shadow:0 0 calc(8*var(--u)) rgba(192,32,42,.5)}
 .tb.red:hover{background:rgba(180,26,32,.96);border-color:#ff5560}
 .tb .dot{width:calc(11*var(--u));height:calc(11*var(--u));border-radius:50%;background:#fff;box-shadow:0 0 calc(6*var(--u)) #fff}
 .tb.red.recording{background:rgba(122,31,40,.95);animation:recblink 1s steps(2,start) infinite}
 .tb.red.recording .dot{background:#ff2b36;box-shadow:0 0 calc(8*var(--u)) #ff2b36}
 @keyframes recblink{50%{box-shadow:0 0 calc(16*var(--u)) rgba(255,43,54,.9)}}
 /* onboard REC (RPi): same rectangle, state colours */
 #recstatus.rec{color:#fff;background:rgba(122,31,40,.95);border-color:#ff5560;animation:recblink 1s steps(2,start) infinite}
 #recstatus.nofix{color:#c08a2a;border-color:#7a5a1e}
 /* TGT designate */
 #desig{color:#8fbce8;border-color:rgba(107,164,222,.6)}
 #desig.on{background:rgba(28,60,90,.9);border-color:#6ba4de;color:#eaf3ff;box-shadow:0 0 calc(8*var(--u)) rgba(107,164,222,.6)}
 body.designating #stage{cursor:crosshair}
 /* INJECT arm */
 #armbtn{border-color:#5a2a2a;color:#e8a0a0}
 #armbtn.on{background:rgba(120,25,30,.92);border-color:#ff5560;color:#fff}
 /* ---- map ---- */
 #mapbtn{color:#8fce8f;border-color:#2e4a3a} #mapbtn.on{background:#215c38;border-color:#4fd07a;color:#eafff0}
 /* ---- SCREENS: the top bar's radio group (MAIN · panel · monitor · joystick · recordings) switches what is
    shown over the video. Non-MAIN screens render in a borderless, full-size frame BELOW the top bar — no
    bar, no close button, no visible frame: the top buttons are the only navigation. ---- */
 .scr{display:flex;gap:calc(6*var(--u));align-items:center}
 .tb.pg.on{background:#215c38;border-color:#4fd07a;color:#eafff0}
 #cfgbtn{text-decoration:none} #cfgbtn .gear{font-size:calc(15*var(--u));line-height:1}
 #cfgbtn.on{background:#215c38;border-color:#4fd07a;color:#eafff0}
 #pgov{position:fixed;left:0;right:0;top:var(--pgtop,calc(44*var(--u)));bottom:0;z-index:12;display:none;background:transparent}   /* --pgtop measured from the real top bar; transparent: the live video shows through (TRIP2 is an OSD over it) */
 body.pgopen #pgov{display:block}
 #pg-frame{width:100%;height:100%;border:0;display:block;background:transparent;visibility:hidden}
 #pg-frame.ready{visibility:visible}                                   /* shown only once its document has loaded: no white blank-document flash */
 body.pgopen #ticker,body.pgopen #ptt,body.pgopen #joy,body.pgopen #rail,body.pgopen #modes{display:none}
 #mapov{position:fixed;inset:0;z-index:12;display:none;background:#0b0e12}
 body.mapopen #mapov{display:block}
 #map{position:absolute;inset:0}
 .map-tools{position:absolute;top:calc(8*var(--u));right:calc(10*var(--u));z-index:1000;display:flex;gap:calc(6*var(--u));align-items:center}
 #map-layer{background:#0d0f12;border:1px solid #2c313a;border-radius:calc(5*var(--u));color:#cfe3cf;padding:calc(4*var(--u)) calc(6*var(--u));font-size:calc(11*var(--u))}
 #map-follow.on,#map-me.on,#map-night.on,#map-labels.on{background:#215c38;border-color:#4fd07a;color:#eafff0}
 body.mapnight .leaflet-tile-pane{filter:invert(.92) hue-rotate(180deg) brightness(.75) contrast(1.05)}   /* dark map from any light layer */
 #map-info{position:absolute;left:calc(10*var(--u));bottom:calc(10*var(--u));z-index:1000;font:calc(11*var(--u))/1.35 ui-monospace,monospace;color:#cfe3cf;background:rgba(0,0,0,.6);padding:calc(6*var(--u)) calc(9*var(--u));border-radius:calc(6*var(--u));pointer-events:none;max-width:calc(100vw - calc(20*var(--u)));overflow:hidden}
 #map-info table{border-collapse:collapse;white-space:nowrap}
 #map-info td,#map-info th{padding:1px calc(10*var(--u)) 1px 0;text-align:left;vertical-align:middle;border:0}
 #map-info tr.h th{color:#8b9199;font-weight:normal;font-size:calc(10*var(--u));letter-spacing:.06em;text-transform:uppercase}
 #map-info i{color:#8b9199;font-style:normal}
 #map-info .mi{display:inline-block;font-size:calc(14*var(--u));line-height:1}
 .leaflet-container{background:#0b0e12}
 .ac-icon{width:calc(34*var(--u));height:calc(34*var(--u));display:flex;align-items:center;justify-content:center;filter:drop-shadow(0 0 calc(3*var(--u)) #000)}
 .ac-icon svg{width:calc(34*var(--u));height:calc(34*var(--u))}
 .u-icon{display:flex;flex-direction:column;align-items:center;filter:drop-shadow(0 0 calc(3*var(--u)) #000)}
 .u-icon .em{font-size:calc(20*var(--u));line-height:1;border-radius:50%;padding:calc(3*var(--u));border:calc(3*var(--u)) solid transparent;background:rgba(0,0,0,.35)}
 .u-icon.tx .em{border-color:#ff5560;box-shadow:0 0 calc(8*var(--u)) #ff5560}         /* I transmit to them */
 .u-icon.rx .em{border-color:#4fd07a;box-shadow:0 0 calc(8*var(--u)) #4fd07a}         /* receiving from them */
 .u-icon.busy .em{border-color:#b57cff;box-shadow:0 0 calc(8*var(--u)) #b57cff}       /* busy: in a transmission not involving me */
 .u-icon.duplex .em{border-color:#ffd54a;box-shadow:0 0 calc(8*var(--u)) #ffd54a}     /* both */
 .u-icon{touch-action:none;cursor:pointer}
 /* long-press must never select text or open a callout — the page is a control surface (Oct 3) */
 body{-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}
 input,textarea{-webkit-user-select:text;user-select:text}
 .ur .dot{cursor:pointer}
 .ac-icon .em{font-size:calc(26*var(--u));line-height:1;display:inline-block}
 .ri{margin-right:calc(4*var(--u))}
 .u-icon .lbl{font:calc(10*var(--u))/1 ui-monospace,monospace;color:#fff;background:rgba(0,0,0,.6);padding:1px calc(4*var(--u));border-radius:calc(3*var(--u));margin-top:calc(2*var(--u));white-space:nowrap}
 .u-icon.stale{opacity:.5}
 body.mapopen #ticker,body.mapopen #ptt,body.mapopen #joy,body.mapopen #rail,body.mapopen #modes{display:none}
 /* ---- chat ---- */
 #chatbtn{color:#8fce8f;border-color:#2e4a3a;position:relative}
 #chatbtn.on{background:#215c38;border-color:#4fd07a;color:#eafff0}
 #chatbtn .badge{position:absolute;top:calc(-6*var(--u));right:calc(-6*var(--u));min-width:calc(16*var(--u));height:calc(16*var(--u));border-radius:calc(8*var(--u));background:#ff3b46;
      color:#fff;font-size:calc(10*var(--u));line-height:calc(16*var(--u));text-align:center;padding:0 calc(4*var(--u));display:none}
 #chatbtn .badge.show{display:block}
 #ticker{position:fixed;left:50%;transform:translateX(-50%);top:calc(78*var(--u));z-index:8;max-width:70vw;pointer-events:none;
      font:calc(12*var(--u))/1.35 ui-monospace,monospace;color:#cfe3cf;text-shadow:0 0 calc(4*var(--u)) #000,0 0 calc(8*var(--u)) #000}
 #ticker .tl{background:rgba(0,0,0,.45);padding:calc(2*var(--u)) calc(8*var(--u));border-radius:calc(4*var(--u));margin:calc(2*var(--u)) 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
 #ticker .tl b{color:#ffd24d}
 body.editing #ticker, body.chatopen #ticker{display:none}
 /* chat window: starts BELOW the top bar (it used to cover the MAP button), shorter, and draggable by its
    head; position is remembered per screen (main / map) in localStorage */
 #chat{position:fixed;right:calc(10*var(--u));top:calc(96*var(--u));height:min(62vh,calc(560*var(--u)));width:min(calc(420*var(--u)),46vw);z-index:13;display:none;flex-direction:column;
      background:rgba(14,18,22,.88);border:1px solid #2e4a3a;border-radius:calc(10*var(--u));backdrop-filter:blur(calc(4*var(--u)))}
 body.chatopen #chat{display:flex}
 .chat-head{display:flex;align-items:center;gap:calc(6*var(--u));padding:calc(8*var(--u)) calc(10*var(--u));border-bottom:1px solid #1e2a22;cursor:move;touch-action:none;user-select:none;min-width:0;overflow:hidden}
 .chat-head>*{flex:none}
 .chat-head .chat-title{cursor:pointer}
 .chat-head .chat-me{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}   /* the only element that shrinks */
 .chat-head .chat-sp{flex:1 1 0;min-width:0}
 .chat-head #voice-target{flex:0 1 auto;width:auto;min-width:calc(84*var(--u));max-width:calc(200*var(--u))}   /* may shrink a little so the header never overflows */                              /* a select needs real width — never squeezed */
 #chat{max-width:calc(100vw - calc(20*var(--u)))}
 #chat.dragging{opacity:.92}
 #map-chat.on{background:#215c38;border-color:#4fd07a;color:#eafff0}
 .chat-title{font-weight:700;color:#9fdf9f;letter-spacing:.5px}
 .chat-me{font-family:ui-monospace,monospace;font-size:calc(11*var(--u));color:#ffd24d}
 .chat-sp{flex:1}
 .chat-pres{font-size:calc(11*var(--u));color:#8b9199;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:40%}
 .tb.small{padding:calc(3*var(--u)) calc(8*var(--u));font-size:calc(11*var(--u))}
 .tb.ok{background:#215c38;border-color:#4fd07a;color:#eafff0}
 /* one panel, split: USERS on top, CHAT below, movable divider; each half scrolls on its own.
    --users-h is the users half's share (set by the divider, remembered) */
 #chat-log{flex:1 1 0;min-height:calc(60*var(--u));overflow-y:auto;padding:calc(8*var(--u)) calc(10*var(--u));font:calc(13*var(--u))/1.4 system-ui,sans-serif;-webkit-overflow-scrolling:touch}
 #chat-split{flex:none;height:calc(10*var(--u));cursor:row-resize;touch-action:none;position:relative;background:transparent}
 #chat-split::before{content:"";position:absolute;left:50%;top:calc(3*var(--u));width:calc(48*var(--u));height:calc(4*var(--u));margin-left:calc(-24*var(--u));border-radius:calc(2*var(--u));background:#2c313a}
 #chat-split:hover::before,#chat-split.drag::before{background:#4fd07a}
 #chat-log .m{margin:calc(3*var(--u)) 0;word-break:break-word}
 #chat-log .m .ts{color:#5b6570;font-family:ui-monospace,monospace;font-size:calc(11*var(--u));margin-right:calc(6*var(--u))}
 #chat-log .m .cs{font-weight:700;color:#ffd24d;margin-right:calc(6*var(--u))}
 #chat-log .m .cs.me{color:#4fd07a}
 #chat-log .m .role{font-size:calc(10*var(--u));color:#6b7d6b;margin-right:calc(6*var(--u))}
 #chat-log .sys{color:#6ba4de;font-size:calc(11*var(--u));font-style:italic;margin:calc(4*var(--u)) 0}
 #chat-log .sys.in{color:#4fd07a} #chat-log .sys.out{color:#ff7b84} #chat-log .sys.stale{color:#c8a55a} #chat-log .sys.page{color:#e0b3ff;font-weight:700}
 #users-list{display:block;flex:0 0 var(--users-h,38%);min-height:calc(48*var(--u));overflow-y:auto;padding:calc(8*var(--u)) calc(10*var(--u));border-bottom:1px solid #1e2a22}
 /* roster row = fixed GRID (Oct 4): dot | icon | callsign | role | rtt | age (state = colour) — never wraps, long text ellipsizes,
    so the ONLINE and 'since' columns line up whatever the names and the UI scale */
 .ur{display:grid;grid-template-columns:calc(10*var(--u)) calc(22*var(--u)) calc(118*var(--u)) minmax(0,1fr) calc(48*var(--u)) calc(52*var(--u));align-items:center;
     column-gap:calc(8*var(--u));padding:calc(8*var(--u)) calc(6*var(--u));border-bottom:1px solid #1a2320;user-select:none;cursor:pointer}
 .ur>span{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
 .ur .dot{width:calc(10*var(--u));height:calc(10*var(--u));border-radius:50%;flex:none}
 /* presence: GREEN heartbeat arriving · YELLOW quiet but connected (backgrounded tab) · RED gone (page closed / link lost) */
 .ur.online .dot{background:#4fd07a;box-shadow:0 0 calc(6*var(--u)) #4fd07a} .ur.stale .dot{background:#ffd54a;box-shadow:0 0 calc(6*var(--u)) #ffd54a} .ur.out .dot,.ur.timeout .dot{background:#ff5560;box-shadow:0 0 calc(6*var(--u)) #ff5560}
 .ur .cs{font-weight:700;color:#cfe3cf} .ur.out .cs,.ur.timeout .cs{color:#6b7280}
 .ur .role{font-size:calc(10*var(--u));color:#6b7d6b} .ur .age{text-align:right;font-family:ui-monospace,monospace;font-size:calc(11*var(--u));color:#8b9199}
 .ur .st{font-size:calc(10*var(--u));padding:1px calc(6*var(--u));border-radius:calc(4*var(--u));background:#1e2a22;color:#8b9199;justify-self:start;max-width:100%}
 .ur .ri{text-align:center}
 .ur .st{display:none}                                   /* state is now the colour of the time column */
 .ur .age.g{color:#4fd07a} .ur .age.y{color:#ffd54a} .ur .age.r{color:#ff5560}
 /* RTT column (Oct 4): hub-measured round trip, median of 5 — green < rtt_warn_ms · yellow < rtt_bad_ms · red above or pings unanswered */
 .ur .rtt{text-align:right;font-family:ui-monospace,monospace;font-size:calc(11*var(--u));color:#6b7280}
 .ur .rtt.g{color:#4fd07a} .ur .rtt.y{color:#ffd54a} .ur .rtt.r{color:#ff5560}
 .ur.online .st{color:#4fd07a} .ur.stale .st{color:#ffd54a} .ur.out .st,.ur.timeout .st{color:#ff5560}
 .chat-in{display:flex;gap:calc(6*var(--u));padding:calc(8*var(--u)) calc(10*var(--u));border-top:1px solid #1e2a22}
 .chat-in input{flex:1;background:#0d0f12;border:1px solid #2c313a;border-radius:calc(6*var(--u));color:#cfe3cf;padding:calc(8*var(--u)) calc(10*var(--u));font-size:calc(14*var(--u))}
 #chat-id{position:fixed;inset:0;z-index:14;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,.55)}
 #chat-id.show{display:flex}
 .cid-box{background:#14181c;border:1px solid #4fd07a;border-radius:calc(10*var(--u));padding:calc(16*var(--u));display:flex;flex-direction:column;gap:calc(10*var(--u));min-width:calc(280*var(--u))}
 .cid-t{font-weight:700;color:#9fdf9f}
 .cid-box input,.cid-box select{background:#0d0f12;border:1px solid #2c313a;border-radius:calc(6*var(--u));color:#cfe3cf;padding:calc(8*var(--u)) calc(10*var(--u));font-size:calc(14*var(--u))}
 /* ---- PTT ---- */
 #ptt{position:fixed;left:50%;bottom:calc(110*var(--u));transform:translateX(-50%);width:calc(120*var(--u));height:calc(120*var(--u));border-radius:50%;z-index:9;
      background:radial-gradient(circle,rgba(24,36,28,.75),rgba(0,0,0,.5));border:calc(3*var(--u)) solid rgba(79,208,122,.55);
      display:flex;flex-direction:column;align-items:center;justify-content:center;touch-action:none;user-select:none;cursor:pointer}
 #ptt .ptt-lbl{font-weight:900;font-size:calc(22*var(--u));letter-spacing:calc(2*var(--u));color:#9fdf9f;pointer-events:none}
 #ptt .ptt-to{font-size:calc(11*var(--u));color:#c8a55a;font-family:ui-monospace,monospace;pointer-events:none;margin-top:calc(2*var(--u))}
 #ptt .ptt-rx{font-size:calc(10*var(--u));font-family:ui-monospace,monospace;pointer-events:none;min-height:calc(12*var(--u));margin-top:calc(2*var(--u));display:flex;flex-direction:column;align-items:center;gap:1px}
 #ptt .ptt-rx .pk{color:#6b7280}                                /* gray  = talked, now silent */
 #ptt .ptt-rx .pk.on{color:#4fd07a;font-weight:700}             /* green = playing now */
 #ptt.tx{background:radial-gradient(circle,rgba(150,20,26,.95),rgba(80,10,14,.9));border-color:#ff5560;box-shadow:0 0 calc(22*var(--u)) #ff2b36}
 #ptt.tx .ptt-lbl{color:#fff}
 #ptt.rx{border-color:#4fd07a;border-width:calc(6*var(--u));box-shadow:0 0 calc(18*var(--u)) #2a7a48}   /* thick green ring = playback pending */
 #ptt.nomic{opacity:.45}
 #ptt .orbit{position:absolute;inset:calc(-9*var(--u));width:calc(100% + calc(18*var(--u)));height:calc(100% + calc(18*var(--u)));pointer-events:none;display:none;transform:rotate(-90deg)}
 #ptt.timed .orbit{display:block}
 #ptt .orbit .track{fill:none;stroke:rgba(255,255,255,.08);stroke-width:3}
 #ptt .orbit .arc{fill:none;stroke:#4fd07a;stroke-width:3;stroke-linecap:round;stroke-dasharray:289;stroke-dashoffset:289;transition:stroke-dashoffset .25s linear}
 #ptt .orbit .dot{fill:#eafff0;stroke:#4fd07a;stroke-width:1.5;transition:transform .25s linear;transform-origin:50px 50px}
 #ptt.warn5 .orbit .arc,#ptt.warn5 .orbit .dot{stroke:#ff5560} #ptt.warn5 .orbit .dot{fill:#ff5560}
 #ptt.fdx{border-color:#6ba4de;box-shadow:0 0 calc(22*var(--u)) #3a5a8a;border-style:double}   /* FDX call: glued, blue */
 #ptt.fdx .ptt-lbl{color:#cfe3ff}
 #ptt.vox{border-style:dashed;border-color:#c8a55a}                       /* VOX armed, listening */
 #ptt.vox .ptt-lbl{color:#c8a55a;font-size:calc(18*var(--u))}
 #ptt.vox.tx{border-style:solid}                                          /* VOX triggered → same red as PTT */
 /* ---- VU METERS unit (Oct 3): fixed rainbow revealed from the left (a colour always means the same level), peak-hold tick ---- */
 #vu{position:fixed;left:50%;bottom:calc(245*var(--u));transform:translateX(-50%);width:calc(230*var(--u));height:calc(46*var(--u));z-index:9;border-radius:calc(8*var(--u));
     background:rgba(10,16,12,.55);border:1px solid rgba(79,208,122,.35);padding:calc(4*var(--u)) calc(8*var(--u));box-sizing:border-box;
     display:flex;flex-direction:column;justify-content:space-around;gap:calc(2*var(--u));pointer-events:none;user-select:none}
 body.editing #vu{pointer-events:auto}
 body.novu #vu{display:none}
 #vu .vrow,.vus .vrow{display:flex;align-items:center;gap:calc(6*var(--u));min-height:0;flex:1}
 .vus{display:flex;flex-direction:column;gap:calc(6*var(--u));margin:calc(6*var(--u)) 0 calc(4*var(--u))}
 .vuswrap{display:flex;align-items:stretch;gap:calc(10*var(--u))}
 .vuswrap .vus{flex:1;min-width:0}
 #setup .vloop{flex:none;margin:calc(6*var(--u)) 0 calc(4*var(--u));padding:calc(4*var(--u)) calc(10*var(--u));line-height:1.15;font-size:calc(11*var(--u));min-width:calc(64*var(--u))}
 #setup .vloop.on{background:#c99a1e;color:#1a1400;border-color:#ffd24d}
 .tbtn{padding:calc(8*var(--u)) calc(14*var(--u));margin:calc(4*var(--u)) calc(8*var(--u)) calc(4*var(--u)) 0;border-radius:calc(8*var(--u));border:1px solid #2c313a;background:#1e2a22;color:#eafff0;font:700 calc(12*var(--u))/1 system-ui,sans-serif;cursor:pointer;user-select:none}
 #setup .tbtn.on,.tbtn.on{background:#c99a1e;color:#1a1400;border-color:#ffd24d}   /* beats #setup button */
 .tfreq{display:flex;gap:calc(10*var(--u));align-items:center;flex-wrap:wrap}
 .tfreq label{display:flex;align-items:center;gap:calc(4*var(--u));font-size:calc(12*var(--u));color:#cfd8d2;cursor:pointer}
 .vus .vrow{height:calc(16*var(--u))}
 #vu .vl,.vus .vl{font:700 calc(10*var(--u))/1 system-ui,sans-serif;color:#9fd9b4;width:calc(24*var(--u));flex:none;letter-spacing:.5px}
 #vu .vdb,.vus .vdb{font:600 calc(10*var(--u))/1 ui-monospace,monospace;color:#cfd8d2;width:calc(34*var(--u));text-align:right;flex:none}
 #vu .vbar,.vus .vbar{position:relative;flex:1;height:100%;min-height:calc(8*var(--u));border-radius:calc(3*var(--u));overflow:hidden;background:#0b120e}
 .vrb{position:absolute;inset:0;background:linear-gradient(90deg,#1fbf5a 0%,#5fd45a 40%,#c9e23c 60%,#ffd24d 75%,#ff8a2a 88%,#ff4040 100%)}
 .vcov{position:absolute;top:0;bottom:0;right:0;width:100%;background:rgba(8,12,10,.86);transition:width .08s linear}
 .vpk{position:absolute;top:0;bottom:0;left:0;width:calc(2*var(--u));background:#fff;opacity:.9;transition:left .04s linear}
 /* VOX trigger notch on the MIC bar: amber tick + small triangles top/bottom (Oct 4) */
 .vthr{position:absolute;top:-1px;bottom:-1px;width:calc(2*var(--u));margin-left:-1px;background:#ffb020;display:none;pointer-events:none;box-shadow:0 0 calc(3*var(--u)) #000}
 .vthr::before,.vthr::after{content:"";position:absolute;left:calc(-3*var(--u));border-left:calc(4*var(--u)) solid transparent;border-right:calc(4*var(--u)) solid transparent}
 .vthr::before{top:0;border-top:calc(4*var(--u)) solid #ffb020} .vthr::after{bottom:0;border-bottom:calc(4*var(--u)) solid #ffb020}
 .vbar::after{content:"";position:absolute;top:0;bottom:0;left:75%;width:1px;background:rgba(255,255,255,.25)}   /* −15 dB mark */
 #ptt .vu{position:absolute;left:calc(10*var(--u));right:calc(10*var(--u));bottom:calc(8*var(--u));height:calc(3*var(--u));border-radius:calc(2*var(--u));background:rgba(255,255,255,.08);overflow:hidden;pointer-events:none;display:none}
 #ptt.vox .vu{display:block}
 body:not(.novu) #ptt.vox .vu{display:none}          /* the VU unit replaces the thin VOX bar while it is shown */
 #ptt .vu i{display:block;height:100%;width:0;background:#c8a55a;transition:width .05s linear}
 #ptt.tx .vu i{background:#ff5560}
 #voice-target{background:#0d0f12;border:1px solid #2c313a;border-radius:calc(5*var(--u));color:#c8a55a;padding:calc(3*var(--u)) calc(6*var(--u));font-size:calc(11*var(--u));max-width:calc(150*var(--u))}
 /* ---- layout edit mode ---- */
 #editbtn{color:#c8a55a;border-color:#6a5a2a}
 body.editing #editbtn{background:#c99a1e;color:#1a1400;border-color:#ffd24d}
 #layoutbar{position:fixed;top:calc(44*var(--u));left:50%;transform:translateX(-50%);z-index:12;display:none;gap:calc(6*var(--u));align-items:center;
      flex-wrap:wrap;justify-content:center;padding:calc(6*var(--u)) calc(10*var(--u));border-radius:calc(8*var(--u));background:rgba(20,24,28,.95);border:1px solid #ffd24d}
 body.editing #layoutbar{display:flex}
 #layoutbar .lb-t{font-size:calc(11*var(--u));font-weight:700;color:#ffd24d;letter-spacing:.4px;margin-right:calc(6*var(--u))}
 #layoutbar select,#layoutbar input{background:#0d0f12;border:1px solid #2c313a;border-radius:calc(5*var(--u));color:#cfe3cf;padding:calc(4*var(--u)) calc(7*var(--u));font-size:calc(12*var(--u))}
 #layoutbar input{width:calc(130*var(--u))}
 #layoutbar button{background:#1b2620;border:1px solid #355c3a;border-radius:calc(5*var(--u));color:#cfe3cf;padding:calc(4*var(--u)) calc(10*var(--u));font-size:calc(12*var(--u));cursor:pointer}
 #layoutbar button.danger{border-color:#7a3a3a;color:#e8a0a0}
 #layoutbar button.ok{background:#215c38;border-color:#4fd07a;color:#eafff0;font-weight:700}
 /* movable units get an outline + a resize grip while editing; their normal
    input handlers are suppressed so dragging doesn't drive the gimbal */
 .movable{box-sizing:border-box}
 body.editing .movable{outline:calc(2*var(--u)) dashed #ffd24d;outline-offset:calc(4*var(--u));touch-action:none}
 /* edit shield: a transparent layer over the whole unit that catches every
    pointer event while editing — children keep full contrast, never see input */
 .movable .eshield{display:none;position:absolute;inset:0;z-index:4;cursor:move;touch-action:none;
      background:rgba(255,210,77,.08)}
 body.editing .movable .eshield{display:block}
 .movable .grip{display:none;position:absolute;right:calc(-6*var(--u));bottom:calc(-6*var(--u));width:calc(22*var(--u));height:calc(22*var(--u));border-radius:calc(5*var(--u));z-index:6;
      background:#ffd24d;border:calc(2*var(--u)) solid #1a1400;cursor:nwse-resize;z-index:5;touch-action:none}
 body.editing .movable .grip{display:block}
 /* status lines under the bar */
 #status{position:fixed;top:calc(44*var(--u));left:50%;transform:translateX(-50%);font-size:calc(12*var(--u));color:#9fdf9f;
         background:rgba(0,0,0,.5);padding:calc(4*var(--u)) calc(10*var(--u));border-radius:calc(6*var(--u));z-index:9}
 #capstatus{position:fixed;top:calc(44*var(--u));left:calc(10*var(--u));z-index:11;font-size:calc(11*var(--u));font-weight:700;
      letter-spacing:.5px;padding:calc(3*var(--u)) calc(10*var(--u));border-radius:calc(5*var(--u));background:rgba(0,0,0,.6);
      color:#9aa4ae;border:1px solid #333;pointer-events:none}
 #capstatus.rec{color:#fff;background:rgba(122,31,40,.9);border-color:#ff5560}
 /* disarmed: gimbal-side controls go witness-only; local ones stay live */
 body.disarmed #gcap,body.disarmed #desig{opacity:.4;pointer-events:none}
 #crosshair{position:fixed;z-index:8;width:calc(40*var(--u));height:calc(40*var(--u));margin:calc(-20*var(--u)) 0 0 calc(-20*var(--u));pointer-events:none;
      display:none;border:calc(2*var(--u)) solid #ffd24d;border-radius:50%;box-shadow:0 0 calc(8*var(--u)) #c99a1e}
 #crosshair::before,#crosshair::after{content:"";position:absolute;background:#ffd24d}
 #crosshair::before{left:50%;top:calc(-8*var(--u));width:calc(2*var(--u));height:calc(14*var(--u));margin-left:-1px}
 #crosshair::after{top:50%;left:calc(-8*var(--u));height:calc(2*var(--u));width:calc(14*var(--u));margin-top:-1px}
 #crosshair.show{display:block}
 /* wire-confirmed track marker (driven by /api/state track callback) */
 #trackmark{position:fixed;z-index:9;width:calc(52*var(--u));height:calc(52*var(--u));margin:calc(-26*var(--u)) 0 0 calc(-26*var(--u));pointer-events:none;
      display:none;border:calc(2*var(--u)) solid #ff3b46;border-radius:50%;box-shadow:0 0 calc(12*var(--u)) #ff2b36}
 #trackmark::before,#trackmark::after{content:"";position:absolute;background:#ff3b46}
 #trackmark::before{left:50%;top:calc(-10*var(--u));width:calc(2*var(--u));height:calc(18*var(--u));margin-left:-1px}
 #trackmark::after{top:50%;left:calc(-10*var(--u));height:calc(2*var(--u));width:calc(18*var(--u));margin-top:-1px}
 #trackmark.show{display:block}
 /* setup panel (local capture folder) */
 .closer{cursor:pointer;user-select:none} .closer .x{font-size:calc(11*var(--u));color:#6b7280;margin-left:calc(4*var(--u))} .closer:active{opacity:.6}
 #setup .stabs{display:flex;gap:calc(4*var(--u));margin:calc(6*var(--u)) 0 calc(10*var(--u));border-bottom:1px solid #2c313a}
 #setup .stabs label{padding:calc(6*var(--u)) calc(12*var(--u));font-size:calc(12*var(--u));color:#8b9199;cursor:pointer;border:1px solid transparent;border-bottom:none;border-radius:calc(6*var(--u)) calc(6*var(--u)) 0 0;user-select:none}
 #setup .stabs label:has(input:checked){color:#eafff0;background:#1e2a22;border-color:#2c313a}
 #setup .stabs input{display:none}
 #setup .spage{display:none} #setup .spage.on{display:block}
 #setup .cs{display:flex;flex-direction:column;gap:calc(3*var(--u));margin:calc(6*var(--u)) 0}
 #setup .cs label{font-size:calc(12*var(--u));color:#cfe3cf} #setup .cs label span{color:#ffd24d;font-family:ui-monospace,monospace;margin-left:calc(6*var(--u))}
 #setup .cs input[type=range]{width:100%}
 #setup .cs input[type=number],#setup .cs select{background:#0d0f12;border:1px solid #2c313a;border-radius:calc(5*var(--u));color:#cfe3cf;padding:calc(5*var(--u)) calc(8*var(--u));font-size:calc(12*var(--u))}
 #setup{position:fixed;top:0;right:0;bottom:0;width:min(calc(360*var(--u)),86vw);z-index:20;
      background:rgba(12,15,18,.97);border-left:1px solid #2c313a;padding:calc(18*var(--u));
      transform:translateX(100%);transition:transform .18s ease;overflow:auto}
 #setup.show{transform:translateX(0)}
 #setup .sh{font-size:calc(13*var(--u));font-weight:700;color:#9fdf9f;letter-spacing:.5px;margin-bottom:calc(14*var(--u))}
 #setup .srow{margin:calc(10*var(--u)) 0;font-size:calc(13*var(--u));color:#cfe3cf}
 #setup .srow b{color:#8fce8f}
 #setup button{font-size:calc(13*var(--u));padding:calc(8*var(--u)) calc(14*var(--u));border-radius:calc(6*var(--u));background:#1b2620;
      border:1px solid #355c3a;color:#cfe3cf;cursor:pointer}
 #setup button:hover{border-color:#5a9c63}
 #setup .snote{font-size:calc(11*var(--u));color:#8b9199;line-height:1.5;margin:calc(12*var(--u)) 0}
 #toast{position:fixed;bottom:calc(22*var(--u));left:50%;transform:translateX(-50%) translateY(calc(20*var(--u)));z-index:21;
      background:rgba(20,24,28,.95);border:1px solid #355c3a;color:#cfe3cf;font-size:calc(12*var(--u));
      padding:calc(8*var(--u)) calc(16*var(--u));border-radius:calc(6*var(--u));opacity:0;pointer-events:none;transition:all .2s}
 #toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
 /* control overlay */
 #joy{position:fixed;left:calc(8*var(--u));bottom:calc(30*var(--u));width:calc(250*var(--u));height:calc(250*var(--u));border-radius:50%;
      background:radial-gradient(circle,rgba(24,36,28,.55),rgba(0,0,0,.35));
      border:calc(2*var(--u)) solid rgba(79,208,122,.5);touch-action:none;cursor:grab;z-index:9}
 #joy::before,#joy::after{content:'';position:absolute;background:rgba(79,208,122,.25)}
 #joy::before{left:50%;top:8%;bottom:8%;width:1px;transform:translateX(-50%)}
 #joy::after{top:50%;left:8%;right:8%;height:1px;transform:translateY(-50%)}
 #knob{width:calc(64*var(--u));height:calc(64*var(--u));border-radius:50%;background:rgba(47,111,79,.9);
       border:calc(2*var(--u)) solid #4fd07a;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
       box-shadow:0 0 calc(12*var(--u)) #2f6f4f;pointer-events:none;z-index:2}
 #shadow{width:calc(52*var(--u));height:calc(52*var(--u));border-radius:50%;background:transparent;
       border:calc(2*var(--u)) dashed rgba(107,164,222,.9);position:absolute;left:50%;top:50%;
       transform:translate(-50%,-50%);pointer-events:none;z-index:1;
       transition:left .08s linear,top .08s linear}
 .rail{position:fixed;right:calc(16*var(--u));bottom:calc(22*var(--u));z-index:9;display:flex;flex-direction:row;gap:calc(10*var(--u));align-items:flex-end}
 .rail,.modes{position:fixed}
 /* vertical spring-return zoom slider (rectangular, up/down only) */
 /* orientation: data-o = 0 (vertical, + on top), 90 (horizontal, + right),
    180 (vertical, + bottom), 270 (horizontal, + left). Real layout, not a CSS
    rotate — so hit-testing, labels and the layout box stay correct. */
 #zoomsl{position:relative;width:calc(56*var(--u));height:calc(190*var(--u));border-radius:calc(10*var(--u));touch-action:none;cursor:ns-resize;user-select:none;
      background:linear-gradient(180deg,rgba(24,36,28,.6),rgba(0,0,0,.4));border:calc(2*var(--u)) solid rgba(79,208,122,.5)}
 #zoomsl::before{content:"";position:absolute;left:50%;top:calc(14*var(--u));bottom:calc(14*var(--u));width:calc(2*var(--u));margin-left:-1px;background:rgba(79,208,122,.25)}
 #zoomsl::after{content:"";position:absolute;left:calc(8*var(--u));right:calc(8*var(--u));top:50%;height:1px;margin-top:-1px;background:rgba(79,208,122,.35)}
 #zoomsl .zknob{position:absolute;left:calc(6*var(--u));right:calc(6*var(--u));height:calc(34*var(--u));top:50%;transform:translateY(-50%);border-radius:calc(7*var(--u));
      background:rgba(47,111,79,.9);border:calc(2*var(--u)) solid #4fd07a;box-shadow:0 0 calc(10*var(--u)) #2f6f4f;pointer-events:none;
      transition:top .08s ease-out}
 #zoomsl.live .zknob{transition:none}
 #zoomsl.zin .zknob{background:rgba(201,154,30,.9);border-color:#ffd24d;box-shadow:0 0 calc(10*var(--u)) #c99a1e}
 #zoomsl.zout .zknob{background:rgba(60,100,150,.9);border-color:#6ba4de;box-shadow:0 0 calc(10*var(--u)) #3a5a8a}
 #zoomsl .zlbl{position:absolute;left:0;right:0;text-align:center;font-size:calc(18*var(--u));font-weight:700;color:#8fce8f;pointer-events:none}
 #zoomsl .zlbl.top{top:calc(2*var(--u))} #zoomsl .zlbl.bot{bottom:0}
 /* --- horizontal (90 / 270): swap axes --- */
 #zoomsl.horiz{width:calc(190*var(--u));height:calc(56*var(--u));cursor:ew-resize}
 #zoomsl.horiz::before{left:calc(14*var(--u));right:calc(14*var(--u));top:50%;bottom:auto;width:auto;height:calc(2*var(--u));margin:-1px 0 0 0}
 #zoomsl.horiz::after{top:calc(8*var(--u));bottom:calc(8*var(--u));left:50%;right:auto;height:auto;width:1px;margin:0 0 0 -1px}
 #zoomsl.horiz .zknob{top:calc(6*var(--u));bottom:calc(6*var(--u));left:50%;right:auto;height:auto;width:calc(34*var(--u));transform:translateX(-50%)}
 #zoomsl.horiz .zlbl{top:0;bottom:0;left:auto;right:auto;width:calc(22*var(--u));display:flex;align-items:center;justify-content:center}
 #zoomsl.horiz .zlbl.top{right:calc(2*var(--u))}   /* "+" side */
 #zoomsl.horiz .zlbl.bot{left:calc(2*var(--u))}    /* "−" side */
 /* 270: "+" on the left */
 #zoomsl.horiz.flip .zlbl.top{right:auto;left:calc(2*var(--u))}
 #zoomsl.horiz.flip .zlbl.bot{left:auto;right:calc(2*var(--u))}
 /* 180: vertical with "+" at the bottom */
 #zoomsl.flip:not(.horiz) .zlbl.top{top:auto;bottom:0}
 #zoomsl.flip:not(.horiz) .zlbl.bot{bottom:auto;top:calc(2*var(--u))}
 body.disarmed #zoomsl{opacity:.5;pointer-events:none}
 .modes .grp{display:flex;gap:calc(6*var(--u))}
 .modes .sep{width:1px;align-self:stretch;margin:0 calc(6*var(--u));background:rgba(79,208,122,.35)}
 .modes .ob.ctx{margin-left:calc(8*var(--u));border-style:dashed;min-width:calc(64*var(--u));text-align:center}
 .modes{position:fixed;left:50%;bottom:calc(22*var(--u));transform:translateX(-50%);z-index:9;
        display:flex;gap:calc(8*var(--u));flex-wrap:wrap;justify-content:center;max-width:60vw}
 .ob{background:rgba(27,38,32,.8);border:1px solid rgba(79,208,122,.45);border-radius:calc(7*var(--u));
     color:#c7e8c7;padding:calc(10*var(--u)) calc(14*var(--u));font-size:calc(13*var(--u));font-weight:600;cursor:pointer;user-select:none}
 .ob:hover{border-color:#5a9c63}
 .ob.warn{background:rgba(201,154,30,.92);border-color:#ffd24d;color:#1a1400}
 .ob.hot{background:rgba(192,32,42,.95);border-color:#ff5560;color:#fff;box-shadow:0 0 calc(10*var(--u)) #c0202a}
 .ob.active{background:rgba(33,92,56,.95);border-color:#4fd07a;color:#eafff0}
 /* INJECT off: overlay controls witness-only — dim + non-interactive, but the
    active states, the shadow knob, and the arm button stay live */
 body.disarmed .ob,
 body.disarmed .zoom .ob,
 body.disarmed #joy{ pointer-events:none; opacity:.5 }
 body.disarmed .ob.active{ opacity:1 }
 body.disarmed #shadow{ opacity:1 }
 body.disarmed #knob{ opacity:.35 }
 .ob.wide{min-width:calc(96*var(--u));text-align:center}

 #joyval{position:fixed;left:calc(12*var(--u));bottom:calc(288*var(--u));z-index:9;font:calc(11*var(--u)) ui-monospace,monospace;
         color:#c8a55a;background:rgba(0,0,0,.4);padding:calc(2*var(--u)) calc(6*var(--u));border-radius:calc(4*var(--u))}
 #gpstatus{position:fixed;left:calc(12*var(--u));bottom:calc(312*var(--u));z-index:9;font:calc(11*var(--u)) ui-monospace,monospace;
         color:#8fae8f;background:rgba(0,0,0,.4);padding:calc(2*var(--u)) calc(6*var(--u));border-radius:calc(4*var(--u))}

 /* chat: PTT button left of the text field; roster hold/tx states */
 #chat-ptt{flex:none;border-color:#2e4a3a;color:#8fce8f;user-select:none;touch-action:none;min-width:calc(54*var(--u));text-align:center}
 #chat-ptt.tx{background:#7a1f1f;border-color:#ff5560;color:#fff;box-shadow:0 0 calc(10*var(--u)) #ff2b36}      /* transmitting (same as the big button) */
 #chat-ptt.rx{border-color:#4fd07a;box-shadow:0 0 calc(8*var(--u)) #2a7a48}                                     /* receiving */
 #chat-ptt.fdx{border-color:#6ba4de;border-style:double;color:#cfe3ff;box-shadow:0 0 calc(10*var(--u)) #3a5a8a}  /* CALL */
 #chat-ptt.vox{border-style:dashed;border-color:#c8a55a;color:#e6cf8a}                             /* VOX armed */
 .ur{touch-action:none}
 .ur.hold{background:rgba(79,208,122,.10)}
 .ur.tx{background:rgba(255,85,96,.25);outline:1px solid #ff5560}
 .ur.rx{background:rgba(79,208,122,.22);outline:1px solid #4fd07a}      /* receiving audio from this user */
 .ur.busy{background:rgba(181,124,255,.22);outline:1px solid #b57cff}   /* busy: talking with / listening to someone else */
 .ur.duplex{background:rgba(255,213,74,.25);outline:1px solid #ffd54a}  /* both: I transmit to them AND receive from them */
