/* Magic PTT SL — Living Floor. Offline-only. No webfonts. */

:root {
  color-scheme: dark;
  --ink: #040308;
  --ink-2: #0b0910;
  --plate: rgba(14, 12, 18, 0.55);
  --line: rgba(255, 255, 255, 0.08);
  --bone: #efe6d6;
  --mute: #8a8174;
  --ok: #3dffc5;
  --tx: #ff2d3a;
  --sl: #2ee6d6;
  --sl-rgb: 46, 230, 214;
  --floor: #2ee6d6;
  --font-display: Bahnschrift, "Segoe UI Condensed", "Arial Narrow", "Helvetica Neue", sans-serif;
  --font-ui: "Segoe UI", system-ui, sans-serif;
  --font-mono: "Cascadia Mono", Consolas, "ui-monospace", monospace;
  --floor-size: min(78vmin, 860px);
  --tx-heat: 1;
}

body[data-room="sl-command"] {
  --sl: #2f6bff;
  --sl-rgb: 47, 107, 255;
  --floor: #2f6bff;
}
body[data-room="sl-check-in"] {
  --sl: #2ee6d6;
  --sl-rgb: 46, 230, 214;
  --floor: #2ee6d6;
}
body[data-room="sl-welfare"] {
  --sl: #b07cff;
  --sl-rgb: 176, 124, 255;
  --floor: #b07cff;
}
body[data-room="sl-medical"] {
  --sl: #e11d3a;
  --sl-rgb: 225, 29, 58;
  --floor: #e11d3a;
}
body[data-room="sl-logistics"] {
  --sl: #f5c542;
  --sl-rgb: 245, 197, 66;
  --floor: #f5c542;
}
body[data-room="sl-tactical"] {
  --sl: #ff7a18;
  --sl-rgb: 255, 122, 24;
  --floor: #ff7a18;
}
/* Magic Mesh (2026-11-07): lime, the Magic Mesh colour (his pick over silver-white: "it stands out") */
body[data-room="sl-mesh"] {
  --sl: #a3e635;
  --sl-rgb: 163, 230, 53;
  --floor: #a3e635;
}

body[data-rate="6"] { --tx-heat: 0.52; }
body[data-rate="8"] { --tx-heat: 0.74; }
body[data-rate="12"] { --tx-heat: 1; }

body[data-desk="balanced"] { --floor-size: min(70vmin, 780px); }
body[data-desk="net"] { --floor-size: min(76vmin, 860px); }
body[data-desk="dispatch"] { --floor-size: min(56vmin, 620px); }

* { box-sizing: border-box; }

html, body { margin: 0; min-height: 100%; }

body {
  font-family: var(--font-ui);
  background: var(--ink);
  color: var(--bone);
  min-height: 100vh;
  overflow-x: hidden;
  transition: background 0.55s ease;
}

body.sl-in {
  height: 100vh;
  overflow: hidden;
}

.sl-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 80% 70% at 50% 42%, rgba(var(--sl-rgb), 0.22), transparent 58%),
    radial-gradient(circle at 50% 100%, rgba(var(--sl-rgb), 0.08), transparent 40%),
    #040308;
  transition: background 0.6s ease;
}

body.sl-tx .sl-bg {
  background:
    radial-gradient(ellipse 90% 80% at 50% 45%, rgba(var(--sl-rgb), 0.38), transparent 60%),
    #040308;
}

.sl-ambience {
  position: fixed;
  inset: -10%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(circle at 50% 48%, rgba(var(--sl-rgb), 0.16), transparent 42%);
  filter: blur(40px);
  animation: sl-ambience 8s ease-in-out infinite;
}

body.sl-tx .sl-ambience {
  animation-duration: 1.4s;
  background: radial-gradient(circle at 50% 48%, rgba(var(--sl-rgb), 0.32), transparent 46%);
}

@keyframes sl-ambience {
  0%, 100% { opacity: 0.55; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.06); }
}

.sl-spine {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  z-index: 6;
  background: linear-gradient(180deg, transparent, var(--sl), transparent);
  opacity: 0.4;
  pointer-events: none;
}

.sl-vignette {
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 48%, rgba(0, 0, 0, 0.55) 100%);
}

.sl-scan {
  display: none;
}

body.sl-in .sl-scan,
.sl-scan--on {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: repeating-linear-gradient(
    180deg,
    transparent 0 3px,
    rgba(0, 0, 0, 0.055) 3px 4px
  );
  opacity: 0.2;
  mix-blend-mode: multiply;
}

.sl-mast {
  position: relative;
  z-index: 8;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.7rem 1rem;
  padding: 0.4rem 1rem 0.38rem 1.05rem;
  background: rgba(6, 5, 10, 0.45);
  backdrop-filter: blur(16px) saturate(1.15);
  border-bottom: 1px solid rgba(var(--sl-rgb), 0.22);
}

body.sl-tx .sl-mast {
  border-bottom-color: rgba(var(--sl-rgb), 0.55);
  box-shadow: 0 12px 36px rgba(var(--sl-rgb), 0.12);
}

.sl-word {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
}

.sl-word__magic {
  font-family: var(--font-display);
  font-size: 1.02rem;
  letter-spacing: 0.28em;
  font-weight: 700;
  color: var(--bone);
  text-shadow: 0 0 18px rgba(255, 255, 255, 0.08);
}

.sl-word__sl {
  position: relative;
  font-family: var(--font-display);
  font-size: 2.45rem;
  line-height: 0.78;
  letter-spacing: 0.16em;
  font-weight: 800;
  color: var(--sl);
  text-shadow:
    0 0 18px rgba(var(--sl-rgb), 0.55),
    0 0 42px rgba(var(--sl-rgb), 0.28);
  transition: color 0.45s ease, text-shadow 0.45s ease;
}

.sl-word__sl::after {
  content: "";
  position: absolute;
  left: 0.04em;
  right: 0.04em;
  bottom: -0.12em;
  height: 2px;
  background: var(--sl);
  box-shadow: 0 0 10px rgba(var(--sl-rgb), 0.7);
  opacity: 0.8;
}

.sl-mast__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.15rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--mute);
}

.sl-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
  justify-content: center;
}

.sl-flag {
  min-width: 4.6rem;
  padding: 0.16rem 0.48rem 0.2rem;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid rgba(255, 255, 255, 0.05);
}

.sl-flag span {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.2em;
  color: var(--mute);
}

.sl-flag b {
  font-family: var(--font-display);
  font-size: 0.88rem;
  letter-spacing: 0.1em;
}

.sl-flag.is-ok b { color: var(--ok); }
.sl-flag.is-hot b { color: var(--sl); text-shadow: 0 0 12px rgba(var(--sl-rgb), 0.45); }
.sl-flag.is-tx { border-color: rgba(var(--sl-rgb), 0.45); background: rgba(var(--sl-rgb), 0.1); }
.sl-flag.is-tx b { color: var(--sl); }
#roomMark { color: var(--sl); }

.sl-mast__end {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.sl-admin-link {
  display: inline-block;
  width: auto;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  font-weight: 600;
  color: var(--mute);
  padding: 0.2rem 0;
  border: none;
  background: none;
}

.sl-admin-link:hover { color: var(--sl); }

.sl-admin-link--here {
  color: var(--sl);
  opacity: 0.85;
}

.sl-exit,
#returnLogin,
#returnChat {
  display: inline-block;
  width: auto;
  text-decoration: none;
  text-align: center;
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  font-weight: 700;
  color: var(--mute);
  border: 1px solid rgba(255, 255, 255, 0.08);
  padding: 0.42rem 0.68rem;
  background: rgba(0, 0, 0, 0.25);
}

.sl-exit:hover,
#returnLogin:hover,
#returnChat:hover {
  color: var(--sl);
  border-color: rgba(var(--sl-rgb), 0.4);
}

.wrap {
  position: relative;
  z-index: 1;
  max-width: 44rem;
  margin: 0 auto;
  padding: 1rem 1rem 1.4rem;
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 4.4rem);
  gap: 0.75rem;
}

body.sl-in .sl-gate-wrap { display: none; }

.sl-banner {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: rgba(var(--sl-rgb), 0.85);
  border: 1px solid rgba(var(--sl-rgb), 0.28);
  background: rgba(var(--sl-rgb), 0.07);
  padding: 0.6rem 0.75rem;
}

.plate {
  background: rgba(16, 13, 18, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.08);
  padding: 0.9rem;
  backdrop-filter: blur(18px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 20px 50px rgba(0, 0, 0, 0.4);
}

.plate-kicker,
.sl-pane-kicker,
.sl-drawer__tab {
  font-family: var(--font-display);
  font-size: 0.64rem;
  letter-spacing: 0.24em;
  color: var(--sl);
}

.plate-title {
  font-family: var(--font-display);
  font-size: 1.35rem;
  letter-spacing: 0.08em;
  margin: 0 0 0.35rem;
}

.sub { color: var(--mute); font-size: 0.85rem; margin: 0; }

label {
  display: block;
  font-family: var(--font-display);
  font-size: 0.64rem;
  letter-spacing: 0.18em;
  color: var(--mute);
  margin: 0.5rem 0 0.22rem;
}

input, textarea, select, button {
  width: 100%;
  font: inherit;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(0, 0, 0, 0.35);
  color: var(--bone);
  padding: 0.62rem 0.72rem;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: rgba(var(--sl-rgb), 0.55);
  box-shadow: 0 0 0 1px rgba(var(--sl-rgb), 0.25);
}

button {
  font-family: var(--font-display);
  letter-spacing: 0.12em;
  font-weight: 700;
  cursor: pointer;
  background: var(--sl);
  color: #081014;
  border: none;
}

button.secondary {
  background: rgba(0, 0, 0, 0.25);
  color: var(--mute);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

/* 0.4 was low enough that a disabled label vanished into the background. The
   explicit palettes below carry the state instead. */
button:disabled { opacity: 0.62; cursor: not-allowed; }

#login { display: block; }

.sl-sr-only,
.room-pick-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

.sl-glass {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.018));
  backdrop-filter: blur(18px) saturate(1.18);
  border: 1px solid rgba(255, 255, 255, 0.075);
  box-shadow:
    0 18px 46px rgba(0, 0, 0, 0.42),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* —— Living Floor —— */

.sl-desk {
  display: none;
  position: relative;
  z-index: 3;
  height: calc(100vh - 3.35rem);
}

body.sl-in .sl-desk { display: block; }

.sl-stage {
  position: absolute;
  inset: 0.4rem 4.4rem 3.4rem;
}

.sl-floor {
  position: absolute;
  left: 50%;
  top: 46%;
  width: var(--floor-size);
  height: var(--floor-size);
  transform: translate(-50%, -50%);
  z-index: 2;
  transition: width 0.65s cubic-bezier(0.22, 1, 0.36, 1), height 0.65s cubic-bezier(0.22, 1, 0.36, 1);
}

.sl-floor__halo {
  position: absolute;
  inset: -8%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--sl-rgb), 0.22), transparent 68%);
  filter: blur(18px);
  animation: sl-breathe 4.8s ease-in-out infinite;
}

