:root {
  --bg: #17130f;
  --bg-2: #201a15;
  --panel: #221c16;
  --panel-2: #2b241d;
  --line: #46392e;
  --line-2: #5c4a3b;
  --fg: #ece2d4;
  --muted: #9c8a76;
  --accent: #c58a3e; /* muted brass — replaces the old rust-orange */
  --link: #d1a262;
  --ok: #6fae5e;
  --warn: #d1a24a;
  --bad: #c85c4c;
  --radius: 2px;
  --mono: ui-monospace, "SFMono-Regular", "Cascadia Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color-scheme: dark;
  font-synthesis-weight: none;
}
* {
  box-sizing: border-box;
}
/* the [hidden] attr must beat any element's own display rule (e.g. the map
   popover's display:flex) */
[hidden] {
  display: none !important;
}
html,
body {
  margin: 0;
  padding: 0;
}
body {
  background: var(--bg);
  color: var(--fg);
  font: 14px / 1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
}
a {
  color: var(--link);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
code,
.mono {
  font-family: var(--mono);
}
code {
  background: var(--bg-2);
  border: 1px solid var(--line);
  padding: 0 4px;
  font-size: .88em;
}
h1 {
  font-size: 1.15rem;
  margin: 0 0 .15em;
  letter-spacing: .01em;
}
h2 {
  font-size: .78rem;
  margin: 0 0 .6em;
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--muted);
}
h3 {
  font-size: .72rem;
  margin: 1.1em 0 .5em;
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--muted);
}
.muted {
  color: var(--muted);
}
.hint {
  color: var(--muted);
  font-size: .88em;
}
.err {
  color: var(--bad);
  background: #331c18;
  border: 1px solid #5a2f28;
  padding: .5em .7em;
}

/* ---- app shell ---- */
.appshell {
  display: flex;
  min-height: 100vh;
}
.sidebar {
  width: 188px;
  flex: none;
  background: var(--bg-2);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}
.brand {
  display: block;
  padding: .9rem 1rem .8rem;
  color: var(--fg);
  font-weight: 700;
  font-family: var(--mono);
  letter-spacing: .04em;
  border-bottom: 1px solid var(--line);
}
.brand span {
  color: var(--accent);
}
.mainnav {
  display: flex;
  flex-direction: column;
  padding: .4rem 0;
  flex: 1;
}
.mainnav a {
  padding: .5rem 1rem;
  color: var(--muted);
  font-size: .82rem;
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: .08em;
  border-left: 2px solid transparent;
}
.mainnav a:hover {
  color: var(--fg);
  text-decoration: none;
  background: var(--panel);
}
.mainnav a.active {
  color: var(--fg);
  background: var(--panel);
  border-left-color: var(--accent);
}
.sidefoot {
  border-top: 1px solid var(--line);
  padding: .7rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .4rem;
  font-size: .8rem;
}
.who {
  color: var(--muted);
  font-family: var(--mono);
}
.inline {
  display: inline;
}
.linkbtn {
  background: none;
  border: none;
  color: var(--link);
  cursor: pointer;
  font: inherit;
  padding: 0;
  text-align: left;
}

.content {
  flex: 1;
  min-width: 0;
  max-width: 1080px;
  margin: 0 auto;
  padding: 1.1rem 1.4rem 4rem;
}
.content.auth {
  display: grid;
  place-items: center;
  min-height: 100vh;
}

@media (max-width: 780px) {
  .appshell {
    flex-direction: column;
  }
  .sidebar {
    width: auto;
    height: auto;
    position: static;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
  }
  .brand {
    border-bottom: none;
    border-right: 1px solid var(--line);
    padding: .6rem .9rem;
  }
  .mainnav {
    flex-direction: row;
    flex-wrap: wrap;
    padding: .2rem;
  }
  .mainnav a {
    border-left: none;
    border-bottom: 2px solid transparent;
    padding: .4rem .6rem;
  }
  .mainnav a.active {
    border-left: none;
    border-bottom-color: var(--accent);
  }
  .sidefoot {
    border-top: none;
    flex-direction: row;
    align-items: center;
    margin-left: auto;
  }
}

