/* APRSWeb — design tokens. Light is the base; both dark states redefine
   only tokens, so the un-stamped "system" case works too. */
:root{
  --ground:#EDF1F1;
  --panel:#FFFFFF;
  --panel-2:#F5F8F8;
  --sunk:#E4EAEA;
  --ink:#0E171B;
  --ink-2:#39494F;
  --muted:#5C6F77;
  --faint:#8A9AA0;
  --line:#D5DDDE;
  --line-2:#E7ECEC;

  --accent:#C2246E;
  --accent-soft:#FBE9F1;
  --c1:#2A7FB8;
  --c2:#C2246E;
  --c3:#C8720F;
  --c4:#12876A;
  --c5:#6F55B5;

  --ok:#12876A;
  --warn:#C8720F;
  --dead:#8A9AA0;
  --alert:#C42B2B;
  --ok-soft:#E6F3EF;
  --alert-soft:#FDECEC;
  --alert-line:#F5D2D2;

  --ramp-1:#CFE2F0; --ramp-2:#9CC4E1; --ramp-3:#5C9FCE; --ramp-4:#2A7FB8; --ramp-5:#17527A;

  --shadow:0 1px 2px rgba(14,23,27,.06), 0 8px 24px -12px rgba(14,23,27,.18);
  --focus:#2A7FB8;

  --f-disp:"Barlow Condensed","Helvetica Neue",Arial,sans-serif;
  --f-body:"Barlow","Helvetica Neue",Arial,sans-serif;
  --f-mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --tile-filter:none;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --ground:#0A1216; --panel:#111C21; --panel-2:#16242A; --sunk:#0D181D;
    --ink:#E6EEF1; --ink-2:#B6C8CE; --muted:#8DA3AC; --faint:#677F88;
    --line:#1E2E35; --line-2:#182429;
    --accent:#E14A8E; --accent-soft:#33121F;
    --c1:#3F94CC; --c2:#E14A8E; --c3:#C87F26; --c4:#2FA98A; --c5:#9A85D6;
    --ok:#2FA98A; --warn:#C87F26; --dead:#677F88;
    --ok-soft:#10241F;
    --alert:#F2555A; --alert-soft:#2A1214; --alert-line:#43191C;
    --ramp-1:#173449; --ramp-2:#1F5478; --ramp-3:#2C79A8; --ramp-4:#3F94CC; --ramp-5:#7FBCE4;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 30px -14px rgba(0,0,0,.7);
    --focus:#3F94CC;
  }
}
:root[data-theme="dark"]{
  --ground:#0A1216; --panel:#111C21; --panel-2:#16242A; --sunk:#0D181D;
  --ink:#E6EEF1; --ink-2:#B6C8CE; --muted:#8DA3AC; --faint:#677F88;
  --line:#1E2E35; --line-2:#182429;
  --accent:#E14A8E; --accent-soft:#33121F;
  --c1:#3F94CC; --c2:#E14A8E; --c3:#C87F26; --c4:#2FA98A; --c5:#9A85D6;
  --ok:#2FA98A; --warn:#C87F26; --dead:#677F88;
  --ok-soft:#10241F;
  --alert:#F2555A; --alert-soft:#2A1214; --alert-line:#43191C;
  --ramp-1:#173449; --ramp-2:#1F5478; --ramp-3:#2C79A8; --ramp-4:#3F94CC; --ramp-5:#7FBCE4;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 30px -14px rgba(0,0,0,.7);
  --focus:#3F94CC;
}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;background:var(--ground);color:var(--ink);
  font-family:var(--f-body);font-size:15px;line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{font-family:var(--f-disp);font-weight:700;margin:0;line-height:1.1;text-wrap:balance}
a{color:var(--c1);text-decoration:none}
a:hover{text-decoration:underline}
:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
.mono{font-family:var(--f-mono);font-variant-ligatures:none}
.num{font-variant-numeric:tabular-nums}
/* Present in the document for search engines and screen readers, removed from
   the visual layout entirely: clipped to nothing and taken out of flow, so it
   cannot shift a single pixel. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

.eyebrow{font-family:var(--f-disp);font-weight:600;font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted)}

/* ---------- app bar ---------- */
.appbar{
  display:flex;align-items:center;gap:14px;padding:0 12px;height:50px;
  border-bottom:1px solid var(--line);background:var(--panel);position:relative;z-index:1200;
}
.appbar .logo{display:flex;align-items:center;gap:8px;font-family:var(--f-disp);
  font-weight:700;font-size:17px;letter-spacing:.02em;color:var(--ink);text-decoration:none}
.navlink{font-family:var(--f-disp);font-weight:600;font-size:12px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);text-decoration:none;
  padding:4px 8px;border-radius:4px;white-space:nowrap}
.navlink:hover{color:var(--ink);background:var(--panel-2);text-decoration:none}
.navlink[aria-current="page"]{color:var(--ink);background:var(--panel-2)}

.searchbox{
  flex:1;max-width:420px;display:flex;align-items:center;gap:8px;position:relative;
  background:var(--sunk);border:1px solid var(--line);border-radius:5px;padding:5px 10px;
}
.searchbox input{border:0;background:transparent;color:var(--ink);
  font-family:var(--f-mono);font-size:13px;width:100%;outline:none}
.searchbox input::placeholder{color:var(--faint);font-family:var(--f-body)}
.searchbox kbd{font-family:var(--f-mono);font-size:10px;color:var(--faint);
  border:1px solid var(--line);border-radius:3px;padding:1px 4px}
.suggest{
  position:absolute;top:calc(100% + 5px);left:0;right:0;background:var(--panel);
  border:1px solid var(--line);border-radius:5px;box-shadow:var(--shadow);
  max-height:320px;overflow-y:auto;z-index:1300;
}
.suggest:empty{display:none}
.suggest button{
  display:block;width:100%;text-align:left;border:0;background:none;cursor:pointer;
  padding:7px 11px;font:inherit;color:inherit;border-bottom:1px solid var(--line-2);
}
.suggest button:last-child{border-bottom:0}
.suggest button:hover,.suggest button:focus{background:var(--panel-2)}
.suggest .cs{font-family:var(--f-mono);font-size:13px;font-weight:500}
.suggest .meta{font-size:11.5px;color:var(--muted)}

.barright{margin-left:auto;display:flex;align-items:center;gap:12px}
.livestat{display:flex;align-items:center;gap:6px;font-family:var(--f-mono);
  font-size:11.5px;color:var(--muted)}
.pulse{width:7px;height:7px;border-radius:50%;background:var(--ok);position:relative;flex:none}
.pulse.down{background:var(--dead)}
.pulse::after{content:"";position:absolute;inset:-3px;border-radius:50%;
  border:1px solid var(--ok);opacity:.5;animation:ping 2.4s ease-out infinite}
.pulse.down::after{display:none}
@keyframes ping{0%{transform:scale(.55);opacity:.65}70%,100%{transform:scale(1.35);opacity:0}}
@media (prefers-reduced-motion:reduce){.pulse::after{animation:none}}
/* One control in the bar for everything about how the page looks. Theme and
   the recently heard column were two unlabelled icons here, and the symbol set
   would have been a third; a named row in a panel says what each one does. */
.settings{position:relative}
.setbtn{
  appearance:none;padding:0;border:1px solid var(--line);background:var(--panel-2);cursor:pointer;
  border-radius:5px;width:30px;height:26px;display:grid;place-items:center;color:var(--muted);
}
.setbtn:hover{color:var(--ink)}
.setbtn[aria-expanded="true"]{color:var(--accent);border-color:var(--accent);
  background:var(--accent-soft)}
.setmenu{position:absolute;right:0;top:32px;width:248px;background:var(--panel);
  border:1px solid var(--line);border-radius:6px;box-shadow:var(--shadow);padding:5px;
  z-index:1300}
.setmenu[hidden]{display:none}
.setmenu .hd{font-family:var(--f-disp);font-size:10px;font-weight:600;letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint);padding:6px 8px 4px}
.setmenu .hd.sub{border-top:1px solid var(--line-2);margin-top:4px}
.setrow{padding:4px 8px 7px;display:flex;flex-direction:column;gap:5px}
.setrow .lt{font-size:13px;color:var(--ink)}
.setrow .note{font-size:11px;color:var(--faint);line-height:1.35}
.setseg{display:flex;border:1px solid var(--line);border-radius:5px;overflow:hidden;
  background:var(--panel-2)}
.setseg button{appearance:none;flex:1;font-family:var(--f-disp);font-size:11.5px;font-weight:600;
  letter-spacing:.05em;text-transform:uppercase;padding:4px 6px;border:0;background:none;
  color:var(--muted);cursor:pointer;line-height:1.6}
.setseg button+button{border-left:1px solid var(--line)}
.setseg button[aria-pressed="true"]{background:var(--accent-soft);color:var(--accent)}
.setsw{appearance:none;display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;padding:6px 8px;border:0;border-radius:4px;background:none;cursor:pointer;
  font:inherit;font-size:13px;color:var(--ink);text-align:left}
.setsw:hover{background:var(--panel-2)}
.setsw .knob{width:30px;height:17px;flex:none;border-radius:9px;background:var(--sunk);
  border:1px solid var(--line);position:relative;transition:background .12s}
.setsw .knob::after{content:"";position:absolute;top:1px;left:1px;width:13px;height:13px;
  border-radius:50%;background:var(--panel);box-shadow:0 1px 2px rgba(0,0,0,.3);
  transition:transform .12s}
/* Pressed means the panel is showing, so the switch reads the way the thing it
   controls does rather than the way the old hide button did. */
.setsw[aria-pressed="true"] .knob{background:var(--accent);border-color:var(--accent)}
.setsw[aria-pressed="true"] .knob::after{transform:translateX(13px)}
@media (prefers-reduced-motion:reduce){.setsw .knob,.setsw .knob::after{transition:none}}

/* ---------- app layout ---------- */
.app{display:grid;grid-template-columns:344px minmax(0,1fr) 292px;height:calc(100vh - 50px)}
.app.nodetail{grid-template-columns:minmax(0,1fr) 292px}
/* The reader has folded the recently heard column away: the map takes it back.
   The column is still rendered and still counted — only hidden — so nothing
   about what the browser knows changes with it. */
.app.nofeed{grid-template-columns:344px minmax(0,1fr)}
.app.nofeed.nodetail{grid-template-columns:minmax(0,1fr)}
.app.nodetail .col.left{display:none}
.app.nofeed .col.right{display:none}
.col{min-width:0;display:flex;flex-direction:column;min-height:0}
.col.left{border-right:1px solid var(--line);background:var(--panel)}
.col.right{border-left:1px solid var(--line);background:var(--panel)}
.col.mid{background:var(--sunk);position:relative}
#map{position:absolute;inset:0}

/* ---------- station panel ---------- */
.sthead{padding:14px 16px 12px;border-bottom:1px solid var(--line);background:var(--panel-2)}
.sthead .top{display:flex;align-items:flex-start;gap:11px}
.symbadge{width:34px;height:34px;flex:none;border-radius:5px;display:grid;place-items:center;
  background:var(--panel);border:1px solid var(--line);color:var(--ink)}
.symbadge.sel{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
.callsign{font-family:var(--f-mono);font-weight:600;font-size:19px;letter-spacing:-.01em;line-height:1.15}
.callsign a{color:inherit}
.sthead .desc{font-size:13.5px;color:var(--muted);line-height:1.35;margin-top:2px}
.pills{display:flex;flex-wrap:wrap;gap:5px;margin-top:10px}
.pill{font-family:var(--f-disp);font-weight:600;font-size:11px;letter-spacing:.09em;
  text-transform:uppercase;padding:2px 7px;border-radius:3px;border:1px solid var(--line);
  color:var(--ink-2);background:var(--panel);display:inline-flex;align-items:center;gap:5px}
.pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor}
.pill.live{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent)}
.pill.stale{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 40%,transparent)}
.pill.gone{color:var(--dead)}