body.sl-tx .sl-floor__halo {
  animation-duration: 1.15s;
  background: radial-gradient(circle, rgba(var(--sl-rgb), 0.45), transparent 70%);
}

.sl-floor__disc {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 42%, rgba(var(--sl-rgb), 0.14), rgba(4, 3, 8, 0.82) 72%);
  border: 1px solid rgba(var(--sl-rgb), 0.35);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.6),
    0 0 60px rgba(var(--sl-rgb), 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.sl-floor__scope {
  position: absolute;
  inset: 6%;
  border-radius: 50%;
  pointer-events: none;
}

.sl-ptt__rings {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle, transparent 22%, rgba(var(--sl-rgb), 0.2) 23%, transparent 24%),
    radial-gradient(circle, transparent 40%, rgba(var(--sl-rgb), 0.16) 41%, transparent 42%),
    radial-gradient(circle, transparent 58%, rgba(var(--sl-rgb), 0.14) 59%, transparent 60%),
    radial-gradient(circle, transparent 76%, rgba(var(--sl-rgb), 0.12) 77%, transparent 78%);
  animation: sl-rings 5.4s ease-in-out infinite;
}

body.sl-tx .sl-ptt__rings {
  animation: sl-rings-hot calc(1.3s / var(--tx-heat)) ease-in-out infinite;
}

.sl-ptt__cross {
  position: absolute;
  inset: 10%;
  background:
    linear-gradient(90deg, transparent 49.5%, rgba(var(--sl-rgb), 0.2) 49.5% 50.5%, transparent 50.5%),
    linear-gradient(0deg, transparent 49.5%, rgba(var(--sl-rgb), 0.2) 49.5% 50.5%, transparent 50.5%);
}

.sl-ptt__sweep {
  position: absolute;
  inset: 6%;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0 76%, rgba(var(--sl-rgb), 0.28) 90%, transparent 100%);
  animation: sl-sweep 8s linear infinite;
  mix-blend-mode: screen;
}

body.sl-tx .sl-ptt__sweep { animation-duration: calc(2.2s / var(--tx-heat)); }

.sl-ptt__ripple {
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
}

body.sl-tx .sl-ptt__ripple {
  opacity: 1;
  background: radial-gradient(circle, rgba(var(--sl-rgb), calc(0.28 * var(--tx-heat))), transparent 64%);
  animation: sl-ripple calc(1.1s / var(--tx-heat)) ease-out infinite;
}

.sl-floor__core {
  position: absolute;
  left: 16%;
  right: 16%;
  top: 22%;
  text-align: center;
  z-index: 2;
  pointer-events: none;
}

.sl-ptt__air-lab {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(0.7rem, 1.4vw, 0.95rem);
  letter-spacing: 0.36em;
  color: var(--mute);
  transition: transform 0.3s ease, color 0.3s ease, letter-spacing 0.3s ease;
}

body[data-air="standby"] .sl-ptt__air-lab { color: var(--ok); }
body[data-air="onair"] .sl-ptt__air-lab {
  color: var(--sl);
  letter-spacing: 0.46em;
  transform: scale(1.12);
  text-shadow: 0 0 18px rgba(var(--sl-rgb), 0.45);
}
body[data-air="tx"] .sl-ptt__air-lab {
  color: var(--sl);
  letter-spacing: 0.54em;
  transform: scale(1.28);
  text-shadow: 0 0 24px rgba(var(--sl-rgb), 0.65);
}

#talkingNow {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2.6vw, 2.1rem);
  letter-spacing: 0.08em;
  font-weight: 700;
  color: #5c564e;
  min-height: 1.15em;
  margin-top: 0.25rem;
}

body[data-desk="net"] #talkingNow { font-size: clamp(1.4rem, 3.2vw, 2.6rem); }
#talkingNow.is-hot,
#talkingNow.is-tx { color: var(--sl); text-shadow: 0 0 18px rgba(var(--sl-rgb), 0.4); }
#talkingNow:empty::after {
  content: "NO TRAFFIC";
  color: #4a453e;
  font-size: 0.92rem;
  letter-spacing: 0.2em;
}

#pttBtn {
  position: absolute;
  z-index: 3;
  left: 11%;
  right: 11%;
  bottom: 6%;
  height: 23%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 50% 50% 46% 46% / 18% 18% 88% 88%;
  background: linear-gradient(180deg, rgba(40, 36, 42, 0.55), rgba(10, 8, 12, 0.95));
  color: #6f675c;
  box-shadow:
    0 10px 0 rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.14);
  transform: translateY(-5px);
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

#pttBtn .ptt-key__word {
  font-family: var(--font-display);
  font-size: clamp(0.78rem, 1.6vw, 1.15rem);
  letter-spacing: 0.18em;
}

#pttBtn .ptt-key__sub {
  font-family: var(--font-mono);
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  color: #5a534a;
}

#pttBtn.ready {
  background: linear-gradient(180deg, rgba(var(--sl-rgb), 0.38), rgba(8, 8, 12, 0.92));
  color: var(--bone);
  border-color: rgba(var(--sl-rgb), 0.55);
  box-shadow:
    0 10px 0 rgba(0, 0, 0, 0.45),
    0 0 34px rgba(var(--sl-rgb), 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.22);
  animation: sl-key-arm 2.6s ease-in-out infinite;
}

#pttBtn.ready .ptt-key__sub { color: var(--sl); }

#pttBtn.tx {
  background: linear-gradient(180deg, rgba(var(--sl-rgb), 0.95), rgba(var(--sl-rgb), 0.35));
  color: #fff;
  transform: translateY(3px);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.5), 0 0 calc(24px + 28px * var(--tx-heat)) rgba(var(--sl-rgb), 0.55);
  animation: none;
}

#pttBtn:disabled {
  background: #121014;
  color: #3f3a36;
  box-shadow: none;
  transform: none;
  animation: none;
}

@keyframes sl-breathe {
  0%, 100% { opacity: 0.55; transform: scale(0.98); }
  50% { opacity: 1; transform: scale(1.04); }
}
@keyframes sl-rings {
  0%, 100% { transform: scale(0.97); opacity: 0.7; }
  50% { transform: scale(1.04); opacity: 1; }
}
@keyframes sl-rings-hot {
  0% { transform: scale(0.96); }
  50% { transform: scale(1.08); }
  100% { transform: scale(0.96); }
}
@keyframes sl-sweep { to { transform: rotate(360deg); } }
@keyframes sl-ripple {
  0% { transform: scale(0.82); opacity: 0.8; }
  100% { transform: scale(1.2); opacity: 0; }
}
@keyframes sl-key-arm {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.14); }
}

.sl-orbit {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

.room-plate {
  --chip: var(--sl);
  pointer-events: auto;
  position: absolute;
  width: 6.6rem;
  margin: 0;
  padding: 0.42rem 0.4rem 0.38rem;
  transform: translate(-50%, -50%) scale(0.92);
  text-align: center;
  border-radius: 999px;
  background: rgba(8, 7, 12, 0.62);
  color: #6d665c;
  border: 1px solid rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(12px);
  filter: saturate(0.4);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), filter 0.4s ease, box-shadow 0.4s ease, color 0.4s ease;
}