/* ---- primitives ---- */
.page-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  padding-bottom: .6rem;
  border-bottom: 1px solid var(--line);
}
.page-head .sub {
  color: var(--muted);
  margin: 0;
  font-size: .85em;
}

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .85rem .9rem;
  margin-bottom: .9rem;
}
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: .6rem;
  padding-bottom: .4rem;
  border-bottom: 1px solid var(--line);
}
.card-head h2,
.card-head h3 {
  margin: 0;
}
.grid {
  display: grid;
  gap: .9rem;
}
.grid-2 {
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 820px) {
  .grid-2 {
    grid-template-columns: 1fr;
  }
}

.row {
  display: flex;
  gap: .5rem;
  align-items: center;
  flex-wrap: wrap;
}
.row.between {
  justify-content: space-between;
}
.stack {
  display: flex;
  flex-direction: column;
  gap: .55rem;
}

input,
select,
button,
.btn {
  font: inherit;
  color: var(--fg);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .4rem .55rem;
}
input:focus,
select:focus {
  outline: none;
  border-color: var(--accent);
}
button,
.btn {
  cursor: pointer;
  background: var(--panel-2);
}
button:hover,
.btn:hover {
  border-color: var(--line-2);
  text-decoration: none;
}
button[type="submit"],
.btn-primary {
  border-color: var(--accent);
  color: var(--accent);
  background: transparent;
}
button[type="submit"]:hover,
.btn-primary:hover {
  background: rgba(197, 138, 62, 0.12);
}
.btn.sm {
  padding: .2rem .45rem;
  font-size: .82em;
}
.btn.danger,
button.danger {
  color: var(--bad);
  border-color: #5a3029;
  background: transparent;
}
.btn.danger:hover {
  background: rgba(200, 92, 76, 0.12);
}
.iconbtn {
  background: var(--bg-2);
  border: 1px solid var(--line);
  color: var(--muted);
  padding: .15rem .4rem;
}
.iconbtn:hover {
  color: var(--fg);
}

.statgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .3rem .8rem;
}
.statgrid > div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px dotted var(--line);
  padding: .2rem 0;
  font-family: var(--mono);
  font-size: .85em;
}
.statgrid .k {
  color: var(--muted);
}

.conn {
  font-family: var(--mono);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  padding: .1em .45em;
  border: 1px solid var(--line);
  color: var(--muted);
  border-radius: var(--radius);
}
.conn-connected {
  color: var(--ok);
  border-color: var(--ok);
}
.conn-connecting {
  color: var(--warn);
  border-color: var(--warn);
}
.conn-disconnected,
.conn-error {
  color: var(--bad);
  border-color: var(--bad);
}
.conn-locked {
  color: var(--warn);
  border-color: var(--warn);
}