.tabs{display:flex;border-bottom:1px solid var(--line);padding:0 8px;background:var(--panel);
  overflow-x:auto;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tab{appearance:none;background:none;border:0;border-bottom:2px solid transparent;
  font-family:var(--f-disp);font-weight:600;font-size:13px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted);padding:9px 10px;cursor:pointer;white-space:nowrap}
.tab:hover{color:var(--ink)}
.tab[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--accent)}
.tabbody{overflow-y:auto;flex:1;min-height:0}

.kv{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--line-2);margin:0}
.kv > div{background:var(--panel);padding:9px 12px 10px}
.kv dt{font-family:var(--f-disp);font-weight:600;font-size:10.5px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);margin:0 0 1px}
.kv dd{margin:0;font-family:var(--f-mono);font-size:14px;font-variant-numeric:tabular-nums;color:var(--ink)}
.kv dd .u{font-family:var(--f-body);font-size:12px;color:var(--muted);margin-left:2px}
.kv .wide{grid-column:1/-1}

.block{padding:12px 14px;border-bottom:1px solid var(--line-2)}
.block:last-child{border-bottom:0}
.blocktitle{font-family:var(--f-disp);font-weight:600;font-size:11px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);margin-bottom:8px;
  display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.blocktitle .aux{font-family:var(--f-mono);font-size:11px;letter-spacing:0;
  text-transform:none;color:var(--faint)}
.empty{font-size:13px;color:var(--muted);padding:16px 14px}

.path{display:flex;flex-wrap:wrap;align-items:center;gap:4px;font-family:var(--f-mono);font-size:12px}
.hop{border:1px solid var(--line);border-radius:3px;padding:2px 6px;
  background:var(--panel-2);color:var(--ink-2)}
.hop.used{border-color:var(--c1);color:var(--c1);background:transparent}
.hop.src{border-color:var(--accent);color:var(--accent);background:transparent;font-weight:500}
.hop.q{color:var(--faint);border-style:dashed}
.arrow{color:var(--faint);font-size:11px}

.rawlog{font-family:var(--f-mono);font-size:11.5px;line-height:1.5}
.rawrow{padding:6px 0;border-bottom:1px solid var(--line-2);overflow-x:auto;white-space:nowrap}
.rawrow:last-child{border-bottom:0}
.rawrow .t{color:var(--faint);margin-right:8px}
.rawrow .src{color:var(--accent)}
.rawrow .dst{color:var(--muted)}
.rawrow .bd{color:var(--ink)}

/* ---------- map chrome ---------- */
.mapfilters{position:absolute;top:10px;left:54px;display:flex;gap:4px;flex-wrap:wrap;z-index:900}
.fbtn{appearance:none;cursor:pointer;font-family:var(--f-disp);font-weight:600;font-size:11px;
  letter-spacing:.09em;text-transform:uppercase;padding:4px 9px;border-radius:4px;
  border:1px solid var(--line);background:color-mix(in srgb,var(--panel) 90%,transparent);
  color:var(--muted);backdrop-filter:blur(3px)}
.fbtn:hover{color:var(--ink)}
.fbtn[aria-pressed="true"]{background:var(--ink);color:var(--panel);border-color:var(--ink)}
/* The blur on these is deliberate and was once removed on a wrong hunch. It is
   expensive — the compositor snapshots everything beneath, and beneath is a
   WebGL canvas — and it was suspected of starving Safari of compositing layers
   until the markers stopped painting. It was not the cause: the map was
   attaching a marker element for every station in the world, and culling those
   to the ones near the view fixed it. See Troubleshooting in README.md. */
.mapstats{position:absolute;left:10px;bottom:10px;z-index:900;min-width:172px;
  background:color-mix(in srgb,var(--panel) 92%,transparent);backdrop-filter:blur(3px);
  border:1px solid var(--line);border-radius:5px;overflow:hidden;box-shadow:var(--shadow)}
.statshead{appearance:none;width:100%;display:flex;align-items:center;gap:6px;
  background:none;border:0;cursor:pointer;color:inherit;font:inherit;padding:7px 9px;text-align:left}
.statshead:hover{background:var(--panel-2)}
.statshead .lt{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-2)}
.statshead .chev{margin-left:auto;color:var(--faint);transition:transform .15s}
.statshead[aria-expanded="false"] .chev{transform:rotate(-90deg)}
.statsbody{margin:0;display:grid;grid-template-columns:1fr 1fr;gap:1px;
  background:var(--line-2);border-top:1px solid var(--line)}
/* An author display value beats the UA's [hidden] rule, so restate it. */
.statsbody[hidden]{display:none}
.statsbody > div{background:var(--panel);padding:5px 9px 6px}
.statsbody dt{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);margin:0}
.statsbody dd{margin:0;font-family:var(--f-mono);font-size:12.5px;line-height:1.25;
  font-variant-numeric:tabular-nums;color:var(--ink);white-space:nowrap}
.statsbody dd .u{font-family:var(--f-body);font-size:10.5px;color:var(--muted);margin-left:2px}
.statswarn{margin:0;padding:5px 9px 6px;border-top:1px solid var(--line);
  font-family:var(--f-mono);font-size:11px;color:var(--warn);background:var(--panel)}
@media (max-width:900px){.mapstats{display:none}}

/* Distress beacons. Loud on purpose: on a full feed this is the one thing on
   the map that might need someone to act. */
.mapalerts{position:absolute;left:50%;transform:translateX(-50%);top:10px;z-index:1000;
  width:min(340px,calc(100% - 130px));border-radius:6px;overflow:hidden;
  border:1px solid var(--alert);background:var(--alert-soft);
  backdrop-filter:blur(3px);box-shadow:var(--shadow)}
.mapalerts .ah a{color:#fff;text-decoration:none}
.mapalerts .ah a:hover{text-decoration:underline}
.mapalerts .ah{display:flex;align-items:center;gap:7px;padding:6px 9px;
  background:var(--alert);color:#fff;
  font-family:var(--f-disp);font-weight:600;font-size:10.5px;letter-spacing:.11em;
  text-transform:uppercase}
.mapalerts .ah .n{margin-left:auto;font-family:var(--f-mono);letter-spacing:0}
.alertitem{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:center;
  width:100%;text-align:left;padding:6px 9px;border:0;border-top:1px solid var(--alert-line);
  background:none;cursor:pointer;color:inherit;font:inherit}
.alertitem:hover{background:var(--alert-line)}
.alertitem .cs{font-family:var(--f-mono);font-size:12.5px;font-weight:500;color:var(--ink)}
.alertitem .st{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--alert)}
.alertitem .meta{font-family:var(--f-mono);font-size:10.5px;color:var(--muted)}
.mk.alert{border-color:var(--alert);color:var(--alert);
  background:var(--alert-soft);animation:alertpulse 1.6s ease-in-out infinite}
@keyframes alertpulse{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--alert) 60%,transparent)}
  70%{box-shadow:0 0 0 8px transparent}}
@media (prefers-reduced-motion:reduce){.mk.alert{animation:none}}

/* A real Leaflet control, so .leaflet-bar gives it the zoom buttons' exact
   size, border and radius in both touch and non-touch modes. Only the glyph
   centring and the busy tint are ours. */
.maphome a{display:flex;align-items:center;justify-content:center}
.maphome a.busy{color:var(--accent)}
.mehere{width:14px;height:14px;border-radius:50%;
  background:var(--accent);border:2px solid var(--panel);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 30%,transparent)}

/* The RF paths drawn while a station is hovered: a label at the gate end of
   each line, so the callsign and distance read without a click. */
.gatelbl{font-family:var(--f-mono);font-size:10px;white-space:nowrap;
  color:var(--ground);background:var(--c4);padding:1px 4px;border-radius:2px;
  pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,.3)}
.gatelbl b{font-weight:600}