.room-plate[data-room="sl-command"] { --chip: #2f6bff; }
.room-plate[data-room="sl-check-in"] { --chip: #2ee6d6; }
.room-plate[data-room="sl-welfare"] { --chip: #b07cff; }
.room-plate[data-room="sl-medical"] { --chip: #e11d3a; }
.room-plate[data-room="sl-logistics"] { --chip: #f5c542; }
.room-plate[data-room="sl-tactical"] { --chip: #ff7a18; }
.room-plate[data-room="sl-mesh"] { --chip: #a3e635; }

.room-plate__floor {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.16em;
}

.room-plate__code {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.14em;
}

.room-plate__name {
  display: block;
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
}

.room-plate.is-on {
  filter: none;
  color: var(--chip);
  transform: translate(-50%, -50%) scale(1.18);
  border-color: var(--chip);
  background: color-mix(in srgb, var(--chip) 18%, rgba(6, 5, 10, 0.75));
  box-shadow: 0 0 28px color-mix(in srgb, var(--chip) 40%, transparent);
  z-index: 4;
}

.room-plate.is-on .room-plate__floor,
.room-plate.is-on .room-plate__code { color: var(--chip); }

/* Drawers */
.sl-drawer {
  position: absolute;
  z-index: 5;
  display: flex;
  overflow: hidden;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1), width 0.55s cubic-bezier(0.22, 1, 0.36, 1), height 0.55s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease;
}

.sl-drawer__tab {
  flex: none;
  width: auto;
  background: transparent;
  border: none;
  color: var(--sl);
  padding: 0.55rem 0.45rem;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  letter-spacing: 0.2em;
}

.sl-drawer__body {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: none;
  flex-direction: column;
  padding: 0.55rem 0.65rem 0.6rem;
}

.sl-drawer--traffic {
  left: 0.55rem;
  top: 0.75rem;
  bottom: 3.75rem;
  width: 2.35rem;
  flex-direction: row;
  transform: none;
}

.sl-drawer--here {
  right: 0.55rem;
  top: 0.75rem;
  bottom: 3.75rem;
  width: 2.85rem;
  flex-direction: row-reverse;
  transform: none;
}

.sl-drawer__tab--ops {
  padding: 0.75rem 0.42rem;
  color: #081014;
  background: var(--sl);
  box-shadow: 0 0 22px rgba(var(--sl-rgb), 0.55);
  letter-spacing: 0.22em;
  font-weight: 800;
  font-size: 0.8rem;
}

.sl-ops-tab__n {
  display: inline-flex;
  min-width: 1.15rem;
  height: 1.15rem;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: #081014;
  color: var(--sl) !important;
  font-size: 0.68rem;
  letter-spacing: 0;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

/* STATION and LOG render only while open -- they are opened from the desk bar
   and have no tab of their own, so a closed pane would be an empty lit box.
   Kept here beside the other drawer rules rather than as an override in the
   page: body.sl-can-log .sl-drawer--log used to win on specificity and put the
   shell back. */
.sl-drawer--log {
  display: none;
  /* Left of the RADIO drawer's open width so the two never overlap. */
  left: calc(50% - 24.4rem);
  bottom: 3.65rem;
  width: 5.4rem;
  height: 2.15rem;
  flex-direction: column;
  transform: none;
}

.sl-drawer--radio { display: none; }
body.sl-open-radio .sl-drawer--radio:not([hidden]),
body.sl-can-log.sl-open-log .sl-drawer--log:not([hidden]) { display: flex; }

body.sl-open-log .sl-drawer--log {
  width: min(28rem, 40vw);
  height: min(24rem, 44vh);
}

body.sl-open-log .sl-drawer--log .sl-drawer__body {
  display: flex;
}

.sl-drawer--radio {
  left: 50%;
  bottom: 3.65rem;
  width: 8.5rem;
  height: 2.15rem;
  transform: translate(-50%, 0);
  flex-direction: column;
}

body[data-desk="dispatch"] .sl-drawer--traffic,
body.sl-open-traffic .sl-drawer--traffic {
  width: min(32rem, 34vw);
}

body[data-desk="dispatch"] .sl-drawer--traffic .sl-drawer__body,
body.sl-open-traffic .sl-drawer--traffic .sl-drawer__body,
body[data-desk="balanced"] .sl-drawer--here .sl-drawer__body,
body[data-desk="dispatch"] .sl-drawer--here .sl-drawer__body,
body.sl-open-here .sl-drawer--here .sl-drawer__body,
body.sl-open-radio .sl-drawer--radio .sl-drawer__body {
  display: flex;
}

body[data-desk="balanced"] .sl-drawer--here,
body[data-desk="dispatch"] .sl-drawer--here,
body.sl-open-here .sl-drawer--here {
  width: 16.5rem;
}

body.sl-open-radio .sl-drawer--radio {
  width: min(36rem, 70vw);
  /* Grows upward with its content (POSITION added a section); never spills over the desk bar. */
  height: auto;
  max-height: calc(100% - 4.4rem);
}
body.sl-open-radio .sl-drawer--radio .sl-drawer__body { overflow: auto; }

body.sl-floor-cut .sl-floor {
  animation: sl-floor-cut 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes sl-floor-cut {
  0% { opacity: 0.25; filter: blur(10px); transform: translate(-50%, -42%); }
  100% { opacity: 1; filter: none; transform: translate(-50%, -50%); }
}

#log {
  flex: 1;
  overflow: auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.msg {
  background: rgba(0, 0, 0, 0.28);
  border-left: 2px solid rgba(var(--sl-rgb), 0.5);
  padding: 0.4rem 0.55rem;
}

.msg .meta {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  color: var(--mute);
}

.sl-composer { margin-top: 0.35rem; }
.row { display: flex; gap: 0.45rem; }
.row input { flex: 1; }
.row button { width: auto; padding-inline: 0.9rem; }

.status {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--mute);
}
.status.ok { color: var(--ok); }

.err {
  color: #ff8a8a;
  font-size: 0.78rem;
  min-height: 1em;
  margin: 0.1rem 0 0;
}

.actions { display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: flex-end; }
.actions button, .actions a { width: auto; }
/* Account deletion link (App Store 5.1.1(v), 2026-09-25): reads as a warning,
   scoped to this one link so no other SL button changes. */
.actions a.sl-danger { color: #ff9a8f; border-color: rgba(255, 90, 79, 0.45); }
.voice-row { display: flex; gap: 0.4rem; }
.sl-check { display: flex; align-items: center; gap: 0.45rem; font-size: 0.72rem; color: var(--mute); margin: 0.35rem 0 0.2rem; cursor: pointer; text-transform: none; letter-spacing: 0; }
.sl-check input { width: auto; margin: 0; accent-color: #f0b429; }
.voice-row button { flex: 1; }

/* The manual-square row is an input and a button, and the input's width:100%
   base plus the button's flex:1 pushed SET off the right edge of the drawer --
   it was clipped to a single letter. Stack it: the field gets the full width it
   needs for a 10-character locator or a pair of coordinates, and SET sits under
   it where it cannot be cut off at any drawer width. */
#shareGridRow { flex-direction: column; align-items: stretch; }
#shareGridRow input { width: 100%; }
#shareGridRow button { flex: 0 0 auto; }

.sl-meters {
  display: flex;
  align-items: flex-end;
  gap: 0.28rem;
  height: 2.4rem;
  margin: 0.3rem 0 0.45rem;
}

body.sl-open-radio .sl-meters { height: 4.6rem; }

.sl-meters i {
  flex: 1;
  height: 30%;
  background: rgba(var(--sl-rgb), 0.2);
}

body.sl-voice-up .sl-meters i {
  animation: sl-meter 1.1s ease-in-out infinite;
  background: rgba(var(--sl-rgb), 0.6);
}

.sl-meters i:nth-child(1) { animation-delay: 0s; height: 30%; }
.sl-meters i:nth-child(2) { animation-delay: 0.08s; height: 48%; }
.sl-meters i:nth-child(3) { animation-delay: 0.16s; height: 72%; }
.sl-meters i:nth-child(4) { animation-delay: 0.04s; height: 90%; }
.sl-meters i:nth-child(5) { animation-delay: 0.2s; height: 64%; }
.sl-meters i:nth-child(6) { animation-delay: 0.12s; height: 40%; }
.sl-meters i:nth-child(7) { animation-delay: 0.24s; height: 80%; }
.sl-meters i:nth-child(8) { animation-delay: 0.1s; height: 36%; }

@keyframes sl-meter {
  0%, 100% { transform: scaleY(0.45); }
  50% { transform: scaleY(1); }
}

#hereList {
  flex: 1;
  overflow: auto;
  font-size: 0.8rem;
  color: var(--mute);
}

#hereList .op-line {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.2rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

#hereList .op-led {
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: #3a3530;
  flex: none;
}

#hereList .op-line.voice .op-led {
  background: var(--ok);
  box-shadow: 0 0 8px rgba(61, 255, 197, 0.65);
}

#hereList .me { color: var(--bone); }
#hereList .voice { color: var(--ok); }
#hereCount { font-family: var(--font-mono); color: var(--sl); }

#who {
  font-family: var(--font-display);
  letter-spacing: 0.06em;
  font-weight: 700;
  font-size: 0.76rem;
}

.rate-rail {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.32rem;
}

.rate-rail .room-plate {
  position: relative;
  left: auto;
  top: auto;
  width: 100%;
  transform: none;
  border-radius: 6px;
}

.rate-rail .room-plate.is-on { transform: none; }

.hint { font-size: 0.7rem; color: var(--mute); margin: 0.2rem 0 0; }
#bitrateProbe { font-family: var(--font-mono); font-size: 0.64rem; }

.sl-desk-bar {
  position: absolute;
  left: 0.5rem;
  right: 0.5rem;
  bottom: 0.35rem;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.28rem 0.55rem;
}

.sl-presets { display: flex; gap: 0.28rem; }

.sl-preset {
  width: auto;
  min-width: 5.4rem;
  padding: 0.4rem 0.65rem;
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  background: rgba(0, 0, 0, 0.3);
  color: #6d665c;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.sl-preset.is-on {
  color: #081014;
  background: var(--sl);
  border-color: var(--sl);
  box-shadow: 0 0 18px rgba(var(--sl-rgb), 0.35);
}

.sl-preset--reset.is-on {
  background: transparent;
  color: var(--sl);
  box-shadow: none;
}

.voice-status-row { display: flex; gap: 0.5rem; }
.backbar { display: flex; gap: 0.5rem; }

@media (max-width: 980px) {
  body.sl-in { overflow: auto; height: auto; }
  body.sl-in .sl-desk { height: auto; min-height: calc(100vh - 3.35rem); }
  .sl-stage { position: relative; inset: auto; height: 70vmin; min-height: 22rem; }
  .sl-drawer__body { display: flex; }
  .sl-drawer,
  .sl-drawer--traffic,
  .sl-drawer--here,
  .sl-drawer--log,
  .sl-drawer--radio,
  body[data-desk="dispatch"] .sl-drawer--traffic,
  body.sl-open-log .sl-drawer--log,
  body.sl-open-radio .sl-drawer--radio {
    position: relative;
    transform: none;
    left: auto;
    right: auto;
    top: auto;
    bottom: auto;
    width: auto;
    height: auto;
    margin: 0.4rem;
  }
  .sl-drawer__tab { writing-mode: horizontal-tb; transform: none; }
  .sl-desk-bar { position: relative; }
  #pttBtn { height: 26%; }
}

body.sl-admin {
  --sl: #c31d38;
  --sl-rgb: 195, 29, 56;
  --floor: #c31d38;
  --ink: #060308;
  --ink-2: #0c0709;
  color: var(--bone);
}

body.sl-admin .sl-bg {
  background:
    radial-gradient(ellipse 78% 58% at 50% 8%, rgba(130, 16, 32, 0.42), transparent 56%),
    radial-gradient(circle at 88% 92%, rgba(72, 8, 16, 0.38), transparent 44%),
    radial-gradient(circle at 8% 78%, rgba(48, 6, 12, 0.28), transparent 40%),
    #060308;
}

body.sl-admin .sl-ambience {
  background: radial-gradient(circle at 50% 12%, rgba(160, 22, 42, 0.28), transparent 46%);
}

body.sl-admin .sl-spine {
  background: linear-gradient(180deg, transparent, rgba(195, 29, 56, 0.7), transparent);
  opacity: 0.55;
}

body.sl-admin .sl-mast {
  background: rgba(8, 4, 6, 0.52);
  border-bottom-color: rgba(var(--sl-rgb), 0.28);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
}

body.sl-admin .sl-flag {
  min-width: 5.4rem;
  padding: 0.22rem 0.62rem 0.26rem;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015));
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

body.sl-admin .sl-flag b {
  color: var(--bone);
  text-shadow: 0 0 14px rgba(var(--sl-rgb), 0.28);
}

body.sl-admin .sl-admin-link--here {
  color: var(--sl);
  text-shadow: 0 0 12px rgba(var(--sl-rgb), 0.45);
}

body.sl-admin .sl-exit:hover {
  color: var(--bone);
  border-color: rgba(var(--sl-rgb), 0.5);
}

body.sl-admin .sl-admin-wrap {
  max-width: 72rem;
  z-index: 3;
  padding-top: 1.35rem;
  gap: 1rem;
}

body.sl-admin .sl-banner {
  color: rgba(239, 230, 214, 0.78);
  border-color: rgba(var(--sl-rgb), 0.32);
  background: linear-gradient(180deg, rgba(195, 29, 56, 0.12), rgba(0, 0, 0, 0.22));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  letter-spacing: 0.04em;
}

body.sl-admin .plate,
body.sl-admin .sl-glass {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(18, 8, 10, 0.42));
  backdrop-filter: blur(20px) saturate(1.2);
  border: 1px solid rgba(255, 255, 255, 0.09);
  box-shadow:
    0 22px 52px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 0 40px rgba(195, 29, 56, 0.05);
}

body.sl-admin .plate-kicker {
  color: var(--sl);
}

body.sl-admin .plate-title {
  letter-spacing: 0.14em;
  text-shadow: 0 0 22px rgba(var(--sl-rgb), 0.18);
}

body.sl-admin label {
  color: rgba(239, 230, 214, 0.55);
}

body.sl-admin input,
body.sl-admin select {
  background: rgba(4, 2, 3, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

body.sl-admin input:focus,
body.sl-admin select:focus {
  border-color: rgba(var(--sl-rgb), 0.62);
  box-shadow:
    0 0 0 1px rgba(var(--sl-rgb), 0.28),
    0 0 18px rgba(var(--sl-rgb), 0.12);
}

body.sl-admin button {
  background: #c31d38;
  color: #efe6d6;
  box-shadow: 0 0 22px rgba(195, 29, 56, 0.32);
}

body.sl-admin button:hover {
  filter: brightness(1.08);
}

body.sl-admin button.secondary {
  background: rgba(0, 0, 0, 0.32);
  color: #8a8174;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: none;
}

body.sl-admin button.secondary:hover {
  color: var(--bone);
  border-color: rgba(var(--sl-rgb), 0.35);
}

body.sl-admin #desk {
  flex-direction: row !important;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 1rem !important;
}

body.sl-admin #desk > .plate {
  flex: 1 1 22rem;
  padding: 1.25rem 1.3rem 1.3rem;
}

body.sl-admin #desk > .hint {
  flex: 1 1 100%;
}

body.sl-admin #gate {
  max-width: 30rem;
  padding: 1.35rem 1.4rem 1.4rem;
}

@media (max-width: 640px), ((max-height: 520px) and (pointer: coarse)) {
  /* Phone mast: wordmark on its own line, four flags in one even row. */
  .sl-mast { grid-template-columns: 1fr; gap: 0.35rem; padding: 0.35rem 0.6rem; }
  .sl-word { flex-wrap: nowrap; white-space: nowrap; gap: 0.45rem; }
  .sl-mast__end { justify-self: start; }
  .sl-word__sl { font-size: 1.7rem; }
  .sl-word__magic { font-size: 0.8rem; letter-spacing: 0.2em; }
  .sl-flags { flex-wrap: wrap; justify-content: stretch; gap: 0.3rem; }
  .sl-flag { flex: 1 1 calc(25% - 0.3rem); min-width: 0; padding: 0.12rem 0.3rem 0.16rem; overflow: hidden; }
  .sl-flag b { font-size: 0.78rem; letter-spacing: 0.06em; white-space: nowrap; }
  .sl-voice-top { flex: 1 1 100%; flex-direction: row; align-items: baseline; justify-content: space-between; min-width: 0; padding: 0.42rem 0.7rem; }
  .sl-mark-top { flex: 1 1 100%; flex-direction: row; align-items: baseline; justify-content: space-between; min-width: 0; padding: 0.42rem 0.7rem; }
  .sl-mark-strip__btn { flex: 1 1 0; min-width: 0; }
  .sl-voice-top__val { font-size: 1rem; }

  /* Sections: RADIO / OPERATORS / TRAFFIC, each header taps to collapse. */
  .sl-drawer { flex-direction: column !important; }
  .sl-drawer__tab {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0.55rem 0.7rem;
    font-size: 0.72rem;
    letter-spacing: 0.22em;
    border-bottom: 1px solid rgba(var(--sl-rgb), 0.16);
  }
  /* Collapse arrow: big enough to read at arm's length (was 0.9rem at 70%). */
  .sl-drawer__tab::after {
    content: "\25BE";
    font-size: 1.5rem;
    line-height: 1;
    opacity: 1;
    flex: none;
    width: 1.6rem;
    text-align: center;
    text-shadow: 0 0 8px rgba(var(--sl-rgb), 0.6);
  }
  .sl-drawer.is-collapsed .sl-drawer__tab::after { content: "\25B8"; }
  .sl-drawer__tab--ops::after { text-shadow: none; }
  .sl-drawer.is-collapsed .sl-drawer__tab { border-bottom: none; }
  .sl-drawer.is-collapsed .sl-drawer__body { display: none !important; }
  .sl-drawer__tab--ops { justify-content: flex-start; gap: 0.5rem; }
  .sl-drawer__tab--ops::after { margin-left: auto; }
  #log { max-height: 46vh; overflow: auto; }
}

/* Team plot — lives inside the scope. Rings are exact (auto-scaled), blips at
   true bearing/distance from you. Only visible when you share position. */
.sl-plot {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s ease;
}
/* Active plot takes touches for pinch-zoom + double-tap reset. pan-y keeps the
   phone page scrolling when a swipe starts on the scope. The PTT key is a
   separate element below and is never under this circle. */
.sl-plot.is-active { opacity: 1; pointer-events: auto; touch-action: pan-y; }
body.sl-plot-on .sl-plot.is-active ~ .sl-ptt__rings,
body.sl-plot-on .sl-ptt__rings { opacity: 0.35; animation: none; }
.sl-plot__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--r) * 92%);
  height: calc(var(--r) * 92%);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid rgba(var(--sl-rgb), 0.22);
}
.sl-plot__ring--outer { border-color: rgba(var(--sl-rgb), 0.45); }
.sl-plot__me {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 10px;
  height: 10px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: #ffb02e;
  box-shadow: 0 0 0 3px rgba(255, 176, 46, 0.25), 0 0 14px rgba(255, 176, 46, 0.7);
}
.sl-plot__blips { position: absolute; inset: 0; }
.sl-blip {
  position: absolute;
  width: auto;
  padding: 0;
  background: transparent;
  border: none;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--sl);
  cursor: pointer;
}
.sl-blip__dot {
  display: block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--sl);
  box-shadow: 0 0 0 3px rgba(var(--sl-rgb), 0.22), 0 0 14px rgba(var(--sl-rgb), 0.8);
}
.sl-blip__tag {
  font-family: var(--font-mono);
  font-size: 0.52rem;
  letter-spacing: 0.12em;
  color: var(--bone);
  text-shadow: 0 0 6px #000, 0 0 2px #000;
  white-space: nowrap;
}
.sl-blip.voice .sl-blip__dot { animation: sl-blip-voice 1.2s ease-in-out infinite; }
.sl-blip.square .sl-blip__dot {
  background: transparent;
  border: 2px solid var(--sl);
  box-shadow: 0 0 10px rgba(var(--sl-rgb), 0.5);
}
.sl-blip.stale { opacity: 0.45; }
/* Zoomed in past this member: pinned to the ring edge, dashed so it reads as "beyond". */
.sl-blip.beyond .sl-blip__dot {
  background: transparent;
  border: 2px dashed var(--sl);
  box-shadow: none;
  opacity: 0.8;
}
@keyframes sl-blip-voice {
  0%, 100% { box-shadow: 0 0 0 3px rgba(var(--sl-rgb), 0.22), 0 0 14px rgba(var(--sl-rgb), 0.8); }
  50% { box-shadow: 0 0 0 7px rgba(var(--sl-rgb), 0.12), 0 0 22px rgba(var(--sl-rgb), 1); }
}
.sl-plot__scalebar {
  position: absolute;
  left: 50%;
  bottom: 31%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.45rem;
  pointer-events: auto;
}
.sl-plot__scale {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.16em;
  color: var(--mute);
  white-space: nowrap;
}
.sl-plot.is-zoomed .sl-plot__scale { color: var(--sl); }
/* − / + ring zoom. Hidden until the plot is live (you share). */
.sl-plot__zoom {
  display: none;
  width: 1.7rem;
  height: 1.7rem;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(var(--sl-rgb), 0.55);
  background: rgba(0, 0, 0, 0.55);
  color: var(--sl);
  font-family: var(--font-mono);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  touch-action: manipulation;
}
.sl-plot.is-active .sl-plot__zoom { display: inline-flex; align-items: center; justify-content: center; }
.sl-plot__zoom:active { background: rgba(var(--sl-rgb), 0.3); }
.sl-plot__info {
  margin-top: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--bone);
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(var(--sl-rgb), 0.35);
  padding: 0.25rem 0.5rem;
  display: inline-block;
  pointer-events: auto;
}
.sl-plot__info[hidden] { display: none; }
/* Cluster picker: stacked blips list out, tap one. */
.sl-plot__pick-head {
  display: block;
  color: var(--sl);
  letter-spacing: 0.16em;
  font-size: 0.52rem;
  margin-bottom: 0.2rem;
}
.sl-plot__pick {
  display: block;
  width: 100%;
  margin: 0.15rem 0;
  padding: 0.38rem 0.55rem;
  border: 1px solid rgba(var(--sl-rgb), 0.35);
  background: rgba(var(--sl-rgb), 0.08);
  color: var(--bone);
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.sl-plot__pick:active { background: rgba(var(--sl-rgb), 0.25); }
.sl-plot__pick.mark-help { color: #ff5c6e; border-color: rgba(255, 47, 74, 0.55); }
.sl-plot__pick.mark-ok { color: #2ee68c; }
#shareGrid { text-transform: none; font-family: var(--font-mono); letter-spacing: 0.12em; }

/* Mast VOICE button - obvious connect / drop on every screen (Desktop + phone). */
.sl-voice-top {
  --vt: #ffb02e;
  width: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
  min-width: 11.5rem;
  padding: 0.16rem 0.6rem 0.2rem;
  background: rgba(255, 176, 46, 0.08);
  border: 1px solid rgba(255, 176, 46, 0.55);
  color: var(--vt);
  text-align: left;
  letter-spacing: 0.1em;
  box-shadow: 0 0 16px rgba(255, 176, 46, 0.18);
  animation: sl-voice-off 2.4s ease-in-out infinite;
}
.sl-voice-top[hidden] { display: none; }
.sl-voice-top__lab {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.2em;
  color: var(--mute);
}
.sl-voice-top__val {
  font-family: var(--font-display);
  font-size: 0.88rem;
  letter-spacing: 0.1em;
  white-space: nowrap;
}
.sl-voice-top[data-state="connecting"] {
  animation: none;
  border-color: rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.3);
  color: var(--bone);
  box-shadow: none;
}
.sl-voice-top[data-state="live"] {
  --vt: var(--sl);
  animation: none;
  background: color-mix(in srgb, var(--sl) 22%, rgba(0, 0, 0, 0.35));
  border-color: var(--sl);
  color: var(--sl);
  box-shadow: 0 0 22px rgba(var(--sl-rgb), 0.35);
}
@keyframes sl-voice-off {
  0%, 100% { box-shadow: 0 0 10px rgba(255, 176, 46, 0.12); border-color: rgba(255, 176, 46, 0.45); }
  50% { box-shadow: 0 0 22px rgba(255, 176, 46, 0.38); border-color: rgba(255, 176, 46, 0.95); }
}

/* Voice off: the big key is the connect key. */
#pttBtn.connect {
  color: #ffb02e;
  border-color: rgba(255, 176, 46, 0.55);
  background: linear-gradient(180deg, rgba(60, 40, 14, 0.55), rgba(16, 10, 6, 0.95));
  box-shadow: 0 10px 0 rgba(0, 0, 0, 0.55), 0 0 26px rgba(255, 176, 46, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.14);
  animation: sl-voice-off 2.4s ease-in-out infinite;
}
#pttBtn.connect .ptt-key__sub { color: rgba(255, 176, 46, 0.7); }
#pttBtn.busy { color: var(--bone); animation: none; }

/* RADIO drawer buttons: connect is primary when available, drop is amber when live. */
#voiceConnectBtn:not(:disabled) { background: var(--sl); color: #081014; border-color: var(--sl); }
#voiceDisconnectBtn:not(:disabled) { color: #ffb02e; border-color: rgba(255, 176, 46, 0.6); }

/* CONNECT is disabled for exactly one reason: you are already connected. That
   is a STATUS, not a broken control, and it was being painted as a ghost --
   muted grey on near-black under a blanket opacity of 0.4, which is the least
   readable thing on the screen at the moment you most want to know whether
   voice is up. It now reads as what it is: a lit outline saying CONNECTED,
   with no opacity trick, still plainly not pressable. */
#voiceConnectBtn:disabled {
  opacity: 1;
  background: rgba(var(--sl-rgb), 0.1);
  color: rgba(var(--sl-rgb), 0.95);
  border: 1px solid rgba(var(--sl-rgb), 0.5);
  box-shadow: inset 0 0 18px rgba(var(--sl-rgb), 0.12);
  cursor: default;
}