/* ---- chat ---- */
.chat-log {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  max-height: 320px;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: .3rem;
  font-size: .9em;
}
.chat-log.tall {
  max-height: 64vh;
}
#chat-form {
  margin-top: 1rem;
}
#chat-form input[name="message"] {
  flex: 1;
  min-width: 0;
}
.msg {
  display: flex;
  flex-direction: column;
  gap: .05rem;
  padding: .2rem .1rem;
  border-left: 2px solid transparent;
  padding-left: .5rem;
}
.msg-meta {
  display: flex;
  gap: .5rem;
  align-items: baseline;
}
.msg-name {
  color: var(--fg);
  font-weight: 600;
  font-size: .88em;
}
.msg-time {
  color: var(--muted);
  font-size: .72em;
  font-family: var(--mono);
  opacity: 0.7;
}
.msg-text {
  color: var(--fg);
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.msg-alarm {
  border-left-color: var(--warn);
  background: rgba(209, 162, 74, 0.08);
}
.msg-alarm .msg-name {
  color: var(--warn);
  font-family: var(--mono);
  text-transform: lowercase;
}
.msg-alarm .msg-text {
  color: var(--muted);
  -webkit-line-clamp: 2;
  font-size: .92em;
}

/* ---- alarms ---- */
.alarmlist {
  list-style: none;
  margin: 0;
  padding: 0 .3rem 0 0;
  max-height: 46vh;
  overflow-y: auto;
}
.alarmlist li {
  display: flex;
  gap: .55rem;
  align-items: baseline;
  padding: .28rem 0;
  border-bottom: 1px solid var(--line);
  font-size: .9em;
}
.alarmlist .al-title {
  font-weight: 600;
}
.alarmlist .al-msg {
  flex: 1;
  min-width: 0;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.alarmlist .al-time {
  color: var(--muted);
  font-size: .78em;
  font-family: var(--mono);
}
.alarmlist .sg-del {
  align-self: center;
  flex: none;
  font-size: .8em;
  line-height: 1;
  opacity: 0.45;
}
.alarmlist .sg-del:hover {
  opacity: 1;
  color: var(--bad);
}

/* ---- devices ---- */
.group-title {
  margin: 1.2rem 0 .5rem;
}
.group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.group-actions {
  display: flex;
  gap: .35rem;
}
.btn.xs {
  padding: .12rem .4rem;
  font-size: .74rem;
  font-family: var(--mono);
}
.group-filter {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin: .2rem 0 1rem;
}
.group-filter .gf {
  background: var(--bg-2);
  border: 1px solid var(--line);
  color: var(--muted);
  font-family: var(--mono);
  font-size: .78rem;
  padding: .2rem .55rem;
  border-radius: var(--radius);
  cursor: pointer;
}
.group-filter .gf.active {
  color: var(--fg);
  border-color: var(--accent);
}
.dev-group[hidden] {
  display: none;
}
select.dev-group {
  font: inherit;
  font-size: .72rem;
  font-family: var(--mono);
  color: var(--muted);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .1rem .2rem;
  max-width: 100%;
  margin-top: -.2rem;
}
.devgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
  gap: .7rem;
  margin-bottom: 1.2rem;
  grid-auto-rows: 186px;
  grid-auto-flow: row dense;
}
/* storage monitors get a 2-row-tall card (2x1) for the item list */
.devcard-wrap.t3 {
  grid-row: span 2;
}
.devcard-wrap {
  min-width: 0;
  height: 100%;
}
/* every card the same box: cells define the height, foot pinned to the bottom */
.devcard {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .7rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  height: 100%;
}
.devcard.on {
  border-color: var(--ok);
}
/* decaying tool cupboard / firing alarm — dotted attention ring */
.devcard.uk-crit,
.devcard.alarm-crit {
  outline: 2px dotted var(--bad);
  outline-offset: 2px;
}
.devcard.uk-warn {
  outline: 2px dotted var(--warn);
  outline-offset: 2px;
}
.devcard-body {
  flex: 1;
  align-items: center;
  overflow: hidden;
}
.devcard-body.storage {
  align-items: stretch;
}
#flows {
  margin-top: .4rem;
  clear: both;
}
.devcard-top {
  display: flex;
  justify-content: space-between;
  gap: .5rem;
  align-items: start;
}
.devcard-actions {
  display: flex;
  gap: .2rem;
  flex: none;
}
.devcard-title {
  display: flex;
  flex-direction: column;
  gap: .12rem;
}
.devcard-title strong {
  font-size: .95em;
}
.tag {
  font-family: var(--mono);
  font-size: .62rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .05em .4em;
  width: fit-content;
}
.devcard-body {
  display: flex;
  justify-content: center;
  padding: .3rem 0;
}
/* even, two-way split switch: ON | OFF, each half the width, whichever is
   active gets filled in — no ambiguity about which state you're looking at */