/* Density cells stand in for markers when the view is too wide to draw them. */
.dcell{display:grid;place-items:center;border-radius:50%;cursor:pointer;
  font-family:var(--f-mono);font-variant-numeric:tabular-nums;font-weight:500;
  color:#fff;background:color-mix(in srgb,var(--c1) 78%,transparent);
  border:1px solid color-mix(in srgb,var(--c1) 45%,#fff);text-shadow:0 1px 2px rgba(0,0,0,.35)}
.dcell:hover{background:var(--c1)}
.densitynote{position:absolute;left:50%;transform:translateX(-50%);bottom:10px;z-index:900;
  background:color-mix(in srgb,var(--panel) 92%,transparent);backdrop-filter:blur(3px);
  border:1px solid var(--line);border-radius:5px;padding:5px 10px;
  font-family:var(--f-mono);font-size:11px;color:var(--muted);white-space:nowrap}

/* Transient word from the map itself, in the same place as the density note.
   Used when something the reader asked for did not happen, which otherwise
   shows as the map quietly not moving. */
.mapnote{position:absolute;left:50%;transform:translateX(-50%);bottom:10px;z-index:950;
  background:color-mix(in srgb,var(--alert-soft) 94%,transparent);backdrop-filter:blur(3px);
  border:1px solid var(--alert-line);border-radius:5px;padding:6px 11px;
  font-size:11.5px;color:var(--ink);max-width:min(420px,80%);text-align:center;
  box-shadow:var(--shadow)}

/* The right-click menu on a station marker. Fixed, because it is placed at
   the pointer in viewport coordinates, and above the Leaflet panes. */
.mapmenu{position:fixed;z-index:1400;min-width:170px;padding:4px;
  background:var(--panel);border:1px solid var(--line);border-radius:6px;
  box-shadow:var(--shadow)}
.mapmenu .mm-head{font-family:var(--f-mono);font-size:11px;font-weight:600;
  color:var(--muted);padding:4px 8px 5px;border-bottom:1px solid var(--line-2);
  margin-bottom:3px}
.mapmenu button{display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  padding:5px 8px;border:0;border-radius:4px;background:none;color:inherit;
  font:inherit;cursor:pointer}
.mapmenu button:hover{background:var(--panel-2)}
.mapmenu button:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.mapmenu .mm-what{font-family:var(--f-disp);font-weight:600;font-size:9.5px;
  letter-spacing:.11em;text-transform:uppercase;color:var(--muted)}
.mapmenu .mm-val{font-family:var(--f-mono);font-size:12px;color:var(--ink)}

.maplegend{position:absolute;right:10px;top:10px;z-index:900;
  background:color-mix(in srgb,var(--panel) 92%,transparent);backdrop-filter:blur(3px);
  border:1px solid var(--line);border-radius:5px;padding:8px 10px;font-size:11px;color:var(--muted)}
.maplegend .lt{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-2);margin-bottom:6px}
.ramp{display:flex;align-items:center;gap:6px;font-family:var(--f-mono);font-size:10.5px}
.rampbar{width:70px;height:7px;border-radius:2px;
  background:linear-gradient(90deg,var(--ramp-1),var(--ramp-2),var(--ramp-3),var(--ramp-4),var(--ramp-5))}

/* Leaflet markers are DOM, so they inherit the theme directly. */
.mk{width:26px;height:26px;border-radius:5px;display:grid;place-items:center;
  background:var(--panel);border:1px solid var(--line);color:var(--ink-2);
  box-shadow:0 1px 3px rgba(0,0,0,.22);cursor:pointer}
.mk:hover{border-color:var(--ink-2);color:var(--ink)}
.mk.sel{border-color:var(--accent);color:var(--accent);background:var(--accent-soft);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
.mk.nolabel .lbl{display:none}
.mk .lbl{position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:3px;
  font-family:var(--f-mono);font-size:10px;white-space:nowrap;color:var(--ink);
  background:color-mix(in srgb,var(--panel) 92%,transparent);padding:0 3px;
  border-radius:2px;pointer-events:none}
.mk.sel .lbl{color:var(--accent);font-weight:500}
.leaflet-container{background:var(--sunk);font-family:var(--f-body)}
/* The basemap is a MapLibre canvas in Leaflet's tile pane, styled by
   maplibre-gl.css; --sunk above shows through until it has drawn. */

/* Pinned to a compositing layer of its own, because Safari was dropping it.
   The whole pane would stop painting — markers, density bubbles, the location
   dot — while every element stayed in the layout tree at the right size and
   position and stayed clickable, which is the signature of a lost layer rather
   than of anything this code does.
   
   This is not the layer-budget bug in Troubleshooting: that one had 1468
   markers attached and was fixed by culling. This happened with eight, with
   opacity 1, visibility visible, no filter and no containment anywhere up the
   ancestor chain, and it survived a forced red background and z-index 9999 on
   every marker — the compositor simply painted none of it. Setting these two
   properties on the live pane brought them straight back.
   
   Both are here because both were applied in the test that fixed it and
   neither was isolated afterwards. They cost one layer hint on one element,
   which is cheaper than finding out which. The pane is already
   position:absolute, so promoting it changes no containing block and moves
   nothing. */
.leaflet-marker-pane{will-change:transform;transform:translateZ(0)}
.leaflet-control-attribution{font-size:10px;background:color-mix(in srgb,var(--panel) 80%,transparent)!important;
  color:var(--muted)!important}
.leaflet-control-attribution a{color:var(--muted)!important}

/* ---------- activity feed ---------- */
.feedhead{padding:11px 13px 9px;border-bottom:1px solid var(--line);
  display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.feed{overflow-y:auto;flex:1;min-height:0}
.feeditem{display:grid;grid-template-columns:24px 1fr auto;gap:9px;align-items:center;
  width:100%;text-align:left;padding:9px 13px;border:0;border-bottom:1px solid var(--line-2);
  background:none;cursor:pointer;color:inherit;font:inherit}
.feeditem:hover{background:var(--panel-2)}
.feeditem[aria-current="true"]{background:var(--accent-soft);box-shadow:inset 2px 0 0 var(--accent)}
.feeditem .ico{width:24px;height:24px;border-radius:4px;display:grid;place-items:center;
  border:1px solid var(--line);color:var(--ink-2);background:var(--panel)}
.feeditem[aria-current="true"] .ico{border-color:var(--accent);color:var(--accent)}
.feeditem .cs{font-family:var(--f-mono);font-size:13px;font-weight:500;line-height:1.2}
.feeditem .meta{font-size:11.5px;color:var(--muted);line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.feeditem .age{font-family:var(--f-mono);font-size:11px;color:var(--faint);
  font-variant-numeric:tabular-nums;text-align:right}
.feeditem .age.live{color:var(--ok)}
.feeditem .age.stale{color:var(--warn)}

/* ---------- station page ---------- */
.page{max-width:1360px;margin:0 auto;padding:24px}
.stpage{border:1px solid var(--line);border-radius:8px;background:var(--panel);
  overflow:hidden;box-shadow:var(--shadow)}
.stpage-head{display:flex;flex-wrap:wrap;gap:20px;align-items:center;justify-content:space-between;
  padding:18px 20px;border-bottom:1px solid var(--line);background:var(--panel-2)}
.stpage-id{display:flex;align-items:center;gap:13px}
.stpage-id .symbadge{width:44px;height:44px}
.stpage-id .callsign{font-size:26px}
.readouts{display:flex;flex-wrap:wrap;border:1px solid var(--line);border-radius:6px;
  overflow:hidden;background:var(--line-2);gap:1px}
.readout{background:var(--panel);padding:8px 16px;min-width:104px}
.readout .l{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted)}
.readout .v{font-family:var(--f-disp);font-weight:700;font-size:25px;line-height:1.1;
  font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.readout .v small{font-family:var(--f-body);font-weight:500;font-size:12px;color:var(--muted);margin-left:2px}
.readout.hero .v{color:var(--c1)}

.stgrid{display:grid;grid-template-columns:minmax(0,1.62fr) minmax(0,1fr)}
.stgrid > *{min-width:0}
.stcol-l{border-right:1px solid var(--line)}
.pad{padding:16px 20px}
.pad + .pad{border-top:1px solid var(--line-2)}
#trackmap{height:300px;border-radius:6px;overflow:hidden;border:1px solid var(--line)}

.smallmults{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:1px;background:var(--line-2)}
.smallmults > figure{margin:0;background:var(--panel);padding:11px 12px 8px}
.smallmults figcaption{font-family:var(--f-disp);font-weight:600;font-size:10.5px;
  letter-spacing:.11em;text-transform:uppercase;color:var(--muted);
  display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:4px}
.smallmults figcaption .now{font-family:var(--f-mono);font-size:13px;letter-spacing:0;
  text-transform:none;color:var(--ink);font-weight:500}

.heardlist{display:flex;flex-direction:column;gap:9px}
.heardrow{display:grid;grid-template-columns:minmax(80px,110px) 1fr 52px;gap:10px;
  align-items:center;font-size:12.5px}
.heardrow .cs{font-family:var(--f-mono);font-size:12.5px;overflow:hidden;text-overflow:ellipsis}
.heardrow .track{height:9px;background:var(--sunk);border-radius:2px;overflow:hidden}
.heardrow .fill{height:100%;background:var(--c1);border-radius:2px}
.heardrow .n{font-family:var(--f-mono);font-size:12px;color:var(--muted);
  text-align:right;font-variant-numeric:tabular-nums}

.wxgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;
  background:var(--line-2);border:1px solid var(--line-2);border-radius:5px;overflow:hidden}
.wxcell{background:var(--panel);padding:9px 11px}
.wxcell .l{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted)}
.wxcell .v{font-family:var(--f-mono);font-size:15px;font-variant-numeric:tabular-nums}
.wxcell .v small{font-family:var(--f-body);font-size:11.5px;color:var(--muted)}

.chartfig{margin:0}
.charthead{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:6px}
.charthead h3{font-family:var(--f-disp);font-weight:600;font-size:14px;letter-spacing:.03em}
.charthead .sub{font-size:12px;color:var(--muted);font-family:var(--f-mono)}
.chartbox{position:relative}
.chartbox svg{display:block;width:100%;height:auto;overflow:visible}
.tip{position:absolute;pointer-events:none;opacity:0;transition:opacity .1s;
  background:var(--ink);color:var(--ground);border-radius:4px;padding:5px 8px;
  font-family:var(--f-mono);font-size:11px;line-height:1.45;white-space:nowrap;z-index:12;
  transform:translate(-50%,-115%);font-variant-numeric:tabular-nums}
.tip.on{opacity:1}
.tip b{font-weight:600;color:var(--panel)}
.legend{display:flex;gap:12px;flex-wrap:wrap;font-size:11.5px;color:var(--muted);margin-top:6px}
.legend span{display:inline-flex;align-items:center;gap:5px}
.legend i{width:11px;height:3px;border-radius:2px;display:block}

.crumb{display:flex;align-items:center;gap:8px;margin-bottom:14px;font-size:13px;color:var(--muted)}
.banner{border:1px solid var(--warn);background:color-mix(in srgb,var(--warn) 10%,transparent);
  color:var(--ink);border-radius:6px;padding:10px 14px;font-size:13px;margin-bottom:16px}

/* ---------- messages ---------- */
/* Wider than it was: two columns need the room the single one did not. */
.msgpage{max-width:1240px;display:flex;flex-direction:column;gap:14px}
/* The feed on the left, composing on the right. align-items:start so the
   compose box is its own height rather than stretched to the feed's. */
.msgcols{display:grid;grid-template-columns:minmax(0,1fr) 336px;gap:16px;align-items:start}
/* Sticky, because the feed never ends: a form that scrolls out of reach on a
   page whose content keeps arriving is a form nobody uses twice. */
.msgside{position:sticky;top:14px;display:flex;flex-direction:column;gap:14px;min-width:0}
.chips{display:flex;gap:5px;flex-wrap:wrap;padding:11px 14px;
  border-bottom:1px solid var(--line);background:var(--panel-2)}
.chip{font-family:var(--f-disp);font-weight:600;font-size:11px;letter-spacing:.09em;
  text-transform:uppercase;padding:3px 9px;border-radius:4px;border:1px solid var(--line);
  color:var(--muted);display:inline-flex;align-items:center;gap:6px;
  text-decoration:none;background:var(--panel)}
.chip:hover{color:var(--ink);text-decoration:none}
.chip .c{font-family:var(--f-mono);font-size:10px;letter-spacing:0;color:var(--faint)}
.chip.on{background:var(--ink);color:var(--panel);border-color:var(--ink)}
.chip.on .c{color:var(--panel)}

/* Sits between the kind chips and the live toggle, matching the chip height
   so the row reads as one control strip rather than a box dropped into it. */
.msgsearch{margin-left:auto;display:flex;align-items:center;gap:6px;
  padding:0 9px;border:1px solid var(--line);border-radius:4px;background:var(--panel);
  color:var(--faint);min-width:180px}
.msgsearch:focus-within{border-color:var(--focus);color:var(--muted)}
.msgsearch input{appearance:none;border:0;background:none;outline:none;padding:3px 0;
  font-family:var(--f-mono);font-size:11.5px;color:var(--ink);width:100%;min-width:0}
.msgsearch input::placeholder{color:var(--faint);font-family:var(--f-body)}
/* Safari draws its own clear button and reserves space for it. */
.msgsearch input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}

.livetoggle{margin-left:8px;appearance:none;cursor:pointer;display:inline-flex;
  align-items:center;gap:7px;padding:3px 10px;border-radius:4px;
  border:1px solid var(--line);background:var(--panel);color:var(--muted);
  font-family:var(--f-disp);font-weight:600;font-size:11px;letter-spacing:.09em;
  text-transform:uppercase}
.livetoggle:hover{color:var(--ink)}
/* Paused is a held state, so it reads as pressed rather than as another chip. */
.livetoggle[aria-pressed="false"]{background:var(--ink);color:var(--panel);border-color:var(--ink)}
.livetoggle .pulse{width:6px;height:6px}
.livetoggle[aria-pressed="false"] .pulse{background:var(--dead)}
.livetoggle[aria-pressed="false"] .pulse::after{display:none}

.msglist{display:flex;flex-direction:column}
.msg{display:grid;grid-template-columns:74px 1fr;gap:12px;padding:11px 14px;
  border-bottom:1px solid var(--line-2)}
.msg:last-child{border-bottom:0}
.msg .when{font-family:var(--f-mono);font-size:11px;color:var(--faint);
  font-variant-numeric:tabular-nums;padding-top:2px}
.msg .mbody{min-width:0;display:flex;flex-direction:column;gap:3px}
.msg .route{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  font-family:var(--f-mono);font-size:12.5px}
.msg .route .from{color:var(--ink);font-weight:500}
.msg .route .to{color:var(--c1)}
.msg .route .arw{color:var(--faint)}
.msg .mtext{font-family:var(--f-mono);font-size:13px;color:var(--ink);word-break:break-word}
.badge{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;padding:1px 5px;border-radius:3px;border:1px solid currentColor}
.b-bln{color:var(--c1)} .b-wx{color:var(--c3)} .b-bot{color:var(--c5)}
.delivered{font-family:var(--f-mono);font-size:10.5px;color:var(--ok)}
.delivered.pending{color:var(--faint)}
.msgnote{font-size:12.5px;color:var(--muted);margin:0;max-width:66ch}

/* ---------- aloft ---------- */
/* The flight list is the page; picking one fills the right-hand side. Same
   two-column shape as the map, so the eye lands in the same place. */
.aloft{display:grid;grid-template-columns:minmax(0,290px) minmax(0,1fr);gap:16px;
  align-items:start;margin-bottom:18px}
.flightlist{background:var(--panel);border:1px solid var(--line);border-radius:8px;
  overflow:hidden;box-shadow:var(--shadow)}
.flightlist .ftitle{font-family:var(--f-disp);font-weight:600;font-size:12px;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.flightlist .fcount{font-family:var(--f-mono);font-size:11px;color:var(--faint)}
.fl{display:grid;grid-template-columns:1fr auto;gap:2px 10px;align-items:center;
  width:100%;text-align:left;padding:9px 13px;border:0;border-bottom:1px solid var(--line-2);
  background:none;font:inherit;color:inherit;cursor:pointer}
.fl:last-child{border-bottom:0}
.fl:hover{background:var(--panel-2)}
.fl.on{background:var(--accent-soft)}
.fl:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.flmain{display:flex;flex-direction:column;min-width:0}
.flmain .cs{font-family:var(--f-mono);font-size:13px;font-weight:600;color:var(--ink)}
.flmain .sub{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.flnum{font-family:var(--f-mono);font-size:15px;text-align:right;
  font-variant-numeric:tabular-nums;color:var(--ink)}
.flnum .u{font-size:10px;color:var(--muted);margin-left:2px}
/* State is a word, not a colour alone: "descending" and "on the ground" are
   different facts and both are ordinary. */
.fstate{grid-column:2;justify-self:end;font-family:var(--f-mono);font-size:9.5px;
  letter-spacing:.07em;text-transform:uppercase;padding:1px 6px;border-radius:9px;
  border:1px solid currentColor;white-space:nowrap}
.fstate.s-ascending{color:var(--ok)}
.fstate.s-descending{color:var(--warn)}
.fstate.s-down{color:var(--dead)}

.flightbody{background:var(--panel);border:1px solid var(--line);border-radius:8px;
  padding:14px 16px 16px;box-shadow:var(--shadow);min-width:0}
.fhead{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.fcall{font-family:var(--f-mono);font-size:20px;font-weight:600}
.ftag{font-family:var(--f-mono);font-size:10px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--c5);border:1px solid currentColor;border-radius:9px;padding:1px 7px}
.ftag.sonde{color:var(--c4)}
.fmeta{font-size:12.5px;color:var(--muted);margin-left:auto}
.fpanes{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px}
.fpane{border:1px solid var(--line-2);border-radius:6px;background:var(--panel-2);
  overflow:hidden;min-width:0}
.fpane .panetitle{display:flex;justify-content:space-between;align-items:baseline;gap:8px;
  font-family:var(--f-disp);font-weight:600;text-transform:uppercase;letter-spacing:.11em;
  font-size:10.5px;color:var(--muted);padding:8px 11px 4px}
.fpane .panetitle .aux{font-family:var(--f-mono);font-size:9.5px;letter-spacing:0;
  text-transform:none;color:var(--faint)}
.fpane .chartbox{padding:0 4px 4px}
#flightmap{height:230px;background:var(--sunk)}
.flightbody .block{margin-top:14px}

/* The list runs to as many flights as the history holds while the detail is
   one panel, so reading down the list used to scroll the thing being read off
   the top of the screen. The panel follows instead. It needs a ceiling as
   well as a top: a sticky box taller than the viewport pins its head and puts
   its foot out of reach, so anything that does not fit scrolls inside the
   panel rather than becoming unreachable. The grid is already align-items:
   start, which sticky in a grid item requires. */
@media (min-width:901px){
  .flightbody{position:sticky;top:16px;
    max-height:calc(100vh - 32px);max-height:calc(100dvh - 32px);
    overflow-y:auto;overscroll-behavior:contain}
}

@media (max-width:1100px){
  .fpanes{grid-template-columns:1fr}
  #flightmap{height:260px}
}
@media (max-width:900px){
  .aloft{grid-template-columns:1fr}
  .flightlist{max-height:340px;overflow-y:auto}
}
.msgnote code{font-family:var(--f-mono);font-size:11.5px}

/* ---------- distress history ---------- */
.ahead{display:flex;align-items:center;gap:8px;padding:8px 14px;background:var(--alert);
  color:#fff;font-family:var(--f-disp);font-weight:600;font-size:10.5px;letter-spacing:.11em;
  text-transform:uppercase}
.ahead.warn{background:var(--warn)}
.arow{display:grid;grid-template-columns:132px 1fr 148px;gap:14px;padding:11px 14px;
  border-bottom:1px solid var(--line-2);align-items:start}
.arow:last-child{border-bottom:0}
.arow .when{font-family:var(--f-mono);font-size:11px;color:var(--faint);
  font-variant-numeric:tabular-nums}
.arow .who{display:flex;flex-direction:column;gap:3px;min-width:0}
.arow .cs{font-family:var(--f-mono);font-size:13px;font-weight:500;color:var(--ink);
  display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.arow .cmt{font-family:var(--f-mono);font-size:12px;color:var(--muted);word-break:break-word}
.arow .where{font-family:var(--f-mono);font-size:11px;color:var(--c1);text-align:right;
  font-variant-numeric:tabular-nums}
.tier{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;padding:1px 5px;border-radius:3px;border:1px solid currentColor}
.t-em{color:var(--alert)} .t-pr{color:var(--warn)}
.fixes{display:block;font-family:var(--f-mono);font-size:10.5px;color:var(--faint)}
.sep{width:1px;height:18px;background:var(--line);margin:0 4px;display:inline-block}

/* ---------- responsive ---------- */
@media (max-width:1180px){
  .app{grid-template-columns:320px minmax(0,1fr)}
  .app.nodetail{grid-template-columns:minmax(0,1fr)}
  .col.right{display:none}
}
@media (max-width:900px){
  .msg{grid-template-columns:58px 1fr}
  .arow{grid-template-columns:1fr;gap:6px}
  .arow .where{text-align:left}
  .app,.app.nodetail{grid-template-columns:1fr;height:auto}
  .col.left{border-right:0;border-bottom:1px solid var(--line)}
  /* Sharing the screen with the detail panel, so the map takes part of it. */
  .col.mid{height:60vh;min-height:340px}
  /* Nothing selected, which is how the page opens: the map is the whole page,
     so it fills the viewport instead of stopping short and leaving a void.
     dvh follows the browser chrome as it hides; vh is the fallback. */
  .app.nodetail .col.mid{height:calc(100vh - 50px);height:calc(100dvh - 50px)}
  .tabbody{max-height:340px}
  .stgrid{grid-template-columns:1fr}
  .stcol-l{border-right:0;border-bottom:1px solid var(--line)}

  /* The bar has more in it than a phone is wide. Drop the decorations, keep
     the search, the messages link and the theme toggle. */
  .appbar{gap:8px;padding:0 10px}
  .appbar .livestat{display:none}
  .searchbox{min-width:0}
  .searchbox kbd{display:none}
  .navlink{padding:4px 6px;letter-spacing:.08em}
  .msgpage{padding:16px 12px}
}

/* --- Records ------------------------------------------------------------
   The verification line is the content, not a footnote: on this page how a
   number was checked matters as much as the number, so it gets its own band
   under every figure rather than a tooltip. */
.rechead{max-width:66ch;margin-bottom:20px}
.recstats{display:flex;gap:14px;flex-wrap:wrap;margin-top:11px;
  font-family:var(--f-disp);font-weight:600;font-size:11px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted)}
.recstats b{font-family:var(--f-mono);font-size:13px;color:var(--ink);margin-right:3px}
.recstats .rsflag b{color:var(--warn)}
.recstats .rsheld b{color:var(--faint)}

.recs{display:grid;grid-template-columns:repeat(auto-fit,minmax(268px,1fr));gap:1px;
  background:var(--line-2);border:1px solid var(--line);border-radius:6px;overflow:hidden}
.rec{background:var(--panel);padding:13px 15px 14px;display:flex;flex-direction:column;gap:2px}
.rec .t{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted)}
.rec .v{font-family:var(--f-mono);font-weight:600;font-size:25px;line-height:1.15;
  font-variant-numeric:tabular-nums;color:var(--ink);margin-top:3px}
.rec .v .u{font-family:var(--f-body);font-weight:500;font-size:13px;color:var(--muted);
  margin-left:3px}
.rec .who{font-family:var(--f-mono);font-size:12.5px;margin-top:1px}
.rec .who .arrow{color:var(--faint)}
.rec .meta{font-size:12px;color:var(--muted);margin-top:5px}
.rec .when{font-family:var(--f-mono);font-size:11px;color:var(--faint);padding-top:9px}
.chk{margin-top:auto;display:flex;align-items:center;gap:6px;font-size:11.5px;
  font-family:var(--f-mono);padding:4px 7px;border-radius:3px;background:var(--panel-2);
  color:var(--muted);border:1px solid var(--line-2)}
.chk .dot{width:6px;height:6px;border-radius:50%;background:var(--ok);flex:none}
.chk.pass{color:var(--ok)}
.chk.flag{color:var(--warn);background:color-mix(in srgb,var(--warn) 8%,transparent);
  border-color:color-mix(in srgb,var(--warn) 26%,transparent)}
.chk.flag .dot{background:var(--warn)}
.chk.none{color:var(--faint)}
.chk.none .dot{background:var(--dead)}
/* A held record still occupies its slot, so the grid does not reflow as
   records come and go, but it must never read as a figure. */
.rec.withheld .v{color:var(--faint);font-size:19px;font-weight:500}

/* --- Maidenhead grid ----------------------------------------------------
   The overlay is reference, not content: it takes the existing chart blue at
   low opacity so it sits behind the station markers rather than competing
   with them, and resolves on both basemap styles. */
.fsep{width:1px;align-self:stretch;background:var(--line);margin:0 3px}
.gridlabel{font-family:var(--f-mono);font-weight:600;color:var(--c1);
  white-space:nowrap;pointer-events:none;
  /* The marker anchors at the cell centre and a divIcon grows right and down
     from its point, so the label is pulled back onto the centre it names. */
  display:inline-block;transform:translate(-50%,-50%);
  /* A bare label over a street grid reads as one more road, the same problem
     the station labels have, so it gets the same halo. */
  text-shadow:0 0 3px var(--panel), 0 0 3px var(--panel),
              0 0 3px var(--panel), 0 0 3px var(--panel)}
.gridlabel.field{font-size:15px}
.gridlabel.square{font-size:11.5px}
.gridlabel.sub{font-size:10px;opacity:.85}
/* The square a search landed on, so it is findable once the map has moved. */
.gridfound{font-family:var(--f-mono);font-weight:600;font-size:12px;color:var(--accent);
  white-space:nowrap;pointer-events:none;
  text-shadow:0 0 3px var(--panel), 0 0 3px var(--panel),
              0 0 3px var(--panel), 0 0 3px var(--panel)}
/* A grid hit in the search dropdown: same row shape as a station, marked. */
.suggest .gridhit{background:var(--accent-soft);display:flex;align-items:center;gap:7px}
.suggest .gridhit .cs{color:var(--accent);font-weight:600}
.suggest .gtag{margin-left:auto;font-family:var(--f-disp);font-weight:600;font-size:9px;
  letter-spacing:.11em;text-transform:uppercase;color:var(--accent);
  border:1px solid var(--accent);border-radius:2px;padding:0 4px}

/* --- Standard APRS symbols ----------------------------------------------
   The set every other APRS client draws, vendored in symbols/ and served from
   this node like the flags. One background-position against one cached sheet:
   cheaper than the inline <use> it replaces, and it attaches nothing extra to
   the marker pane, which is where layer count has hurt before.

   --s is the drawn size, --c and --r the cell; every sheet is 16 cells across
   and 6 down in ASCII order from '!', which is what app.js computes. The
   plain url() is there for Safari before 17, which ignores image-set: it gets
   the 1x sheet rather than nothing. */
.apsym{position:relative;flex:none;width:var(--s);height:var(--s);background-repeat:no-repeat;
  background-size:calc(var(--s) * 16) calc(var(--s) * 6);
  background-position:calc(var(--c) * var(--s) * -1) calc(var(--r) * var(--s) * -1)}
.apsym.t0{background-image:url("/symbols/aprs-symbols-24-0.png");
  background-image:image-set(url("/symbols/aprs-symbols-24-0.png") 1x,
    url("/symbols/aprs-symbols-24-0@2x.png") 2x)}
.apsym.t1{background-image:url("/symbols/aprs-symbols-24-1.png");
  background-image:image-set(url("/symbols/aprs-symbols-24-1.png") 1x,
    url("/symbols/aprs-symbols-24-1@2x.png") 2x)}
/* An overlay character sits on top of its alternate-table symbol, as it does
   on the air. */
.apsym .ov{position:absolute;inset:0;background-image:url("/symbols/aprs-symbols-24-2.png");
  background-image:image-set(url("/symbols/aprs-symbols-24-2.png") 1x,
    url("/symbols/aprs-symbols-24-2@2x.png") 2x)}

/* --- Country flags ------------------------------------------------------
   Vendored from Flagpedia and served from /flags, one small cached file each,
   so a page fetches only the flags it actually shows. See flags/NOTICE.
   The class list below is mechanical; the entity-to-flag decision is in
   flags.go. */
.flag{display:inline-block;flex:none;background-size:cover;background-position:center;
  border:1px solid color-mix(in srgb,var(--ink) 22%,transparent);border-radius:2px}
.flag-ad{background-image:url("/flags/ad.svg")}
.flag-ae{background-image:url("/flags/ae.svg")}
.flag-af{background-image:url("/flags/af.svg")}
.flag-ag{background-image:url("/flags/ag.svg")}
.flag-ai{background-image:url("/flags/ai.svg")}
.flag-al{background-image:url("/flags/al.svg")}
.flag-am{background-image:url("/flags/am.svg")}
.flag-ao{background-image:url("/flags/ao.svg")}
.flag-ar{background-image:url("/flags/ar.svg")}
.flag-as{background-image:url("/flags/as.svg")}
.flag-at{background-image:url("/flags/at.svg")}
.flag-au{background-image:url("/flags/au.svg")}
.flag-aw{background-image:url("/flags/aw.svg")}
.flag-ax{background-image:url("/flags/ax.svg")}
.flag-az{background-image:url("/flags/az.svg")}
.flag-ba{background-image:url("/flags/ba.svg")}
.flag-bb{background-image:url("/flags/bb.svg")}
.flag-bd{background-image:url("/flags/bd.svg")}
.flag-be{background-image:url("/flags/be.svg")}
.flag-bf{background-image:url("/flags/bf.svg")}
.flag-bg{background-image:url("/flags/bg.svg")}
.flag-bh{background-image:url("/flags/bh.svg")}
.flag-bi{background-image:url("/flags/bi.svg")}
.flag-bj{background-image:url("/flags/bj.svg")}
.flag-bl{background-image:url("/flags/bl.svg")}
.flag-bm{background-image:url("/flags/bm.svg")}
.flag-bn{background-image:url("/flags/bn.svg")}
.flag-bo{background-image:url("/flags/bo.svg")}
.flag-bq{background-image:url("/flags/bq.svg")}
.flag-br{background-image:url("/flags/br.svg")}
.flag-bs{background-image:url("/flags/bs.svg")}
.flag-bt{background-image:url("/flags/bt.svg")}
.flag-bv{background-image:url("/flags/bv.svg")}
.flag-bw{background-image:url("/flags/bw.svg")}
.flag-by{background-image:url("/flags/by.svg")}
.flag-bz{background-image:url("/flags/bz.svg")}
.flag-ca{background-image:url("/flags/ca.svg")}
.flag-cd{background-image:url("/flags/cd.svg")}
.flag-cf{background-image:url("/flags/cf.svg")}
.flag-cg{background-image:url("/flags/cg.svg")}
.flag-ch{background-image:url("/flags/ch.svg")}
.flag-ci{background-image:url("/flags/ci.svg")}
.flag-ck{background-image:url("/flags/ck.svg")}
.flag-cl{background-image:url("/flags/cl.svg")}
.flag-cm{background-image:url("/flags/cm.svg")}
.flag-cn{background-image:url("/flags/cn.svg")}
.flag-co{background-image:url("/flags/co.svg")}
.flag-cr{background-image:url("/flags/cr.svg")}
.flag-cu{background-image:url("/flags/cu.svg")}
.flag-cv{background-image:url("/flags/cv.svg")}
.flag-cw{background-image:url("/flags/cw.svg")}
.flag-cy{background-image:url("/flags/cy.svg")}
.flag-cz{background-image:url("/flags/cz.svg")}
.flag-de{background-image:url("/flags/de.svg")}
.flag-dj{background-image:url("/flags/dj.svg")}
.flag-dk{background-image:url("/flags/dk.svg")}
.flag-dm{background-image:url("/flags/dm.svg")}
.flag-do{background-image:url("/flags/do.svg")}
.flag-dz{background-image:url("/flags/dz.svg")}
.flag-ec{background-image:url("/flags/ec.svg")}
.flag-ee{background-image:url("/flags/ee.svg")}
.flag-eg{background-image:url("/flags/eg.svg")}
.flag-eh{background-image:url("/flags/eh.svg")}
.flag-er{background-image:url("/flags/er.svg")}
.flag-es{background-image:url("/flags/es.svg")}
.flag-et{background-image:url("/flags/et.svg")}
.flag-fi{background-image:url("/flags/fi.svg")}
.flag-fj{background-image:url("/flags/fj.svg")}
.flag-fk{background-image:url("/flags/fk.svg")}
.flag-fm{background-image:url("/flags/fm.svg")}
.flag-fo{background-image:url("/flags/fo.svg")}
.flag-fr{background-image:url("/flags/fr.svg")}
.flag-ga{background-image:url("/flags/ga.svg")}
.flag-gb{background-image:url("/flags/gb.svg")}
.flag-gb-eng{background-image:url("/flags/gb-eng.svg")}
.flag-gb-sct{background-image:url("/flags/gb-sct.svg")}
.flag-gb-wls{background-image:url("/flags/gb-wls.svg")}
.flag-gd{background-image:url("/flags/gd.svg")}
.flag-ge{background-image:url("/flags/ge.svg")}
.flag-gf{background-image:url("/flags/gf.svg")}
.flag-gg{background-image:url("/flags/gg.svg")}
.flag-gh{background-image:url("/flags/gh.svg")}
.flag-gi{background-image:url("/flags/gi.svg")}
.flag-gl{background-image:url("/flags/gl.svg")}
.flag-gm{background-image:url("/flags/gm.svg")}
.flag-gn{background-image:url("/flags/gn.svg")}
.flag-gp{background-image:url("/flags/gp.svg")}
.flag-gq{background-image:url("/flags/gq.svg")}
.flag-gr{background-image:url("/flags/gr.svg")}
.flag-gt{background-image:url("/flags/gt.svg")}
.flag-gu{background-image:url("/flags/gu.svg")}
.flag-gw{background-image:url("/flags/gw.svg")}
.flag-gy{background-image:url("/flags/gy.svg")}
.flag-hk{background-image:url("/flags/hk.svg")}
.flag-hn{background-image:url("/flags/hn.svg")}
.flag-hr{background-image:url("/flags/hr.svg")}
.flag-ht{background-image:url("/flags/ht.svg")}
.flag-hu{background-image:url("/flags/hu.svg")}
.flag-id{background-image:url("/flags/id.svg")}
.flag-ie{background-image:url("/flags/ie.svg")}
.flag-il{background-image:url("/flags/il.svg")}
.flag-im{background-image:url("/flags/im.svg")}
.flag-in{background-image:url("/flags/in.svg")}
.flag-io{background-image:url("/flags/io.svg")}
.flag-iq{background-image:url("/flags/iq.svg")}
.flag-ir{background-image:url("/flags/ir.svg")}
.flag-is{background-image:url("/flags/is.svg")}
.flag-it{background-image:url("/flags/it.svg")}
.flag-je{background-image:url("/flags/je.svg")}
.flag-jm{background-image:url("/flags/jm.svg")}
.flag-jo{background-image:url("/flags/jo.svg")}
.flag-jp{background-image:url("/flags/jp.svg")}
.flag-ke{background-image:url("/flags/ke.svg")}
.flag-kg{background-image:url("/flags/kg.svg")}
.flag-kh{background-image:url("/flags/kh.svg")}
.flag-ki{background-image:url("/flags/ki.svg")}
.flag-km{background-image:url("/flags/km.svg")}
.flag-kn{background-image:url("/flags/kn.svg")}
.flag-kp{background-image:url("/flags/kp.svg")}
.flag-kr{background-image:url("/flags/kr.svg")}
.flag-kw{background-image:url("/flags/kw.svg")}
.flag-ky{background-image:url("/flags/ky.svg")}
.flag-kz{background-image:url("/flags/kz.svg")}
.flag-la{background-image:url("/flags/la.svg")}
.flag-lb{background-image:url("/flags/lb.svg")}
.flag-lc{background-image:url("/flags/lc.svg")}
.flag-li{background-image:url("/flags/li.svg")}
.flag-lk{background-image:url("/flags/lk.svg")}
.flag-lr{background-image:url("/flags/lr.svg")}
.flag-ls{background-image:url("/flags/ls.svg")}
.flag-lt{background-image:url("/flags/lt.svg")}
.flag-lu{background-image:url("/flags/lu.svg")}
.flag-lv{background-image:url("/flags/lv.svg")}
.flag-ly{background-image:url("/flags/ly.svg")}
.flag-ma{background-image:url("/flags/ma.svg")}
.flag-mc{background-image:url("/flags/mc.svg")}
.flag-md{background-image:url("/flags/md.svg")}
.flag-me{background-image:url("/flags/me.svg")}
.flag-mf{background-image:url("/flags/mf.svg")}
.flag-mg{background-image:url("/flags/mg.svg")}
.flag-mh{background-image:url("/flags/mh.svg")}
.flag-mk{background-image:url("/flags/mk.svg")}
.flag-ml{background-image:url("/flags/ml.svg")}
.flag-mm{background-image:url("/flags/mm.svg")}
.flag-mn{background-image:url("/flags/mn.svg")}
.flag-mo{background-image:url("/flags/mo.svg")}
.flag-mp{background-image:url("/flags/mp.svg")}
.flag-mq{background-image:url("/flags/mq.svg")}
.flag-mr{background-image:url("/flags/mr.svg")}
.flag-ms{background-image:url("/flags/ms.svg")}
.flag-mt{background-image:url("/flags/mt.svg")}
.flag-mu{background-image:url("/flags/mu.svg")}
.flag-mv{background-image:url("/flags/mv.svg")}
.flag-mw{background-image:url("/flags/mw.svg")}
.flag-mx{background-image:url("/flags/mx.svg")}
.flag-my{background-image:url("/flags/my.svg")}
.flag-mz{background-image:url("/flags/mz.svg")}
.flag-na{background-image:url("/flags/na.svg")}
.flag-nc{background-image:url("/flags/nc.svg")}
.flag-ne{background-image:url("/flags/ne.svg")}
.flag-ng{background-image:url("/flags/ng.svg")}
.flag-ni{background-image:url("/flags/ni.svg")}
.flag-nl{background-image:url("/flags/nl.svg")}
.flag-no{background-image:url("/flags/no.svg")}
.flag-np{background-image:url("/flags/np.svg")}
.flag-nu{background-image:url("/flags/nu.svg")}
.flag-nz{background-image:url("/flags/nz.svg")}
.flag-om{background-image:url("/flags/om.svg")}
.flag-pa{background-image:url("/flags/pa.svg")}
.flag-pe{background-image:url("/flags/pe.svg")}
.flag-pf{background-image:url("/flags/pf.svg")}
.flag-pg{background-image:url("/flags/pg.svg")}
.flag-ph{background-image:url("/flags/ph.svg")}
.flag-pk{background-image:url("/flags/pk.svg")}
.flag-pl{background-image:url("/flags/pl.svg")}
.flag-pm{background-image:url("/flags/pm.svg")}
.flag-pn{background-image:url("/flags/pn.svg")}
.flag-pr{background-image:url("/flags/pr.svg")}
.flag-ps{background-image:url("/flags/ps.svg")}
.flag-pt{background-image:url("/flags/pt.svg")}
.flag-pw{background-image:url("/flags/pw.svg")}
.flag-py{background-image:url("/flags/py.svg")}
.flag-qa{background-image:url("/flags/qa.svg")}
.flag-re{background-image:url("/flags/re.svg")}
.flag-ro{background-image:url("/flags/ro.svg")}
.flag-rs{background-image:url("/flags/rs.svg")}
.flag-ru{background-image:url("/flags/ru.svg")}
.flag-rw{background-image:url("/flags/rw.svg")}
.flag-sa{background-image:url("/flags/sa.svg")}
.flag-sb{background-image:url("/flags/sb.svg")}
.flag-sc{background-image:url("/flags/sc.svg")}
.flag-sd{background-image:url("/flags/sd.svg")}
.flag-se{background-image:url("/flags/se.svg")}
.flag-sg{background-image:url("/flags/sg.svg")}
.flag-sh{background-image:url("/flags/sh.svg")}
.flag-si{background-image:url("/flags/si.svg")}
.flag-sj{background-image:url("/flags/sj.svg")}
.flag-sk{background-image:url("/flags/sk.svg")}
.flag-sl{background-image:url("/flags/sl.svg")}
.flag-sm{background-image:url("/flags/sm.svg")}
.flag-sn{background-image:url("/flags/sn.svg")}
.flag-so{background-image:url("/flags/so.svg")}
.flag-sr{background-image:url("/flags/sr.svg")}
.flag-ss{background-image:url("/flags/ss.svg")}
.flag-st{background-image:url("/flags/st.svg")}
.flag-sv{background-image:url("/flags/sv.svg")}
.flag-sx{background-image:url("/flags/sx.svg")}
.flag-sy{background-image:url("/flags/sy.svg")}
.flag-sz{background-image:url("/flags/sz.svg")}
.flag-tc{background-image:url("/flags/tc.svg")}
.flag-td{background-image:url("/flags/td.svg")}
.flag-tf{background-image:url("/flags/tf.svg")}
.flag-tg{background-image:url("/flags/tg.svg")}
.flag-th{background-image:url("/flags/th.svg")}
.flag-tj{background-image:url("/flags/tj.svg")}
.flag-tl{background-image:url("/flags/tl.svg")}
.flag-tm{background-image:url("/flags/tm.svg")}
.flag-tn{background-image:url("/flags/tn.svg")}
.flag-to{background-image:url("/flags/to.svg")}
.flag-tr{background-image:url("/flags/tr.svg")}
.flag-tt{background-image:url("/flags/tt.svg")}
.flag-tv{background-image:url("/flags/tv.svg")}
.flag-tw{background-image:url("/flags/tw.svg")}
.flag-tz{background-image:url("/flags/tz.svg")}
.flag-ua{background-image:url("/flags/ua.svg")}
.flag-ug{background-image:url("/flags/ug.svg")}
.flag-un{background-image:url("/flags/un.svg")}
.flag-us{background-image:url("/flags/us.svg")}
.flag-uy{background-image:url("/flags/uy.svg")}
.flag-uz{background-image:url("/flags/uz.svg")}
.flag-va{background-image:url("/flags/va.svg")}
.flag-vc{background-image:url("/flags/vc.svg")}
.flag-ve{background-image:url("/flags/ve.svg")}
.flag-vg{background-image:url("/flags/vg.svg")}
.flag-vi{background-image:url("/flags/vi.svg")}
.flag-vn{background-image:url("/flags/vn.svg")}
.flag-vu{background-image:url("/flags/vu.svg")}
.flag-wf{background-image:url("/flags/wf.svg")}
.flag-ws{background-image:url("/flags/ws.svg")}
.flag-xk{background-image:url("/flags/xk.svg")}
.flag-ye{background-image:url("/flags/ye.svg")}
.flag-yt{background-image:url("/flags/yt.svg")}
.flag-za{background-image:url("/flags/za.svg")}
.flag-zm{background-image:url("/flags/zm.svg")}
.flag-zw{background-image:url("/flags/zw.svg")}

/* Sized per surface: large enough to read the design, never taller than the
   text it sits beside. */
.callsign .flag{width:22px;height:15px;margin-right:8px;vertical-align:1px}
.cs .flag{width:15px;height:10px;margin-right:6px;vertical-align:0}
.suggest .flag{width:15px;height:10px;margin-right:6px}
/* Inline beside a callsign in the message feed, distress list, flights and
   records: small enough to sit on the text baseline without lifting the line. */
.msg .from .flag,.msg .to .flag,.cs .flag,.fcall .flag,.rec .who .flag{
  width:14px;height:10px;margin-right:5px;vertical-align:0}

/* ---------- signing in, and sending ---------- */
/* The account control is the last thing in the bar, which is where an account
   control belongs, and it is a link rather than a button: /login is a page. */
.acct{position:relative}
.acctbtn{appearance:none;display:inline-flex;align-items:center;gap:6px;cursor:pointer;
  font-family:var(--f-disp);font-weight:600;font-size:11.5px;letter-spacing:.1em;
  text-transform:uppercase;border:1px solid var(--line);background:var(--panel-2);
  border-radius:5px;padding:0 10px;height:26px;color:var(--muted);white-space:nowrap;
  text-decoration:none}
.acctbtn:hover{color:var(--ink);text-decoration:none}
/* Signing in and asking for an account, side by side. Register is the quieter
   of the two: most visitors want neither, and of those who want one it is
   nearly always the other. */
.acctout{display:flex;align-items:center;gap:6px}
.acctbtn.quiet{border-color:transparent;background:none;color:var(--faint)}
.acctbtn.quiet:hover{color:var(--ink);background:var(--panel-2);border-color:var(--line)}
.acctbtn.in{border-color:var(--ok);color:var(--ok);background:var(--panel)}
.acctbtn .acctcall{font-family:var(--f-mono);font-size:11.5px;letter-spacing:0;color:var(--ink)}
.acctmenu{position:absolute;right:0;top:32px;min-width:190px;background:var(--panel);
  border:1px solid var(--line);border-radius:6px;box-shadow:var(--shadow);padding:5px;
  z-index:1300}
.acctmenu[hidden]{display:none}
.acctmenu .hd{font-size:11.5px;color:var(--muted);padding:5px 8px 6px;line-height:1.35;
  border-bottom:1px solid var(--line-2);margin-bottom:4px}
.acctmenu .hd b{font-family:var(--f-mono);color:var(--ink);font-weight:500}
.acctmenu button{appearance:none;width:100%;text-align:left;background:none;border:0;
  cursor:pointer;font:inherit;font-size:13px;color:var(--ink);padding:6px 8px;border-radius:4px}
.acctmenu button:hover{background:var(--panel-2)}

/* /account is one narrow column of cards on the page ground, like /login but
   taller: what it holds is a list of facts and one form, not a single box. */
.acctpage{max-width:520px;margin:0 auto;padding:28px 20px 64px}
.acctpage .card{background:var(--panel);border:1px solid var(--line);border-radius:8px;
  box-shadow:var(--shadow);overflow:hidden;margin-bottom:16px}
.acctpage .chead{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:11px 14px;border-bottom:1px solid var(--line-2);background:var(--panel-2)}
.acctpage .chead .lt{font-family:var(--f-disp);font-weight:600;font-size:14px;letter-spacing:.4px}
.acctpage .chead .aux{font-size:11.5px;color:var(--faint)}
.acctpage .cbody{padding:14px}
.acctpage .foot{margin:12px 0 0;font-size:11.5px;color:var(--faint);line-height:1.45}
/* Label and value in two columns: a definition list is what this is, and the
   labels are short enough that a column of them stays a column. */
.facts{display:grid;grid-template-columns:auto 1fr;gap:8px 18px;font-size:13.3px;margin:0}
.facts dt{font-family:var(--f-disp);font-weight:600;font-size:10.5px;line-height:1.7;
  letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.facts dd{margin:0;color:var(--ink)}
.facts dd.mono{font-family:var(--f-mono);font-size:12.6px}
.facts dd .q{color:var(--faint);font-size:12px}
@media (max-width:420px){
  /* Two columns stop being two columns before the values start wrapping. */
  .facts{grid-template-columns:1fr;gap:2px 0}
  .facts dd{margin-bottom:8px}
}
/* One sentence at the top of the page, in the colour of what it is saying. */
.banner{border:1px solid;border-radius:6px;padding:10px 12px;font-size:13px;margin-bottom:16px}
.banner.ok{background:var(--ok-soft);border-color:var(--ok);color:var(--ink)}
.banner.bad{background:var(--alert-soft);border-color:var(--alert-line);color:var(--ink)}

/* /login is a page like any other, so it gets the page ground and a card. */
.loginpage{display:flex;justify-content:center;padding:56px 20px 64px;
  min-height:calc(100vh - 50px);background:var(--ground)}
.logincard{width:min(390px,100%);align-self:flex-start;border:1px solid var(--line);
  border-radius:8px;background:var(--panel);box-shadow:var(--shadow);overflow:hidden;height:max-content}
.loginhead{padding:20px 22px 0}
.loginhead h2{font-family:var(--f-disp);font-weight:700;font-size:23px;margin:0}
.loginhead p{margin:6px 0 0;font-size:13.5px;color:var(--muted)}
.loginbody{padding:16px 22px 6px;display:flex;flex-direction:column;gap:12px}
.loginfoot{display:flex;align-items:center;gap:10px;padding:14px 22px 20px}
.loginfoot .backlink{font-size:13px;color:var(--c1);text-decoration:none;margin-right:auto}
.loginerr{margin:0;font-size:12.5px;color:var(--alert);background:var(--alert-soft);
  border:1px solid var(--alert-line);border-radius:4px;padding:7px 10px}
/* The other half of that: something went right and the next step is here. It
   is what a finished password reset lands on. */
.loginok{margin:0;font-size:12.5px;color:var(--ink);background:var(--ok-soft);
  border:1px solid var(--ok);border-radius:4px;padding:7px 10px}
/* Not an error: nothing is wrong with what was typed, there is simply nobody
   to sign in as. Usually the accounts file is not where the node looked. */
.loginhint{margin:0;font-size:12.5px;color:var(--warn);
  background:color-mix(in srgb,var(--warn) 8%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 30%,transparent);
  border-radius:4px;padding:7px 10px}
.loginhint code{font-family:var(--f-mono);font-size:11.5px;color:var(--ink)}
.lfld{display:flex;flex-direction:column;gap:4px;min-width:0}
.lfld label{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.lfld input{appearance:none;font-family:var(--f-mono);font-size:13px;color:var(--ink);
  background:var(--panel);border:1px solid var(--line);border-radius:4px;padding:8px 10px;
  width:100%;min-width:0}
.lfld input:focus{outline:2px solid var(--focus);outline-offset:-1px;border-color:var(--focus)}
.lfld input.upper{text-transform:uppercase}

.btn{appearance:none;cursor:pointer;font-family:var(--f-disp);font-weight:600;font-size:12px;
  letter-spacing:.1em;text-transform:uppercase;border-radius:4px;padding:8px 16px;
  white-space:nowrap;border:1px solid var(--line);background:var(--panel-2);
  color:var(--ink-2);text-decoration:none;display:inline-flex;align-items:center}
.btn:hover{color:var(--ink);text-decoration:none}
.btn.primary{background:var(--ink);border-color:var(--ink);color:var(--panel)}
.btn.primary:hover{background:var(--accent);border-color:var(--accent);color:var(--panel)}
.btn:disabled{background:var(--sunk);border-color:var(--line);color:var(--faint);cursor:not-allowed}

/* Sending, on the message page. */
.locked{display:flex;flex-direction:column;align-items:flex-start;gap:10px;
  padding:14px;border:1px solid var(--line);border-radius:8px;background:var(--panel);
  font-size:13.5px;color:var(--muted)}
.locked .lk{display:flex;align-items:center;gap:8px;font-family:var(--f-disp);
  font-weight:600;font-size:10px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-2)}
.locked svg{color:var(--faint);flex:none}
.compose{border:1px solid var(--line);border-radius:8px;background:var(--panel);
  overflow:hidden}
.compose .chead{display:flex;align-items:center;gap:9px;padding:11px 14px 0}
.compose .chead .lt{font-family:var(--f-disp);font-weight:600;font-size:10px;
  letter-spacing:.11em;text-transform:uppercase;color:var(--ink-2)}
.compose .chead .aux{margin-left:auto;font-size:11.5px;color:var(--faint);
  font-variant-numeric:tabular-nums}
.cform{display:flex;flex-direction:column;gap:11px;padding:11px 14px 14px}
.crow{display:flex;align-items:center;gap:10px}
.crow .btn{margin-left:auto}
.cfld{display:flex;flex-direction:column;gap:4px;min-width:0}
.cfld label{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.cfld input{appearance:none;font-family:var(--f-mono);font-size:13px;color:var(--ink);
  background:var(--panel);border:1px solid var(--line);border-radius:4px;padding:8px 10px;
  width:100%;min-width:0}
.cfld input:focus{outline:2px solid var(--focus);outline-offset:-1px;border-color:var(--focus)}
/* One control, not two joined: the callsign is the account's and the SSID is
   the node's, so there is nothing beside it to choose from any more. Sunk
   rather than white, because it is the one field on this form that is told to
   you rather than asked of you. */
.fixedcall{display:flex;align-items:center;gap:7px;font-family:var(--f-mono);font-size:13px;
  background:var(--sunk);border:1px solid var(--line);border-radius:4px;
  padding:8px 10px;color:var(--ink);white-space:nowrap;min-width:0;overflow:hidden;
  text-overflow:ellipsis}
.fixedcall svg{color:var(--faint);flex:none}
.ccount{font-family:var(--f-mono);font-size:10.5px;color:var(--faint);
  font-variant-numeric:tabular-nums}
.ccount.over{color:var(--alert)}
.cwire{font-family:var(--f-mono);font-size:10.5px;line-height:1.45;color:var(--muted);
  background:var(--sunk);border:1px solid var(--line-2);border-radius:4px;padding:7px 9px;
  white-space:pre-wrap;word-break:break-all}
.cwire b{color:var(--accent);font-weight:500}
.cnote{margin:0;font-size:12.5px;border-radius:4px;padding:7px 10px}
.cnote[hidden]{display:none}
.cnote.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--ok) 30%,transparent)}
.cnote.bad{color:var(--alert);background:var(--alert-soft);border:1px solid var(--alert-line)}
.cwarn{margin:0;padding:9px 14px 13px;font-size:12.5px;color:var(--warn)}
.cwarn b{font-family:var(--f-mono);color:var(--ink)}
/* ---------- the inbox ---------- */
/* Under composing, in the same column, because they are one activity: the box
   you write in and the messages you are writing back to. The feed beside it is
   a different thing — a record of the whole network, not your own post. */
.inbox{border:1px solid var(--line);border-radius:8px;background:var(--panel);
  overflow:hidden;display:flex;flex-direction:column;min-height:0}
.ihead{display:flex;align-items:center;gap:8px;padding:11px 14px 10px;
  border-bottom:1px solid var(--line-2)}
.ihead .lt{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-2)}
/* The unread count is the one number on this card, so it rides on the title
   rather than being a figure floated somewhere else. */