/* DROP is disabled when there is nothing to drop. That one really is inert,
   so it stays quiet -- but legible. A control you cannot read is not a control
   you understand as unavailable; it just looks broken. */
#voiceDisconnectBtn:disabled {
  opacity: 1;
  background: rgba(255, 255, 255, 0.03);
  color: rgba(226, 232, 240, 0.42);
  border: 1px dashed rgba(255, 255, 255, 0.16);
  box-shadow: none;
}

/* MARK button (mast) — quiet until used; red when you or anyone needs help. */
.sl-mark-top {
  width: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
  min-width: 9rem;
  padding: 0.16rem 0.6rem 0.2rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--bone);
  text-align: left;
  letter-spacing: 0.1em;
}
.sl-mark-top[hidden] { display: none; }
.sl-mark-top[data-state="ok"] {
  --ok-rgb: 46, 230, 140;
  border-color: rgba(var(--ok-rgb), 0.8);
  color: rgb(var(--ok-rgb));
  background: rgba(var(--ok-rgb), 0.1);
}
.sl-mark-top[data-state="help"],
.sl-mark-top[data-alert="1"] {
  border-color: #ff2f4a;
  color: #ff5c6e;
  background: rgba(255, 47, 74, 0.12);
  animation: sl-mark-alert 1.4s ease-in-out infinite;
}
@keyframes sl-mark-alert {
  0%, 100% { box-shadow: 0 0 8px rgba(255, 47, 74, 0.2); }
  50% { box-shadow: 0 0 24px rgba(255, 47, 74, 0.7); }
}
.sl-mark-strip {
  flex: 1 1 100%;
  display: flex;
  gap: 0.4rem;
  justify-content: center;
  padding: 0.2rem 0 0.1rem;
}
.sl-mark-strip[hidden] { display: none; }
.sl-mark-strip__btn {
  width: auto;
  min-width: 8rem;
  padding: 0.55rem 0.9rem;
  font-size: 0.8rem;
  background: rgba(0, 0, 0, 0.35);
  color: var(--bone);
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.sl-mark-strip__btn--help { background: #c8102e; color: #fff; border-color: #ff2f4a; }
.sl-mark-strip__btn--ok { background: #1f9d5a; color: #fff; border-color: #2ee68c; }

/* Marked blips on the plot */
.sl-blip.mark-help .sl-blip__dot {
  background: #ff2f4a;
  box-shadow: 0 0 0 4px rgba(255, 47, 74, 0.25), 0 0 18px rgba(255, 47, 74, 0.9);
  animation: sl-blip-help 0.9s ease-in-out infinite;
}
.sl-blip.mark-help .sl-blip__tag { color: #ff8a97; }
.sl-blip.mark-ok .sl-blip__dot { background: #2ee68c; box-shadow: 0 0 0 3px rgba(46, 230, 140, 0.25), 0 0 14px rgba(46, 230, 140, 0.8); }
.sl-blip.ghost { opacity: 0.55; }
.sl-blip.ghost .sl-blip__dot { border: 2px dashed currentColor; background: transparent; }
@keyframes sl-blip-help {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.35); }
}

/* Operators list marks + system messages */
.op-line.mark-help { color: #ff5c6e; }
.op-line.mark-help .op-led { background: #ff2f4a; box-shadow: 0 0 10px #ff2f4a; }
.op-line.mark-ok .op-led { background: #2ee68c; box-shadow: 0 0 8px #2ee68c; }
.op-line.offline { opacity: 0.7; }
.msg--system { border-left-color: #ffb02e; }
.msg--system .body { font-weight: 700; }
.msg--help { border-left-color: #ff2f4a; background: rgba(255, 47, 74, 0.08); }
.msg--ok { border-left-color: #2ee68c; }

/* MAGIC LOCK. A locked room looks different on purpose: somebody glancing at
   a screen over a shoulder should be able to tell which room keeps a record
   and which one does not, without reading a word.
   These paint the message strip only. The room colours, the plates and the
   PTT key are untouched - see the SL button styling note before adding any
   rule broader than this. */
.msg--locked { border-left-color: #7bd8ff; }
.msg--sealed .body {
  color: var(--mute);
  font-style: italic;
}
/* NEXCRYPT LOCK BANNER.
   BRIGHTNESS IS TUNED FOR THE DESK, NOT FOR A BLANK PAGE. Magic PTT SL lays
   two fixed overlays over everything at z-index 4: .sl-vignette, a radial that
   reaches rgba(0,0,0,0.55) at the edges, and .sl-scan, scanlines multiplying
   at 0.2. The traffic column sits hard against the LEFT edge, the darkest part
   of that vignette, so anything tuned on a plain background arrives washed
   out. This is deliberately brighter than it needs to be in isolation - do not
   calm it down by looking at it outside the desk.
   THE PLATE IS OPAQUE BLACK ON PURPOSE. v2 used a translucent plate, which
   looked right on a neutral preview and dissolved on the real desk: --ink is
   #040308 and each room lays its own accent over the floor (SpecOps is
   orange), so the colours were sitting on whatever showed through and washing
   out. Solid black behind it and the cyan/violet reads the same in every room.

   Decoration is CSS only — transform, opacity and background-position. No
   canvas, no timer, no per-frame JS, because most of Magic PTT SL runs on
   handhelds and this sits on screen for a whole session. The only real paint
   is the wordmark, a few dozen pixels.
   Kill it from app.js with LOCK_FX = false, which adds .sl-fx-off. */
.sl-lock-banner {
  position: relative;
  display: grid;
  grid-template-columns: 3px 1fr;
  gap: 0.6rem;
  align-items: stretch;
  /* Opaque. Nothing from the floor comes through this. */
  background:
    radial-gradient(130% 200% at 0% 50%, rgba(123, 216, 255, 0.22), transparent 62%),
    #000000;
  border: 1px solid rgba(123, 216, 255, 0.75);
  border-radius: 3px;
  /* Lifts the panel off the floor instead of sitting flat on it. */
  box-shadow:
    0 0 26px rgba(123, 216, 255, 0.42),
    0 0 3px rgba(123, 216, 255, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.09);
  padding: 0.45rem 0.6rem 0.4rem 0.45rem;
  margin: 0 0 0.4rem;
  line-height: 1.35;
  overflow: hidden;
}

/* The live rail: current running down the edge of the room. */
.sl-lock-rail {
  display: block;
  border-radius: 3px;
  background: linear-gradient(
    180deg,
    #7bd8ff 0%,
    #ffffff 18%,
    #a78bfa 40%,
    #5eead4 62%,
    #7bd8ff 84%,
    #a78bfa 100%
  );
  background-size: 100% 300%;
  box-shadow: 0 0 16px rgba(123, 216, 255, 1), 0 0 4px #ffffff;
  animation: sl-lock-rail 3s linear infinite;
}

/* The sweep, above everything, never catches a click. */
.sl-lock-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  width: 38%;
  transform: translateX(-130%);
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(123, 216, 255, 0.22) 42%,
    rgba(255, 255, 255, 0.26) 50%,
    rgba(167, 139, 250, 0.22) 58%,
    transparent 100%
  );
  animation: sl-lock-sweep 6.5s ease-in-out infinite;
}

.sl-lock-body { min-width: 0; }

.sl-lock-head {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-bottom: 0.24rem;
}

/* The core. Rings outward like something drawing power. */
.sl-lock-dot {
  width: 8px;
  height: 8px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: #7bd8ff;
  animation: sl-lock-core 2s ease-out infinite;
}

/* ELECTRIC WORDMARK. A gradient wider than the text slid under a text-shaped
   mask, so the colour travels THROUGH the letters rather than the letters
   moving. Sitting on solid black is what makes it read as lit. */
.sl-lock-mark {
  font-family: var(--font-display);
  font-size: 1.06rem;
  font-weight: 800;
  letter-spacing: 0.26em;
  line-height: 1;
  white-space: nowrap;
  background: linear-gradient(
    100deg,
    #7bd8ff 0%,
    #ffffff 13%,
    #a78bfa 28%,
    #5eead4 45%,
    #ffffff 60%,
    #7bd8ff 78%,
    #a78bfa 100%
  );
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter:
    drop-shadow(0 0 4px rgba(255, 255, 255, 0.55))
    drop-shadow(0 0 10px rgba(123, 216, 255, 0.95))
    drop-shadow(0 0 22px rgba(167, 139, 250, 0.6));
  animation: sl-lock-flow 3.2s linear infinite;
}

/* Says what is happening, not only who is doing it. */
.sl-lock-tag {
  font-family: var(--font-mono);
  font-size: 0.52rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: #8cffe6;
  background: rgba(94, 234, 212, 0.16);
  border: 1px solid rgba(94, 234, 212, 0.75);
  border-radius: 2px;
  padding: 1px 4px 0;
  white-space: nowrap;
  animation: sl-lock-tag 2.4s ease-in-out infinite;
}

/* The stream gets its OWN lane, cut into the plate. */
.sl-lock-stream {
  display: block;
  height: 0.85rem;
  overflow: hidden;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.03);
  box-shadow: inset 0 0 0 1px rgba(123, 216, 255, 0.22);
  margin-bottom: 0.3rem;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

.sl-lock-track {
  display: inline-block;
  white-space: nowrap;
  padding-top: 0.12rem;
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.16em;
  color: rgba(123, 216, 255, 0.66);
  will-change: transform;
  animation: sl-lock-slide 26s linear infinite;
}

/* The sealed half: bright against the dim plaintext. */
.sl-lock-track b {
  font-weight: 700;
  color: #b9ffe9;
  text-shadow: 0 0 12px rgba(94, 234, 212, 1);
}

/* The promise. People are meant to read this one. */
.sl-lock-words {
  display: block;
  font-size: 0.74rem;
  color: rgba(239, 230, 214, 0.94);
}

@keyframes sl-lock-flow {
  from { background-position: 0% 50%; }
  to { background-position: -260% 50%; }
}

@keyframes sl-lock-rail {
  from { background-position: 50% 0%; }
  to { background-position: 50% 300%; }
}

@keyframes sl-lock-slide {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@keyframes sl-lock-sweep {
  0% { transform: translateX(-130%); }
  55%, 100% { transform: translateX(360%); }
}

@keyframes sl-lock-core {
  0% { box-shadow: 0 0 0 0 rgba(123, 216, 255, 0.7), 0 0 10px rgba(123, 216, 255, 1); }
  70% { box-shadow: 0 0 0 8px rgba(123, 216, 255, 0), 0 0 16px rgba(167, 139, 250, 0.9); }
  100% { box-shadow: 0 0 0 0 rgba(123, 216, 255, 0), 0 0 10px rgba(123, 216, 255, 1); }
}

@keyframes sl-lock-tag {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* Off switch, and the accessibility answer. Both leave the words alone. */
.sl-lock-banner.sl-fx-off {
  display: block;
  padding: 0.3rem 0.5rem;
  background: #000000;
  border: 1px solid rgba(123, 216, 255, 0.35);
  border-left-width: 2px;
  box-shadow: none;
}
.sl-lock-banner.sl-fx-off::after,
.sl-lock-banner.sl-fx-off .sl-lock-rail,
.sl-lock-banner.sl-fx-off .sl-lock-stream,
.sl-lock-banner.sl-fx-off .sl-lock-head { display: none; }

@media (prefers-reduced-motion: reduce) {
  .sl-lock-banner::after,
  .sl-lock-track,
  .sl-lock-rail,
  .sl-lock-mark,
  .sl-lock-tag,
  .sl-lock-dot { animation: none; }
  .sl-lock-banner::after { opacity: 0; }
}

/* LINK METER — his screen only, and movable.
   Built only when canUseSlLog() is true (Desktop shell + a log operator
   account), so testers, phones and regular browsers never see it and never
   pay for it. Bright enough to read through the desk vignette and scanlines
   — see the NexCrypt banner note above for why that matters. */
.sl-meter {
  position: fixed;
  right: 14px;
  bottom: 14px;
  z-index: 9;
  width: 218px;
  background: #000000;
  border: 1px solid rgba(123, 216, 255, 0.55);
  border-radius: 4px;
  box-shadow: 0 0 22px rgba(0, 0, 0, 0.85), 0 0 10px rgba(123, 216, 255, 0.22);
  font-family: var(--font-mono);
  user-select: none;
}

.sl-meter.is-dragging { opacity: 0.85; }

.sl-meter__bar {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.28rem 0.3rem 0.26rem 0.45rem;
  border-bottom: 1px solid rgba(123, 216, 255, 0.22);
  cursor: move;
  touch-action: none;
}

.sl-meter__name {
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: #7bd8ff;
}

.sl-meter__room {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.56rem;
  letter-spacing: 0.06em;
  color: rgba(239, 230, 214, 0.65);
}

.sl-meter__e2ee {
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  padding: 1px 3px 0;
  border-radius: 2px;
  white-space: nowrap;
}
.sl-meter__e2ee.is-sealed {
  color: #b9ffe9;
  border: 1px solid rgba(94, 234, 212, 0.7);
  background: rgba(94, 234, 212, 0.12);
}
.sl-meter__e2ee.is-clear {
  color: #ffc98c;
  border: 1px solid rgba(255, 160, 60, 0.6);
  background: rgba(255, 160, 60, 0.1);
}
.sl-meter__e2ee.is-idle {
  color: rgba(239, 230, 214, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.16);
}

.sl-meter__x {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  line-height: 13px;
  padding: 0;
  font-size: 0.8rem;
  color: rgba(239, 230, 214, 0.55);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 2px;
  cursor: pointer;
}
.sl-meter__x:hover { color: #fff; border-color: rgba(255, 255, 255, 0.4); }

.sl-meter__rows { padding: 0.3rem 0.45rem 0.1rem; }

.sl-meter__row {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  margin-bottom: 0.14rem;
}

.sl-meter__row i {
  font-style: normal;
  font-size: 0.54rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: rgba(123, 216, 255, 0.75);
  width: 17px;
}

.sl-meter__row b {
  font-size: 0.92rem;
  font-weight: 700;
  color: #ffffff;
  min-width: 42px;
  text-align: right;
  text-shadow: 0 0 9px rgba(123, 216, 255, 0.5);
}

.sl-meter__row u {
  text-decoration: none;
  font-size: 0.5rem;
  letter-spacing: 0.04em;
  color: rgba(239, 230, 214, 0.5);
  /* One line. Wrapping pushed the rows apart and made the panel jump as the
     numbers changed, which is the last thing a meter should do. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sl-meter__foot {
  padding: 0.1rem 0.45rem 0.32rem;
  font-size: 0.49rem;
  letter-spacing: 0.04em;
  color: rgba(239, 230, 214, 0.42);
}

/* Phones never build it, but if the desk is ever narrow, stay on screen. */
@media (max-width: 640px) {
  .sl-meter { display: none; }
}

/* Phones (standalone SL app / narrow portrait): nets become a wrapped row
   ABOVE the scope so no chip ever sits on the on-screen PTT key. Desktop and
   wide layouts keep the orbital placement (JS still writes left/top; static
   position ignores them here). */
@media (max-width: 640px), ((max-height: 520px) and (pointer: coarse)) {
  /* Glow layers overhang the edges; never let a phone scroll sideways. */
  html, body, body.sl-in { overflow-x: hidden; }
  .sl-desk { overflow-x: clip; }
  .sl-stage {
    height: auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  .sl-orbit {
    position: relative;
    inset: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.35rem 0.2rem 0.55rem;
    order: 0;
  }
  .room-plate,
  .room-plate.is-on {
    position: static !important;
    transform: none;
    flex: 0 0 calc(33.333% - 0.4rem);
    width: auto;
    min-width: 0;
  }
  .room-plate.is-on { transform: scale(1.04); }
  .sl-floor {
    order: 1;
    position: relative;
    left: auto;
    top: auto;
    transform: none;
    margin: 0.2rem auto 0.8rem;
    /* Fill the phone: the scope is the product. Desk presets do not shrink it here. */
    width: min(94vw, 62vh, 560px);
    height: min(94vw, 62vh, 560px);
  }
  .sl-plot__scalebar { bottom: 30%; }
  .sl-plot__scale { font-size: 0.56rem; }
  .sl-plot__zoom { width: 2rem; height: 2rem; font-size: 1.15rem; }
  .sl-blip__tag { font-size: 0.6rem; }
  #pttBtn { height: 27%; }
}

@media (prefers-reduced-motion: reduce) {
  .sl-ambience,
  .sl-floor__halo,
  .sl-ptt__rings,
  .sl-ptt__sweep,
  .sl-ptt__ripple,
  #pttBtn.ready,
  .sl-meters i,
  body.sl-floor-cut .sl-floor { animation: none; }
  .sl-floor, .sl-drawer { transition: none; }
  .sl-scan { display: none; }
}

/* ---- Tiny no-touch radios (Magic G393: 128x160, density 90, dpr 0.5625) ----
   ~227x284 CSS px, ~256 once the shell's status-bar inset (--sl-inset-top) is
   applied. Every CSS px is just over half a device px, so the type is huge
   and bold; anything under ~15px is mush on this LCD (proven 2026-09-03).
   Pixel budget at the inset (2026-09-08): 28 inset + 2 pad + 30 net +
   24 state + 4 face pad + 2 rule + 3 + 5 rows x 27 + 17 hint + 2 pad = 247
   of 256. Every row is flex: 0 0 <height> so nothing can be crushed the way
   the old net line was; if anything ever overflows it is the hint, never the
   channel. Colour follows the net: body[data-room] sets --sl-rgb, and the
   face carries the desk's scope rings in that colour, dim, brighter on TX.
   Gated from app.js (body.sl-micro-mode); phones and Desktop never match. */
.sl-micro { display: none; }
body.sl-micro-mode > :not(.sl-micro) { display: none !important; }
body.sl-micro-mode {
  overflow: hidden;
  background: #000 !important;
}
body.sl-micro-mode .sl-micro {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 9999;
  flex-direction: column;
  padding: calc(2px + var(--sl-inset-top, 0px)) 3px 2px;
  background: #000;
  color: #fff;
  font-family: var(--font-display, system-ui, sans-serif);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: 0;
  overflow: hidden;
}
/* The face: channel name over the scope rings, then one state line. */
.sl-micro__face {
  flex: 0 0 auto;
  position: relative;
  padding: 1px 2px 3px;
  border-bottom: 2px solid rgba(var(--sl-rgb), 0.55);
  /* Rings roughly doubled from the first cut (operator, 2026-09-08: "make
     the radar grid a little brighter so you can see it"). The LCD washes
     out low alphas; the text stays readable because it sits on the rings'
     own colour, not on white. */
  background:
    radial-gradient(circle at 50% 125%, transparent 34%, rgba(var(--sl-rgb), 0.46) 35%, transparent 37.5%),
    radial-gradient(circle at 50% 125%, transparent 56%, rgba(var(--sl-rgb), 0.38) 57%, transparent 59.5%),
    radial-gradient(circle at 50% 125%, transparent 78%, rgba(var(--sl-rgb), 0.30) 79%, transparent 81.5%),
    radial-gradient(circle at 50% 125%, rgba(var(--sl-rgb), 0.26), transparent 62%);
}
body.sl-tx .sl-micro__face { background-color: rgba(var(--sl-rgb), 0.22); }
.sl-micro__net {
  font-size: 28px;
  line-height: 30px;
  height: 30px;
  color: rgb(var(--sl-rgb));
  text-shadow: 0 0 6px rgba(var(--sl-rgb), 0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sl-micro__state {
  font-size: 22px;
  line-height: 24px;
  height: 24px;
  color: #cfe0de;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sl-micro__state[data-state="off"]  { color: #ffc23d; }
.sl-micro__state[data-state="busy"] { color: #9fb3b1; }
.sl-micro__state[data-state="on"]   { color: rgb(var(--sl-rgb)); }
.sl-micro__state[data-state="rx"]   { color: #fff; }
.sl-micro__state[data-state="tx"]   { color: #fff; background: #d1102c; padding: 0 3px; }
.sl-micro__state[data-state="err"]  { color: #ff6b7a; }
/* The deck: a window of rows, cursor kept in view by app.js. */
.sl-micro__deck {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding-top: 3px;
}
.sl-micro__row {
  flex: 0 0 27px;
  height: 27px;
  line-height: 27px;
  font-size: 24px;
  padding: 0 3px;
  color: #cfe0de;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sl-micro__row.tone-help { color: #ff8a95; }
.sl-micro__row.tone-ok { color: #6cf0a8; }
/* Selected row takes the net colour; help and ok keep their own. */
.sl-micro__row.is-sel { background: rgb(var(--sl-rgb)); color: #000; }
.sl-micro__row.is-sel.tone-help { background: #ff2f4a; color: #fff; }
.sl-micro__row.is-sel.tone-ok { background: #2ee68c; color: #000; }
.sl-micro__hint {
  flex: 0 0 17px;
  height: 17px;
  line-height: 17px;
  margin-top: auto;
  font-size: 15px;
  color: #93a8a6;
  white-space: nowrap;
  overflow: hidden;
}

/* ---- Small rugged radios (UNIWA/Anysecu B8000 class: ~320x427 CSS px) ----
   Measured on the field unit 2026-09-03: the ordinary phone layout came to
   mast 192 + nets 125 + scope 265 in a 427-tall viewport, so the scope and the
   on-screen PTT key sat far BELOW the fold. The key could not be tapped at all
   and the scope appeared to "come and go" as the page scrolled.
   On a 3-inch radio the scope and the key matter more than four status chips,
   so the mast collapses to ONE row (wordmark + VOICE + MARK) and the LINK /
   AIR / NET / UTC flags are hidden: the VOICE button, the scope and the
   highlighted net chip already say all of it. The reclaimed height goes to the
   scope, which the operator also asked to be bigger.
   All six nets stay visible and stay above the scope; they never go back on
   the orbit (magic-ptt-sl-locked -> phone layout).
   Width AND height are both required, so taller phones and landscape phones
   never match. Do not use `pointer: coarse` here - a desktop preview reports
   fine pointers and the tier silently stops applying. */
@media (max-width: 640px) and (max-height: 480px) {
  .sl-mast {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.3rem;
    padding: 0.12rem 0.35rem;
  }
  .sl-flags { display: none; }
  .sl-mast__meta { display: none; }
  .sl-word { flex: 0 0 auto; }
  .sl-word__magic { display: none; }
  .sl-word__sl { font-size: 1.05rem; }
  .sl-voice-top,
  .sl-mark-top,
  .sl-pos-top:not([hidden]) {
    display: flex;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: row;
    align-items: baseline;
    justify-content: center;
    gap: 0.25rem;
    padding: 0.12rem 0.25rem;
  }
  /* Three buttons plus the wordmark is tight at 274px - shrink the labels. */
  .sl-voice-top__lab { font-size: 0.38rem; letter-spacing: 0.06em; }
  /* Each button gets an equal slice of a 274px mast, and the value is nowrap,
     so a long state word ("TAP TO DISCONNECT") used to run straight out of its
     button. Clip it here instead: the colour and the VOICE label already carry
     the state, the words are the part that can afford to be cut. */
  .sl-voice-top__val,
  .sl-mark-top__val {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .sl-pos-top .sl-voice-top__lab { display: none; }
  .sl-voice-top__val,
  .sl-mark-top__val { font-size: 0.7rem; }
  .sl-mark-strip__btn { padding: 0.2rem 0.3rem; font-size: 0.62rem; }

  /* A net chip is normally three stacked lines (floor number, tag, name) and
     six of those ate ~95px of a 427px screen. Keep the NAME only: still
     readable, one line, two tight rows. */
  .sl-orbit { gap: 0.16rem; padding: 0.1rem 0.1rem 0.16rem; }
  .room-plate,
  .room-plate.is-on { flex: 0 0 calc(33.333% - 0.16rem); padding: 0.1rem 0.06rem; }
  .room-plate__floor,
  .room-plate__code { display: none; }
  .room-plate__name { font-size: 0.66rem; letter-spacing: 0.02em; line-height: 1.1; }

  /* flex:0 0 auto or the column shrinks the circle back down. Width-limited
     at this size, so the scope fills the panel edge to edge. */
  .sl-floor {
    flex: 0 0 auto;
    width: min(96vw, 78vh, 360px);
    height: min(96vw, 78vh, 360px);
    margin: 0 auto 0.2rem;
  }
  #pttBtn { height: 29%; }

  /* ---- Low-power paint (measured 2026-09-03) ----
     On the B8000 (MediaTek MT6739, 876 MB RAM) the idle SL page was burning
     123% CPU — more than a whole core with nobody touching it — and the panel
     visibly flickered: "tap to connect keeps blinking on and off". The cause
     is the atmosphere, not the logic: a full-screen blur(40px) ambience layer,
     a rotating conic sweep with mix-blend-mode, an animated scanline layer
     also blending, animated ring gradients, and backdrop-filter on the mast
     and on every net chip. All of it repaints continuously, and enlarging the
     scope made it worse.
     These radios get a still radar: same rings, same colours, no animation, no
     blur, no blend modes. Phones and Desktop keep the full atmosphere.
     Do not "restore the sweep" here — it costs a core. */
  .sl-ambience,
  .sl-scan,
  .sl-ptt__sweep,
  .sl-floor__halo { display: none !important; }
  /* Brightness. Killing the animated glow layers to save the CPU also made
     these panels far too dark to read in daylight (field report 2026-09-03).
     Static gradients are cheap — a plain paint, no blur, no animation, no
     blend — so the lift comes back without the cost, and the muted greys are
     raised because a 3-inch transflective panel eats contrast. */
  .sl-bg {
    background:
      radial-gradient(ellipse 92% 78% at 50% 44%, rgba(var(--sl-rgb), 0.30), transparent 64%),
      #0b0a13;
    transition: none;
  }
  body.sl-tx .sl-bg {
    background:
      radial-gradient(ellipse 96% 84% at 50% 46%, rgba(var(--sl-rgb), 0.44), transparent 66%),
      #140b12;
  }
  body { --mute: #b6c4c2; --bone: #f6f1e8; }
  .sl-vignette { display: none; }
  .sl-floor__disc {
    background:
      radial-gradient(circle at 50% 44%, rgba(var(--sl-rgb), 0.30), rgba(8, 8, 16, 0.88) 76%);
    border-color: rgba(var(--sl-rgb), 0.55);
  }
  .sl-ptt__rings { opacity: 0.95; }
  .sl-ptt__cross { opacity: 0.9; }
  .sl-ptt__air-lab { color: var(--bone); }
  .ptt-key__word { color: var(--bone); }
  .sl-flag b, .sl-voice-top__val, .sl-mark-top__val { color: var(--bone); }
  .sl-ptt__rings,
  .sl-ptt__ripple,
  #pttBtn.ready,
  .sl-meters i,
  body.sl-floor-cut .sl-floor { animation: none !important; }
  .sl-floor,
  .sl-drawer,
  .room-plate { transition: none !important; }
  /* Sledgehammer, on purpose: EVERY backdrop-filter and EVERY looping
     animation on this tier. Targeted removals were not enough — the GPU
     thread still sat at 93%. A blurred backdrop under anything that ticks
     (even the once-a-second UTC clock) re-blurs the whole layer every frame
     on this chip. */
  *,
  *::before,
  *::after {
    backdrop-filter: none !important;
    animation: none !important;
  }
  .room-plate { filter: none; box-shadow: none; }
}

/* POSITION button in the mast. It used to be small-rugged-radio only, on the
   grounds that everyone else could reach the three share modes in the RADIO
   drawer. That stopped being true when the roster arrived: range and bearing
   only exist if people share a position, and the switch that decides it was
   several clicks deep inside a drawer nobody opens. It now sits beside VOICE,
   MARK and ROSTER, which is where the other three states of your station live.
   Same control, same three modes, still off until you choose otherwise. */
/* A readout, not a control. It used to cycle the three share modes on tap,
   which fought the three explicit buttons in STATION -- two ways to set one
   thing, one of them invisible. It now only reports what STATION is set to. */
.sl-pos-top:not([hidden]) {
  display: flex;
  cursor: default;
  width: auto;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
  min-width: 8rem;
  padding: 0.16rem 0.6rem 0.2rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--bone);
  text-align: left;
  letter-spacing: 0.1em;
}
.sl-pos-top[data-state="off"] { color: var(--mute); }
.sl-pos-top:not([data-state="off"]) {
  color: var(--vt);
  border-color: color-mix(in srgb, var(--vt) 55%, transparent);
  background: color-mix(in srgb, var(--vt) 10%, transparent);
}
.sl-pos-top[data-state="square"] { --vt: #b07cff; }
.sl-pos-top[data-state="precise"] { --vt: #3dffc5; }

/* ---------------------------------------------------------------------------
 * CONTROLS
 *
 * The channel colour was carrying the whole interface. Underneath it every
 * ordinary control was a flat grey rectangle: no radius, no hover, no pressed
 * state, and .secondary was dim-on-dim enough to read as disabled. These are
 * touchscreens used in gloves and bad light.
 *
 * SCOPE, and why it is drawn this tightly. Almost every button in SL paints
 * itself -- .room-plate, .sl-mark-strip__btn, .sl-preset, .sl-plot__zoom,
 * .sl-drawer__tab, .sl-voice-top, .sl-mark-top, .sl-exit. A blanket `button`
 * rule outranks all of them: the channel pills came out as teal rectangles and
 * the red NEED HELP button came out teal too. So this styles exactly two
 * things -- buttons carrying NO class at all, and .secondary -- and any button
 * that has its own look keeps it. Give a new button a class and it is safe by
 * default; that is the invariant, do not widen this selector.
 *
 * #pttBtn is excluded by name. It has its own physical language and its own
 * transform, and a transition there would put animation in the TX path.
 * [id="pttBtn"] is an attribute selector on purpose: it excludes the key
 * without dragging ID specificity into the rule.
 * ------------------------------------------------------------------------ */
button:not([class]):not([id="pttBtn"]),
button.secondary {
  position: relative;
  overflow: hidden;
  min-height: 2.45rem;
  border-radius: 0.4rem;
  isolation: isolate;
  transition: transform .07s ease, box-shadow .16s ease,
              border-color .16s ease, background-color .16s ease;
}

/* The sheen. A single pseudo-element that lies off the left edge and crosses
   the face on hover. No filters and no blur: this has to stay cheap on the
   HI-B8's Chromium, where a backdrop-filter per control would cost real frames. */
button:not([class]):not([id="pttBtn"])::after,
button.secondary::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(105deg,
    transparent 30%, rgba(255, 255, 255, 0.22) 48%, transparent 66%);
  transform: translateX(-120%);
  transition: transform .5s ease;
}

button:not([class]):not([id="pttBtn"]):hover:not(:disabled)::after,
button.secondary:hover:not(:disabled)::after { transform: translateX(120%); }

/* Primary: the channel colour, lit from above, sitting on its own shadow. */
button:not([class]):not([id="pttBtn"]) {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18), rgba(0, 0, 0, 0.14)),
    var(--sl);
  color: #05131a;
  border: none;
  box-shadow:
    0 2px 0 rgba(0, 0, 0, 0.5),
    0 0 18px rgba(var(--sl-rgb), 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.45);
}

button:not([class]):not([id="pttBtn"]):hover:not(:disabled) {
  box-shadow:
    0 3px 0 rgba(0, 0, 0, 0.5),
    0 0 30px rgba(var(--sl-rgb), 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.55);
}

/* Secondary: an alternative, not a dead control. Channel-tinted glass with a
   bone label -- the old --mute on near-black was indistinguishable from
   :disabled, which is why every one of these read as greyed out. */
button.secondary {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(0, 0, 0, 0.22)),
    rgba(var(--sl-rgb), 0.08);
  color: var(--bone);
  border: 1px solid rgba(var(--sl-rgb), 0.38);
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

button.secondary:hover:not(:disabled) {
  border-color: rgba(var(--sl-rgb), 0.75);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(0, 0, 0, 0.2)),
    rgba(var(--sl-rgb), 0.16);
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.4),
    0 0 20px rgba(var(--sl-rgb), 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* Pressed travels down onto its own shadow, so a tap is felt as well as seen. */
button:not([class]):not([id="pttBtn"]):active:not(:disabled),
button.secondary:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.45);
}

button:not([class]):not([id="pttBtn"]):focus-visible,
button.secondary:focus-visible {
  outline: 2px solid rgba(var(--sl-rgb), 0.9);
  outline-offset: 2px;
}

/* Disabled loses the lit face entirely. Dimming alone left it looking pressable,
   which matters most on DISCONNECT and the log controls. */
button:not([class]):not([id="pttBtn"]):disabled,
button.secondary:disabled {
  opacity: 1;
  background: rgba(255, 255, 255, 0.035);
  color: rgba(226, 232, 240, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: none;
  transform: none;
}
button:not([class]):not([id="pttBtn"]):disabled::after,
button.secondary:disabled::after { display: none; }

/* Fields: the same corner radius as the controls, and a resting border you can
   find before you focus it. */
input, textarea, select {
  min-height: 2.45rem;
  border-radius: 0.4rem;
  border-color: rgba(255, 255, 255, 0.14);
  background: rgba(0, 0, 0, 0.45);
  transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease;
}

input::placeholder, textarea::placeholder { color: rgba(226, 232, 240, 0.38); }

input:focus, select:focus, textarea:focus {
  background: rgba(0, 0, 0, 0.62);
  box-shadow: 0 0 0 2px rgba(var(--sl-rgb), 0.28);
}

/* A select on this background drew no arrow at all in the radios' WebView, so
   it read as an inert box. Draw one, and leave room for it. */
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 1.8rem;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 0.9rem center, right 0.62rem center;
  background-size: 0.28rem 0.28rem, 0.28rem 0.28rem;
  background-repeat: no-repeat;
  background-color: rgba(0, 0, 0, 0.45);
}

/* The self-painted buttons keep their own colour -- that identity is the point,
   and NEED HELP being red is not negotiable -- but they were the last square
   corners left, which made them read as a different, older interface. Radius
   and press feedback only. Nothing here touches a background or a text colour.
   .room-plate (a pill) and .sl-plot__zoom (a circle) are deliberately absent. */
.sl-mark-strip__btn,
.sl-preset,
.sl-voice-top,
.sl-mark-top,
.sl-pos-top,
.sl-exit {
  border-radius: 0.4rem;
  transition: box-shadow .16s ease, border-color .16s ease, transform .07s ease;
}

.sl-mark-strip__btn { box-shadow: 0 2px 0 rgba(0, 0, 0, 0.45); }

.sl-mark-strip__btn:active:not(:disabled),
.sl-preset:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.4);
}

/* NEED HELP earns a halo of its own. In bad light it has to be the thing your
   eye lands on before you have read anything else on the screen. */
.sl-mark-strip__btn--help {
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.45), 0 0 22px rgba(200, 16, 46, 0.45);
}
.sl-mark-strip__btn--help:hover:not(:disabled) {
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.45), 0 0 34px rgba(255, 47, 74, 0.6);
}
.sl-mark-strip__btn--ok:hover:not(:disabled) {
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.45), 0 0 26px rgba(46, 230, 140, 0.45);
}

/* MI / KM toggle beside the plot zoom (2026-09-18). Same chrome as the zoom
   buttons; smaller type so the two letters fit the round button. */
.sl-plot__zoom.sl-plot__unit {
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.06em;
}

/* 2026-10-02: a long press on the radar around the talk key made iOS select
   the whole square scope (seen on build 20 while voice was refused). The key
   already blocks selection; the scope around it now does too. */
#pttPane, #pttPane * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