.segbtn {
  display: flex;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.seg {
  flex: 1 1 50%;
  border: none;
  border-radius: 0;
  background: var(--bg-2);
  color: var(--muted);
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .08em;
  padding: .5rem 0;
}
.seg.on {
  border-right: 1px solid var(--line);
}
.seg:hover {
  color: var(--fg);
}
.seg.on.active {
  background: #223a20;
  color: var(--ok);
}
.seg.off.active {
  background: #3a2320;
  color: var(--bad);
}
.alarm-state {
  padding: .25rem .6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  font-family: var(--mono);
  font-size: .82em;
}
.alarm-state.active {
  color: #fff;
  background: var(--bad);
  border-color: var(--bad);
}
.storage {
  flex-direction: column;
  gap: .3rem;
  width: 100%;
  min-height: 0;
  font-family: var(--mono);
  font-size: .8em;
}
.storage .st-head {
  display: flex;
  gap: 1rem;
  flex: none;
  color: var(--fg);
}
.storage .st-head .k {
  color: var(--muted);
  margin-right: .25em;
}
.itemlist {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}
.itemlist li {
  display: flex;
  justify-content: space-between;
  gap: .5rem;
  padding: .12rem 0;
}
.itemlist .it-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.itemlist .it-qty {
  flex: none;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.devcard-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .5rem;
  font-size: .78em;
}
.devcard-foot .muted {
  font-family: var(--mono);
}
/* click-to-rename */
.dev-name .dn-text {
  cursor: text;
  border-bottom: 1px dotted transparent;
}
.dev-name:hover .dn-text {
  border-bottom-color: var(--line-2);
}
.dn-edit {
  font: inherit;
  width: 12rem;
  max-width: 100%;
  padding: .1rem .3rem;
}

/* drag to reorder */
.drag-item.dragging {
  opacity: 0.4;
}
.grip {
  color: var(--muted);
  cursor: grab;
  font-size: .95em;
  margin-right: .2em;
  user-select: none;
}
.grip:active {
  cursor: grabbing;
}

/* ---- flows ---- */
.flowlist {
  list-style: none;
  margin: 0 0 .8rem;
  padding: 0;
}
.flow-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--line);
  font-size: .88em;
}
.flow-row .fl-name {
  font-weight: 600;
  flex: none;
  min-width: 7rem;
}
.flow-row .fl-sum {
  flex: 1;
  color: var(--muted);
  font-family: var(--mono);
  font-size: .92em;
}
.flow-row.off {
  opacity: 0.5;
}
.flow-row.editing {
  display: block;
  padding: .6rem 0;
}
.flow-row:not(.editing) {
  flex-wrap: wrap;
}
.flow-row.editing .flow-form {
  margin-top: .4rem;
}

/* ---- rules: read-only chip row + run history ---- */
.chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  flex-basis: 100%;
}
.chip {
  font-family: var(--mono);
  font-size: .82em;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .1rem .45rem;
}
.chip b {
  color: var(--fg);
  font-weight: 600;
}
.chip.when {
  border-left: 2px solid var(--accent);
}
.chip.if {
  border-left: 2px solid var(--warn);
}
.chip.do {
  border-left: 2px solid var(--ok);
}
.rl-meta {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-basis: 100%;
}
.rl-meta .fl-name {
  font-weight: 600;
}
.rl-meta .row {
  margin-left: auto;
}
.rl-runs {
  list-style: none;
  margin: .3rem 0 0;
  padding: 0;
  flex-basis: 100%;
  font-family: var(--mono);
  font-size: .78em;
  color: var(--muted);
}
.rl-runs .run-failed b {
  color: var(--bad);
}
.rl-runs .run-skipped b {
  color: var(--warn);
}
.rl-runs .run-ok b {
  color: var(--ok);
}
.cond-set {
  grid-column: 1 / -1;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .5rem .6rem;
}
.cond-set legend {
  font-size: .78em;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}
.cond-list {
  list-style: none;
  margin: 0 0 .4rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .35rem;
}
.cond-row {
  display: flex;
  gap: .35rem;
  align-items: center;
}
.cond-row select,
.cond-row input {
  min-width: 0;
}
.cond-row .cond-val {
  width: 5rem;
}
.cond-row [hidden] {
  display: none;
}
.flow-form label.chk {
  flex-direction: row;
  align-items: center;
  gap: .4rem;
  text-transform: none;
  letter-spacing: 0;
}
.flow-form .flow-when-act[hidden],
.flow-form .flow-when-revert[hidden] {
  display: none;
}
.flow-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .5rem .7rem;
  align-items: end;
}
.flow-form label {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  font-size: .78em;
  color: var(--muted);
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.flow-form .flow-when[hidden] {
  display: none;
}
.flow-form .flow-actions {
  grid-column: 1 / -1;
}
.flow-add {
  margin-top: .4rem;
}
#flow-add-panel {
  margin-top: .7rem;
  border-top: 1px solid var(--line);
  padding-top: .8rem;
}

/* ---- cameras ---- */
.camgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: .8rem;
}
.camtile .cam-thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border: 1px solid var(--line);
  overflow: hidden;
  margin-bottom: .5rem;
}
/* a never-opened camera has no still yet: show a solid black 16:9 box with a
   faint camera glyph, not a collapsed sliver */