.icount{font-family:var(--f-mono);font-size:10px;font-weight:500;color:var(--panel);
  background:var(--accent);border-radius:9px;padding:1px 6px;min-width:18px;text-align:center}
.icount[hidden]{display:none}
/* Narrowing the inbox to one of the operator's stations. It stands where the
   caption naming the account used to: that line existed to say what the list
   was showing, and this decides it.

   Scoped, because an unscoped select rule on this site has form — see what
   `.cfld select` did to the compose row, a dozen rules down. */
.ifilter{position:relative;display:inline-flex;align-items:center;margin-left:auto;min-width:0}
.ifilter[hidden]{display:none}
.ifilter select{appearance:none;font-family:var(--f-mono);font-size:11px;color:var(--ink-2);
  background:var(--panel-2);border:1px solid var(--line);border-radius:4px;
  padding:3px 21px 3px 8px;cursor:pointer;min-width:0;max-width:100%}
.ifilter select:hover{color:var(--ink);border-color:var(--muted)}
.ifilter select:focus-visible{outline:2px solid var(--focus);outline-offset:-1px}
/* Narrowed to one station, which is the whole point of it: the control says so
   rather than looking identical whether it is doing anything or not. */
.ifilter select.on{color:var(--accent);border-color:var(--accent)}
.ifilter svg{position:absolute;right:6px;color:var(--faint);pointer-events:none}
/* A filter that matches nothing, which is not the same as an empty inbox:
   .iempty is the server's "no mail at all" and is removed the moment anything
   arrives, whether or not the filter would have shown it. */
.inone{margin:0;padding:14px 12px;font-size:12px;color:var(--muted);line-height:1.45}
/* Its own scroll, so a busy inbox does not push the compose box off the top of
   a sticky column — and sized to the room actually left under that box rather
   than to a fixed height. A sticky column taller than the window does not
   scroll: its bottom simply cannot be reached, which on a short screen is the
   whole list. The subtrahend is the bar, the crumb and the compose box. */
.ilist{display:flex;flex-direction:column;overflow-y:auto;
  max-height:clamp(140px,calc(100vh - 430px),460px)}
/* One row per message, and the whole row replies: what you do to a message in
   your own inbox is answer it, so it is the row's action rather than a control
   hiding at the end of it. */
.ibx{display:grid;grid-template-columns:8px minmax(0,1fr) 16px;gap:2px 8px;align-items:start;
  width:100%;text-align:left;border:0;border-bottom:1px solid var(--line-2);background:none;
  font:inherit;color:inherit;cursor:pointer;padding:9px 12px 10px}
.ibx:last-child{border-bottom:0}
/* Filtered out by the station dropdown. The attribute on its own does nothing
   here: [hidden] is a user-agent rule and the display:grid above outranks it,
   which is why .icount and .linkbtn each carry one of these too. */
.ibx[hidden]{display:none}
.ibx:hover{background:var(--panel-2)}
.ibx:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.ibx.on{background:var(--accent-soft)}
.ibx .dot{grid-row:1;width:6px;height:6px;border-radius:50%;background:transparent;margin-top:5px}
.ibx.unread .dot{background:var(--accent)}
.ibx .top{grid-row:1;grid-column:2;display:flex;align-items:baseline;gap:7px;min-width:0}
.ibx .from{font-family:var(--f-mono);font-size:12.5px;font-weight:500;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Which of the operator's stations it was addressed to. Absent on the bare
   callsign, because most traffic arrives there and a tag on every row says
   nothing. */
.ibx .ssidtag{font-family:var(--f-mono);font-size:10px;color:var(--c1);
  border:1px solid var(--line);border-radius:3px;padding:0 4px;flex:none}
.ibx .when{margin-left:auto;font-family:var(--f-mono);font-size:10.5px;color:var(--faint);
  font-variant-numeric:tabular-nums;flex:none}
.ibx .text{grid-row:2;grid-column:2;font-family:var(--f-mono);font-size:12.5px;
  color:var(--ink-2);line-height:1.45;word-break:break-word}
.ibx.unread .text{color:var(--ink)}
.ibx .state{grid-row:3;grid-column:2;font-family:var(--f-mono);font-size:10px;
  color:var(--ok);margin-top:2px}
.ibx .state.wants{color:var(--faint)}
.ibx .reply{grid-row:1;grid-column:3;color:var(--faint);opacity:0}
.ibx:hover .reply,.ibx:focus-visible .reply,.ibx.on .reply{opacity:1}
.ibx.on .reply{color:var(--accent)}
/* Where the reader had got to last time, so a busy morning does not mean
   counting down from the top to find where the new ones stop. */
.imark{display:flex;align-items:center;gap:8px;padding:5px 12px;background:var(--panel-2);
  border-bottom:1px solid var(--line-2);font-family:var(--f-disp);font-weight:600;
  font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.imark::after{content:"";flex:1;height:1px;background:var(--line)}
.ifoot{padding:8px 12px;border-top:1px solid var(--line);background:var(--panel-2);
  display:flex;align-items:center;gap:8px}
.ifoot .note{font-size:11.5px;color:var(--faint);line-height:1.35}
.ifoot .note code{font-family:var(--f-mono);font-size:10.5px;color:var(--muted)}
.linkbtn{appearance:none;border:0;background:none;padding:0;cursor:pointer;font:inherit;
  font-size:11.5px;color:var(--c1);white-space:nowrap;margin-left:auto}
.linkbtn[hidden]{display:none}
.linkbtn:hover{text-decoration:underline}
.iempty{margin:0;padding:16px 14px;font-size:12.5px;color:var(--muted);line-height:1.5}
.iempty code{font-family:var(--f-mono);font-size:11.5px;color:var(--ink-2)}

/* Stacked, with composing above the feed: the feed is endless and the box is
   small, so putting it second would bury it. The inbox follows composing down,
   which keeps the pair together. */
@media (max-width:900px){
  .msgcols{grid-template-columns:minmax(0,1fr)}
  .msgside{position:static;order:-1}
  .ilist{max-height:300px}
}

/* ---------- the accounts page ---------- */
/* Reached from the account menu, which is where the account it would change
   already lives. It is not in the nav: five links for a page one person on the
   node can open would be a nav that mostly says no. */
.acctmenu a.item{display:flex;align-items:center;gap:8px;width:100%;font-size:13px;
  color:var(--ink);padding:6px 8px;border-radius:4px;text-decoration:none}
.acctmenu a.item:hover{background:var(--panel-2);text-decoration:none}
.acctmenu a.item svg{color:var(--accent);flex:none}
/* The rule sits between what this account is and what it does: Admin is a place
   to go, Sign out ends the visit.
   width and display are restated, not inherited: the .sep in the app bar is a
   vertical 1px-by-18px inline-block, and overriding only its height and colour
   left this one 1px wide — a dot on a line box of its own, which is what the
   empty row between Admin and Sign out was. */
.acctmenu .sep{display:block;width:auto;height:1px;background:var(--line-2);margin:4px 0}

/* A password this node made up and could not email. It is loud on purpose:
   this is the only moment it exists anywhere readable, and an admin who
   navigates away has lost it. */
.adpw{display:flex;flex-direction:column;gap:6px;margin:0 0 14px;padding:12px 14px;
  border:1px solid var(--accent);border-left-width:3px;border-radius:6px;
  background:var(--accent-soft)}
.adpw .lt{font-family:var(--f-disp);font-weight:600;font-size:10.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--accent)}
.adpw code{font-family:var(--f-mono);font-size:19px;font-weight:500;color:var(--ink);
  letter-spacing:.06em;user-select:all}
.adpw .say{font-size:11.5px;color:var(--ink-2);line-height:1.45}

/* Requests waiting to be reviewed. Above the accounts and full width, because
   it is the only thing on this page that somebody is waiting on — the accounts
   are a list, this is a queue. */
.pend{border:1px solid var(--accent);border-radius:8px;background:var(--panel);
  overflow:hidden;margin-bottom:16px;box-shadow:var(--shadow)}
.phead{display:flex;align-items:center;gap:9px;padding:10px 14px;background:var(--accent-soft);
  border-bottom:1px solid color-mix(in srgb,var(--accent) 25%,transparent)}
.phead .lt{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--accent)}
.pcount{font-family:var(--f-mono);font-size:10px;font-weight:500;color:var(--panel);
  background:var(--accent);border-radius:9px;padding:1px 6px;min-width:18px;text-align:center}