.camtile .cam-thumb::before {
  content: "";
  position: absolute;
  inset: 0;
  background: #000 center / 34px no-repeat
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239c8a76' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M23 7l-7 5 7 5V7z'/><rect x='1' y='5' width='15' height='14' rx='2' ry='2'/></svg>");
  opacity: 0.4;
}
.camtile .cam-thumb img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cam-noframe,
.cam-snap:not([src]) {
  opacity: 0;
}
/* team-in-game lock on the camera grid */
.cam-lock {
  display: none;
}
.cams-locked .cam-thumb img {
  opacity: 0.1;
  filter: grayscale(1);
}
.cams-locked .cam-lock {
  display: grid;
  place-items: center;
  gap: .2rem;
  position: absolute;
  inset: 0;
  font-family: var(--mono);
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--warn);
  background: rgba(0, 0, 0, 0.45);
}
.cams-locked .cam-lock::before {
  content: "\1F512";
  font-size: 1.2rem;
}
.cam-stage {
  display: flex;
  flex-direction: column;
  gap: .8rem;
  min-width: 0;
}
.cam-frame {
  position: relative;
  background-color: #000;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 16 / 9;
  max-height: 72vh;
}
/* desktop: the open feed fills the viewport — the video letterboxes inside it */
@media (min-width: 821px) {
  .cam-layout {
    height: calc(100vh - 10.5rem);
    min-height: 440px;
  }
  .cam-stage {
    height: 100%;
  }
  .cam-frame {
    aspect-ratio: auto;
    max-height: none;
    flex: 1 1 auto;
    min-height: 0;
  }
}
.cam-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
/* the feed starts transparent so the cached still (as the frame's background)
   shows immediately; the first live frame fades in over it */
#cam-video {
  opacity: 0;
  transition: opacity .15s linear;
}
#cam-video.on {
  opacity: 1;
}
.cam-frame.locked {
  outline: 1px solid var(--accent);
  cursor: none;
}
.cam-overlay {
  position: absolute;
  inset: 0;
  display: none;
}
.cam-locked-out .cam-frame img {
  opacity: 0.12;
  filter: grayscale(1);
}
.cam-locked-out .cam-overlay,
.cam-overlay.show {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.4rem;
  text-align: center;
  font-weight: 600;
  line-height: 1.45;
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
  font-family: var(--mono);
  font-size: .9em;
}
.cam-status-line {
  font-size: .88em;
  font-family: var(--mono);
}
/* one status style for every state: coloured dot + lowercase label, and the
   pill (.cam-ovl-status .cam-status-line) is always the box around it */
.cam-stat {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  text-transform: lowercase;
  letter-spacing: .04em;
}
.cam-stat::before {
  content: "";
  flex: none;
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  background: currentColor;
}
.cam-stat-ok {
  color: var(--ok);
}
.cam-stat-warn {
  color: var(--warn);
}
.cam-stat-bad {
  color: var(--bad);
}
.cam-stat-idle {
  color: var(--muted);
}
.cam-stat-fire {
  color: var(--bad);
  font-weight: 700;
}
.cam-stat-fire::before {
  animation: cam-fire-blink .38s steps(1) infinite;
}
@keyframes cam-fire-blink {
  50% {
    opacity: 0.2;
  }
}
/* --- overlays sitting directly on the feed (name TL, status TR, actions BL,
      fullscreen BR) — no chrome above or below the video --- */
.cam-ovl {
  position: absolute;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .22rem .55rem;
  border-radius: 999px;
  font-size: .78em;
  line-height: 1.3;
  color: #fff;
  background: rgba(0, 0, 0, 0.58);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.cam-ovl-name {
  top: .6rem;
  left: .6rem;
  max-width: 62%;
  gap: .5rem;
  padding: .3rem .7rem;
  font-size: 1rem;
  align-items: baseline; /* name + kind sit on the same baseline */
  cursor: text;
}
.cam-ovl-name .nm {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cam-ovl-name .k {
  flex: none;
  color: rgba(255, 255, 255, 0.65);
  text-transform: uppercase;
  letter-spacing: .07em;
  font-size: .8rem;
}
/* the status partial swaps its own markup in — keep this element chrome-less */
.cam-ovl-status {
  top: .55rem;
  right: .55rem;
  padding: 0;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  pointer-events: none;
}
.cam-ovl-status:empty {
  display: none;
}
.cam-ovl-status .cam-status-line {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .22rem .55rem;
  border-radius: 999px;
  font-size: 1em;
  background: rgba(0, 0, 0, 0.58);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.cam-ovl-actions {
  bottom: .55rem;
  left: .55rem;
  padding: 0;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  gap: .4rem;
}
/* both kinds' controls are rendered so an in-place camera switch needs no
   refetch — show only the set that fits the current camera */
#cam-stage[data-cam-kind="cctv"] .turret-only,
#cam-stage[data-cam-kind="turret"] .cctv-only {
  display: none;
}
.cam-ovl-actions .cam-ctl-health:not([hidden]) {
  padding: .22rem .55rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.58);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.cam-ovlbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .34rem .6rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius);
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: .82em;
  line-height: 1;
  cursor: pointer;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.cam-ovlbtn:hover {
  background: rgba(0, 0, 0, 0.8);
  border-color: rgba(255, 255, 255, 0.5);
}
.cam-fsbtn {
  position: absolute;
  z-index: 3;
  right: .55rem;
  bottom: .55rem;
  padding: .38rem .44rem;
}
/* during pointer-lock the mouse is the camera — hide the clickable furniture,
   but keep the name + status labels up so you always know what you're steering */
.cam-frame.locked .cam-ovl-actions,
.cam-frame.locked .cam-fsbtn {
  opacity: 0;
  pointer-events: none;
}
.cam-frame:fullscreen {
  aspect-ratio: auto;
  max-height: none;
  border: 0;
  border-radius: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cam-frame:fullscreen img {
  width: 100%;
  height: 100%;
}
/* camera removed / unreachable — shown instead of a dead feed */
.cam-frame-gone {
  display: grid;
  place-items: center;
}
.cam-gone {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  padding: 1.5rem;
  text-align: center;
  color: var(--muted);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.9);
}
.cam-gone svg {
  color: var(--warn);
}
.cam-gone-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--fg);
}
.cam-gone-sub {
  margin: 0;
  font-size: .85rem;
  max-width: 32ch;
}
/* TV static — shown behind the "not found" card, and over a feed whose camera
   vanished mid-view */
/* TV static — a <canvas> drawn per-pixel by app.js at the camera's own
   resolution, stretched to the frame so the grain matches the feed's pixels */
.cam-static {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0.55;
  image-rendering: pixelated;
}
/* the open feed's camera vanished mid-view: grey the last frame, show static */
.cam-feed-gone .cam-frame img {
  opacity: 0.1;
  filter: grayscale(1);
}
.cam-feed-gone .cam-static {
  display: block !important;
}
/* first-frame spinner: frames take ~10 ray broadcasts to appear */
.cam-spinner {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: .85em;
  pointer-events: none;
}
.cam-spinner .spin {
  width: 14px;
  height: 14px;
  border: 2px solid var(--line-2);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: cam-spin 0.8s linear infinite;
}
@keyframes cam-spin {
  to {
    transform: rotate(360deg);
  }
}
.cam-ctl-health {
  font-family: var(--mono);
  font-size: .82em;
  color: var(--warn);
}
.cam-dpad {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  margin-top: .3rem;
}
.cam-dpad .dpad-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: .3rem;
}
.cam-dpad .btn.dp {
  padding: .6rem 0;
  touch-action: none;
  user-select: none;
}
#cam-viewer[hidden] {
  display: none;
}
/* Cameras page: feed on the left, a vertical camera rail on the right that
   scrolls on its own, then sightings full-width below both. */
.cam-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 190px;
  gap: 1rem;
  align-items: stretch;
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}
/* rail height tracks the feed; it scrolls through the cameras on its own */
.camgrid.cam-rail {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  overflow-x: hidden;
  padding: .1rem .5rem .1rem .1rem;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}
/* when there's more than fits: keep a visible track + fade the clipped ends so
   it's obvious the list scrolls (JS toggles .is-scrollable) */