.prow{display:grid;grid-template-columns:minmax(150px,1.1fr) minmax(160px,1.2fr) minmax(150px,1fr) auto;
  gap:8px 14px;align-items:center;padding:11px 14px;border-bottom:1px solid var(--line-2)}
.prow:last-of-type{border-bottom:0}
.prow.on{background:var(--accent-soft)}
.pwho{display:flex;flex-direction:column;gap:1px;min-width:0}
.pname{font-size:14px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.pcall{font-family:var(--f-mono);font-size:12.5px;color:var(--ink-2)}
.pmail{font-size:13px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pwhen{font-family:var(--f-mono);font-size:11.5px;color:var(--muted);line-height:1.35;
  font-variant-numeric:tabular-nums}
.pwhen .ip{display:block;color:var(--faint);font-size:10.5px}
.pact{display:flex;align-items:center;gap:7px;justify-self:end}
.btn.small{font-size:11px;padding:5px 11px}
.pfoot{padding:9px 14px;border-top:1px solid var(--line);background:var(--panel-2);
  font-size:12px;color:var(--muted)}
@media (max-width:760px){
  .prow{grid-template-columns:minmax(0,1fr);gap:6px}
  .pact{justify-self:start}
}

.adhead{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.adhead h2{font-size:24px}
.adhead .sub{font-size:13px;color:var(--muted)}
.adhead .counts{margin-left:auto;display:flex;gap:14px;font-family:var(--f-mono);
  font-size:11.5px;color:var(--muted)}
.adhead .counts b{color:var(--ink);font-weight:500}
.adnote{margin:0 0 14px;font-size:13px;border-radius:4px;padding:8px 11px}
.adnote.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--ok) 30%,transparent)}
.adnote.bad{color:var(--alert);background:var(--alert-soft);border:1px solid var(--alert-line)}
/* Narrower than the pages that carry a map or a chart: this one is a table and
   a form, and at 1360 the form's column stranded the table's. */