.camgrid.cam-rail.is-scrollable {
  overflow-y: scroll;
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 16px,
    #000 calc(100% - 16px),
    transparent 100%
  );
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 16px,
    #000 calc(100% - 16px),
    transparent 100%
  );
}
.camgrid.cam-rail::-webkit-scrollbar {
  width: 9px;
}
.camgrid.cam-rail::-webkit-scrollbar-track {
  background: transparent;
}
.camgrid.cam-rail::-webkit-scrollbar-thumb {
  background: var(--line-2);
  border-radius: 5px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.camgrid.cam-rail.is-scrollable::-webkit-scrollbar-thumb {
  background: var(--muted);
  background-clip: padding-box;
}
.camgrid.cam-rail::-webkit-scrollbar-thumb:hover {
  background: var(--fg);
  background-clip: padding-box;
}
.cam-rail .camtile {
  padding: .4rem;
}
.cam-rail .cam-thumb {
  margin: 0;
}
.cam-rail .camtile-foot {
  display: flex;
  align-items: center;
  gap: .35rem;
  position: relative;
  margin-top: .35rem;
  font-size: .82em;
}
.cam-rail .camtile-foot .cam-tname {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cam-rail .camtile-foot .grip {
  flex: none;
  font-size: .9em;
}
.cam-rail .cam-opts-btn {
  flex: none;
}
.camtile.cam-active {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.camtile.cam-active .cam-tname {
  color: var(--accent);
}
/* corner badges on a rail thumbnail */
.cam-badge {
  position: absolute;
  top: .25rem;
  z-index: 2;
  display: inline-grid;
  place-items: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 .2rem;
  border-radius: 999px;
  font-size: .72rem;
  line-height: 1;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.cam-badge-star {
  left: .25rem;
  color: var(--accent);
}
.cam-badge-watch {
  right: .25rem;
  color: var(--warn);
}
@media (max-width: 820px) {
  .cam-layout {
    grid-template-columns: 1fr;
  }
  .camgrid.cam-rail {
    position: static;
    max-height: none;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 0 0 .4rem;
  }
  .cam-rail .camtile {
    flex: 0 0 150px;
  }
}
.deobf,
.wd-toggle {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: .85em;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.deobf input,
.wd-toggle input {
  margin: 0;
  padding: 0;
  cursor: pointer;
}
.watch {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .78em;
  color: var(--muted);
  font-family: var(--mono);
  cursor: pointer;
  user-select: none;
}
.watch input {
  margin: 0;
  cursor: pointer;
}
.watch-toggle:checked + span,
.watch:has(.watch-toggle:checked) {
  color: var(--warn);
}
.sg-move {
  color: var(--warn);
  font-family: var(--mono);
  font-size: .92em;
  white-space: nowrap;
}

/* ---- dashboard ---- */
.serverline {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: baseline;
  color: var(--muted);
  font-family: var(--mono);
  font-size: .85rem;
}
.serverline .sl-name {
  color: var(--fg);
  font-weight: 600;
}
.serverline .sl-sep {
  opacity: 0.4;
}
.upkeeplist {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--mono);
  font-size: .88em;
}
.upkeeplist li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: .3rem 0;
  border-bottom: 1px solid var(--line);
}
.upkeeplist .uk-time.low {
  color: var(--bad);
  font-weight: 600;
}

/* ---- tables ---- */
.tbl {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: .84em;
}
.tbl th,
.tbl td {
  text-align: left;
  padding: .35rem .5rem;
  border-bottom: 1px solid var(--line);
}
.tbl th {
  color: var(--muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  font-size: .9em;
}
.tbl tr:nth-child(even) td {
  background: rgba(255, 255, 255, 0.015);
}
.tbl th.num,
.tbl td.num {
  text-align: right;
  white-space: nowrap;
}

/* ---- vending search ---- */
.vending-filters {
  gap: .5rem;
  margin-bottom: .7rem;
}
.vending-filters input[name="q"] {
  flex: 1;
  min-width: 8rem;
}
.vending-head {
  margin-bottom: .5rem;
}
.tbl-wrap {
  overflow-x: auto;
}
.vending-tbl .v-item {
  font-family: var(--sans, inherit);
}
.vending-tbl .v-where {
  white-space: nowrap;
}
.grid-badge {
  font-family: var(--mono);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0 .3rem;
}

/* settings: notification sink status */
b.ok {
  color: var(--ok);
}
b.off {
  color: var(--muted);
}

.vending-hist {
  margin-top: .9rem;
  padding-top: .6rem;
  border-top: 1px solid var(--line);
}
.vending-hist .card-head h3 {
  margin: 0;
}
.vending-watch-add {
  gap: .5rem;
  margin: .6rem 0;
}
.vending-watch-add input[name='item_name'] {
  flex: 1;
  min-width: 8rem;
}
.vending-or {
  color: var(--muted);
  font-size: .8em;
  align-self: center;
}
.vending-filters input:disabled,
.vending-watch-add input:disabled {
  opacity: .4;
}

/* settings: discord links */
.dlink-list {
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.dlink-row {
  display: grid;
  grid-template-columns: minmax(9rem, 1fr) minmax(9rem, 1.2fr) minmax(6rem, .8fr) auto auto;
  gap: .4rem;
  align-items: center;
}
.dlink-row .dlink-who {
  font-size: .85em;
  line-height: 1.2;
}
.dlink-row .mono {
  font-family: var(--mono);
  font-size: .82em;
}
.dlink-add {
  margin-top: .6rem;
  border-top: 1px solid var(--line);
  padding-top: .6rem;
}
@media (max-width: 640px) {
  .dlink-row {
    grid-template-columns: 1fr 1fr;
  }
}
.steps {
  line-height: 1.9;
}

/* ---- login ---- */
.login-card {
  width: min(340px, 92vw);
}
.login-card h1 {
  text-align: center;
  font-family: var(--mono);
  letter-spacing: .04em;
}
.login-card .hint {
  text-align: center;
  margin-bottom: 1rem;
}
.login-form {
  gap: .8rem;
}
.login-form label {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  font-size: .8rem;
  color: var(--muted);
}
.login-form input {
  width: 100%;
  padding: .55rem .6rem;
}
.login-form button {
  margin-top: .2rem;
  padding: .55rem 0;
}
.chk {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .9em;
  color: var(--muted);
}

/* ---- toasts ---- */
#toasts {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  z-index: 50;
}
.toast {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  padding: .55rem .85rem;
  max-width: 320px;
  font-size: .88em;
  animation: slidein .18s ease;
}
.toast.alarm {
  border-left-color: var(--bad);
}
.toast.ok {
  border-left-color: var(--ok);
}
@keyframes slidein {
  from {
    transform: translateX(16px);
    opacity: 0;
  }
}

.cr-name-edit {
  width: 100%;
  min-width: 7rem;
  font: inherit;
  padding: .1rem .25rem;
}
/* rename box on the feed name overlay: keep it legible on the dark pill */
.cam-ovl-name .cr-name-edit {
  color: #fff;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.4);
  font-weight: 700;
}

.cam-add.row {
  gap: .4rem;
  flex: 1 1 22rem;
  max-width: 26rem;
}
.cam-add.row input {
  flex: 1;
  min-width: 0;
}
/* per-tile options menu — JS positions it fixed at the ⋯ button so the
   rail's own scroll clipping can't swallow it */
.cam-opts-btn {
  line-height: 1;
  padding: .05rem .35rem;
  font-size: 1rem;
}
.cam-menu {
  position: fixed;
  z-index: 60;
  display: flex;
  flex-direction: column;
  min-width: 9.5rem;
  background: var(--panel);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.cam-menu button {
  border: none;
  border-radius: 0;
  background: none;
  text-align: left;
  padding: .4rem .6rem;
  font-size: .84em;
}
.cam-menu button:hover {
  background: var(--panel-2);
}
.cam-menu button.danger {
  color: var(--bad);
  border-top: 1px solid var(--line);
}

/* ---- device-card trust states: pending / failed / stale / disconnected ---- */
.devcard.pending {
  border-color: var(--warn);
}
.devcard.pending .segbtn {
  opacity: 0.6;
  animation: pend-pulse 1.1s ease-in-out infinite;
}
@keyframes pend-pulse {
  50% {
    opacity: 0.3;
  }
}
.devcard.pending-failed {
  border-color: var(--bad);
}
.pend-note {
  font-family: var(--mono);
  color: var(--warn);
}
.pend-note.fail {
  color: var(--bad);
}
.pend-note .linkbtn {
  font-size: inherit;
}
.devcard.stale {
  border-style: dashed;
}
.devcard-foot .stale-age {
  color: var(--warn);
}
/* Rust+ socket down: cards are last-known, not live — dim them and lock controls. */
body.rp-disconnected .devcard {
  filter: grayscale(0.7) opacity(0.7);
}
body.rp-disconnected .devcard .seg,
body.rp-disconnected .devcard .iconbtn {
  pointer-events: none;
}
/* removing a device from the dashboard is a local op — allowed while offline */
body.rp-disconnected .devcard .iconbtn.danger {
  pointer-events: auto;
}
body.sse-down .conn.conn-connecting {
  opacity: 0.8;
}