.adminpage{max-width:1240px}

/* One row above everything, answering "is the node all right?" without a
   scroll, and holding the counts that used to float to the far right of a
   section heading with nothing between them and it. */
.strip{display:flex;flex-wrap:wrap;border:1px solid var(--line);border-radius:8px;
  background:var(--panel);overflow:hidden;margin-bottom:16px}
.strip .cell{flex:1 1 148px;padding:9px 14px 10px;border-right:1px solid var(--line-2);min-width:0}
.strip .cell:last-child{border-right:0}
.strip dt{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);margin:0 0 2px}
.strip dd{margin:0;font-family:var(--f-mono);font-size:14px;color:var(--ink);
  font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.strip dd .u{font-family:var(--f-body);font-size:12px;color:var(--muted);margin-left:4px}
.strip dd.off{color:var(--alert)}

.adcols{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:20px;align-items:start}
/* The table and the tools under it are one column, so the left side is tall
   enough to stand beside the form rather than running out halfway down it. */
.leftcol{display:flex;flex-direction:column;gap:20px;min-width:0}
.tools{display:grid;grid-template-columns:repeat(auto-fit,minmax(292px,1fr));gap:20px;
  align-items:start}
/* A looked-up station has more to say than fits half the row. */
.tools .wide{grid-column:1/-1}

/* One row per account, and the row is the object: it is selected, edited and
   removed whole, so it carries the fill and the cells inside stay plain. */
.utab{border:1px solid var(--line);border-radius:8px;background:var(--panel);overflow:hidden}
.utabscroll{overflow-x:auto}
/* Sized so all six columns fit the space the side panel leaves, rather than
   relying on the horizontal scroll: a column scrolled off the right edge is a
   column nobody knows is there, and "last signed in" spent a release being one.
   The mins add up to 634 with the gaps and padding, which is the width the
   table box has at the breakpoint below. */
.utabgrid{min-width:634px}
.urow{display:grid;
  /* The email column was a fixed 104px, which is narrower than most addresses:
     daniel@redetzke.aero is about 150 at this size, so it ran under the role
     chip beside it. It gets a floor wide enough for an ordinary address and a
     share of what is left over. */
  grid-template-columns:minmax(104px,1.2fr) minmax(150px,1.5fr) 76px minmax(96px,.9fr) minmax(112px,1fr) 68px;
  align-items:center;gap:10px;padding:9px 12px;border-bottom:1px solid var(--line-2);
  border-left:2px solid transparent}
/* A grid item's min-width is auto, so a cell wider than its track overflows
   into the next one rather than shrinking. Every cell here can therefore
   collide with its neighbour; this is what stops that, whatever goes in one. */
.urow > *{min-width:0}
.urow:last-child{border-bottom:0}
.urow.on{background:var(--accent-soft);border-left-color:var(--accent)}
.urow .r{text-align:right}
.uhead{background:var(--panel-2);border-bottom:1px solid var(--line);padding-top:7px;
  padding-bottom:7px}
.uhead div{font-family:var(--f-disp);font-weight:600;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
/* The login on top, the person underneath: two facts about one account, and
   side by side they made the widest column on the page wider still. */
.uname{font-weight:600;font-size:14px;display:flex;flex-direction:column;
  align-items:flex-start;gap:1px;min-width:0;max-width:100%}
.uname .un{display:flex;align-items:center;gap:7px;min-width:0;max-width:100%}
.uname .real{font-size:11.5px;font-weight:400;color:var(--faint);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.uname .you{font-family:var(--f-disp);font-weight:600;font-size:9px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);border:1px solid var(--line);border-radius:3px;
  padding:0 4px;flex:none}
/* A send limit of the account's own. Shown as a chip beside the callsign rather
   than as a sixth column, because this table already loses columns before
   1100px — and only on the rows that carry one, so the chips are the exceptions
   rather than a column of identical numbers. */
.uname .lim{font-family:var(--f-mono);font-weight:500;font-size:10px;color:var(--warn);
  border:1px solid var(--warn);border-radius:3px;padding:0 4px;flex:none}
.uname .lim.off{color:var(--alert);border-color:var(--alert)}
/* An address longer than the column is cut with an ellipsis rather than left
   to run over the role chip. The full one is the cell's title, since a
   truncated address is not an address. */
.ucall{font-family:var(--f-mono);font-size:12.5px;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Role is a state rather than a word to read, so it is a chip: two of them are
   told apart down the column without anybody reading either. */
.role{display:inline-flex;align-items:center;gap:5px;font-family:var(--f-disp);font-weight:600;
  font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;border-radius:3px;padding:2px 6px;
  border:1px solid var(--line);color:var(--muted);background:var(--panel-2)}
.role.admin{color:var(--accent);border-color:color-mix(in srgb,var(--accent) 40%,transparent);
  background:var(--accent-soft)}
.role .dot{width:5px;height:5px;border-radius:50%;background:currentColor;flex:none}
.uwhen{font-family:var(--f-mono);font-size:11.5px;color:var(--muted);line-height:1.35;
  font-variant-numeric:tabular-nums;min-width:0}
/* The clock and the address under the date rather than beside it: one line of
   "2026-09-07 07:21z 127.0.0.1" is wider than any column this table can spare,
   and an IPv6 address is wider still. */
.uwhen .clock{display:block;color:var(--faint);font-size:10.5px;overflow-wrap:anywhere}
.uwhen.never{color:var(--faint)}
.uact{display:flex;justify-content:flex-end;gap:6px}
.iconbtn{border:1px solid var(--line);background:var(--panel-2);cursor:pointer;border-radius:4px;
  width:26px;height:26px;display:grid;place-items:center;color:var(--muted);text-decoration:none}
.iconbtn:hover{color:var(--ink);border-color:var(--faint);text-decoration:none}
.iconbtn.danger:hover{color:var(--alert);border-color:var(--alert)}
.iconbtn.off{opacity:.35;cursor:not-allowed}
.utabfoot{padding:9px 14px;border-top:1px solid var(--line);background:var(--panel-2);
  font-size:12px;color:var(--muted)}
.utabfoot code{font-family:var(--f-mono);font-size:11px;color:var(--ink-2)}
.uempty{padding:16px 14px;font-size:13px;color:var(--muted)}

/* The form sits beside the list rather than over it, the same choice composing
   made on /messages: you want to see the other accounts while you change one. */
.adside{position:sticky;top:16px;display:flex;flex-direction:column;gap:12px}
.card{border:1px solid var(--line);border-radius:8px;background:var(--panel);overflow:hidden}
.card .chead{display:flex;align-items:center;gap:9px;padding:11px 14px 0}
.card .chead .lt{font-family:var(--f-disp);font-weight:600;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-2)}
.card .chead .aux{margin-left:auto;font-size:11.5px;color:var(--faint)}
/* Scoped to this panel, all of it. `.cfld select` unscoped outranks `.ssid`
   and stretched the compose page's SSID dropdown to the full width of its row,
   squeezing the callsign beside it down to the padlock — the fields are shared
   with /messages, and only these rules are not. */
/* The hints carry the constraints, so every word stays; they are set smaller
   because together they were most of the form's height, and the form's height
   is what left the column beside it empty. */
.adside .cfld .hint{font-size:11px;color:var(--faint);line-height:1.35}
.adside .cfld select{appearance:none;font-family:var(--f-mono);font-size:13px;color:var(--ink);
  background:var(--panel-2);border:1px solid var(--line);border-radius:4px;padding:8px 10px;
  width:100%;min-width:0;cursor:pointer}
.adside .cfld select:focus{outline:2px solid var(--focus);outline-offset:-1px;
  border-color:var(--focus)}
.adside .cfld select:disabled{background:var(--sunk);color:var(--muted);cursor:not-allowed}
.adside .cfld input:disabled{background:var(--sunk);color:var(--muted);cursor:not-allowed}
.adside .cfld input.upper{text-transform:uppercase}
/* A count of messages is three characters wide. A full-width box for it invites
   a number nobody meant, and the spinner arrows sit closer to the digits. */
.adside .cfld input[type="number"]{width:112px;font-family:var(--f-mono)}
/* Cancel and Save sit together at the right, so the row's auto margin belongs
   to whatever is last rather than to every button in it. */
.adside .crow .btn{margin-left:0}
.adside .crow > :last-child{margin-left:auto}
.btn.ghost{background:none}
.btn.danger{border-color:var(--alert-line);color:var(--alert);background:var(--alert-soft)}
.btn.danger:hover{background:var(--alert);border-color:var(--alert);color:#fff}
/* Removing an account is the one destructive control here, so it is kept out of
   the form it would otherwise sit inside and asks before it acts. */
.dangerzone{border-top:1px solid var(--line-2);padding:11px 14px 13px;display:flex;
  align-items:center;gap:10px}
.dangerzone .lt{font-size:12.5px;color:var(--muted)}
.dangerzone .btn{margin-left:auto}
.confirm{padding:11px 14px 13px;border-top:1px solid var(--alert-line);background:var(--alert-soft)}
.confirm p{margin:0 0 9px;font-size:12.5px;color:var(--ink-2)}
.confirm p b{font-family:var(--f-mono);font-weight:500;color:var(--ink)}
.adhint{margin:0;font-size:12.5px;color:var(--warn);
  background:color-mix(in srgb,var(--warn) 8%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 30%,transparent);border-radius:4px;padding:8px 11px}

/* Stacked, with the form above the list, for the same reason composing is: the
   list is the long thing and a form under it is a form nobody scrolls to.
   1100px rather than 900: beside the 316px panel the table's box is the window
   less 336, so under about 1100 it is narrower than the table and the right of
   it goes off the edge. Giving it the full width is what stops that. */
@media (max-width:1100px){
  .adcols{grid-template-columns:minmax(0,1fr)}
  .adside{position:static;order:-1}
}

/* The Feed and Map tools are cards in the row under the table, not sections of
   their own: as full-width sections with a 760px card each, the right half of
   both was empty and the operational half of the page was below the fold. */
.adfeed .kv{border-top:1px solid var(--line)}
.adfeed .kv > div{padding:9px 14px 10px;min-width:0}
.adfeed .utabfoot{border-top:1px solid var(--line-2)}
/* There is no alert pill: the states a station can be in are live, stale and
   gone, and a feed that has stopped carrying traffic is none of those. */
.pill.bad{color:var(--alert);border-color:var(--alert-line);background:var(--alert-soft)}

/* The Map section: one station looked up, and the one thing to do to it. The
   card is as wide as that needs; across the whole page, a lookup field and a
   button read as a form nobody finished. */
.xlook{display:flex;align-items:flex-end;gap:10px;padding:11px 14px 14px}
.xlook .cfld{flex:1;max-width:240px}
.admap .cfld input.upper{text-transform:uppercase}
.xst{display:flex;gap:11px;align-items:flex-start;padding:14px;background:var(--panel-2);
  border-top:1px solid var(--line)}
.xst .desc{font-size:13.5px;color:var(--muted);line-height:1.35;margin-top:2px;overflow-wrap:anywhere}
.admap .kv{border-block:1px solid var(--line-2)}
.admap .kv > div{padding:9px 14px 10px;min-width:0}
.admap .kv dd{overflow-wrap:anywhere}
.xact{display:flex;gap:14px;align-items:center;flex-wrap:wrap;padding:12px 14px 14px}
.xact p{margin:0;flex:1 1 280px;font-size:12.5px;color:var(--muted);line-height:1.45}
.xact form{margin-left:auto}
@media (max-width:640px){
  .admap .kv{grid-template-columns:minmax(0,1fr)}
  .xlook .cfld{max-width:none}
}
