/* A drawing in a window of its own: it can be zoomed and moved (components/pan-zoom.js).
   Everything in it is SVG; sizes are in the drawing's own units and grow with the zoom. */

.graph-view {
  display: block;
  width: 100%;
  /* Until the script has measured the window; then the drawing fills what is left of it. */
  height: 26rem;
  background: var(--surface-sunken);
  border-radius: 0 0 var(--radius) var(--radius);
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

.graph-view.dragging {
  cursor: grabbing;
}

.graph-view:focus-visible {
  outline-offset: -2px;
}

/* The page of a drawing on show gives it all the room of the window. */
.content:has(.panel:not([hidden]) > .graph-view) {
  padding-bottom: 1.25rem;
}

.panel:has(> .graph-view) {
  margin-bottom: 0;
}

/* The remark on the left and all the buttons in one row to its right, so that the drawing
   keeps the height. In a narrow window the buttons go below the remark. */
.panel-head:has(+ .graph-view) {
  align-items: flex-start;
  padding-top: 0.45rem;
  padding-bottom: 0.45rem;
}

.panel-head:has(+ .graph-view) > button {
  flex: none;
}

/* The remark keeps a width it can be read in and takes what the buttons leave. */
.panel-head:has(+ .graph-view) > .muted {
  flex: 1 1 18rem;
  min-width: 14rem;
}

.graph-controls {
  display: flex;
  flex: 0 1 auto;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.4rem;
}

/* What the drawing shows live, said in a corner of it: when the traffic was read and what
   the closest points stand for. It lies over the drawing and takes no click from it. */
.panel:has(> .graph-view) {
  position: relative;
}

.graph-legend {
  position: absolute;
  bottom: 0.5rem;
  left: 0.6rem;
  max-width: calc(100% - 1.2rem);
  padding: 0.15rem 0.45rem;
  font-size: 0.8rem;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-radius: var(--radius);
  pointer-events: none;
}

/* The way to one address (views/mac-search.js): a drawing that stands still, as large as
   what it shows, and smaller where the window is. */
.graph-way {
  padding: 0.75rem;
  border-top: 1px solid var(--border);
}

.graph-view.graph-way-view {
  width: auto;
  max-width: 100%;
  height: auto;
  background: none;
  border-radius: 0;
  cursor: default;
  touch-action: auto;
  user-select: text;
  -webkit-user-select: text;
}

/* --- What a drawing is made of. Font sizes as in views/topology-graph.js. --- */
.graph-view text {
  font-family: var(--font);
  font-size: 13px;
  fill: var(--text);
}

.graph-view .g-head {
  font-size: 14px;
  font-weight: 600;
}

.graph-view .g-title {
  font-weight: 600;
}

.graph-view .g-detail {
  font-size: 12px;
  fill: var(--text-muted);
}

/* The name of a neighbour's own port, on the line that leads to it. */
.graph-view .g-far {
  font-size: 11px;
  fill: var(--text-muted);
}

.g-box {
  fill: var(--surface);
  stroke: var(--border);
  stroke-width: 1;
}

.g-router > .g-box {
  stroke: var(--text-muted);
  stroke-width: 1.5;
}

.g-rule {
  stroke: var(--border);
}

.g-neighbour > .g-box {
  stroke: var(--accent);
  stroke-width: 1.5;
}

/* What opens the list of the devices behind it on a click. */
.graph-view [data-behind] {
  cursor: pointer;
}

.graph-view [data-behind]:hover > .g-box,
.graph-view [data-behind]:focus-visible > .g-box {
  fill: var(--row-hover);
}

.graph-view [data-behind]:hover > text:not(.g-detail):not(.g-badge-text),
.graph-view [data-behind]:focus-visible > text:not(.g-detail):not(.g-badge-text) {
  fill: var(--accent);
}

.graph-view [data-behind]:focus {
  outline: none;
}

/* A device that is read over SNMP: framed like a neighbour, and marked as read. */
.g-switch > .g-box {
  stroke: var(--accent);
  stroke-width: 1.5;
}

.g-badge {
  fill: var(--accent);
}

.graph-view .g-badge-text {
  font-size: 10px;
  font-weight: 600;
  fill: var(--on-accent);
}

/* The name of a switch's port without a link. */
.graph-view .g-far.down {
  fill: var(--lamp-off);
}

/* The internet is no device: green, so that nobody takes it for one. */
.graph-view .g-internet > .g-box {
  fill: var(--internet-fill);
  stroke: var(--lamp-ok);
  stroke-dasharray: 5 3;
}

.g-line {
  fill: none;
  stroke: var(--text-muted);
}

/* A cable is as wide as this (`CABLE` in topology-graph.js). */
.g-line:not(.wifi) {
  stroke-width: 1.5;
}

/* A line somebody said is a radio link, or that leaves a radio: light blue. How wide it is,
   the line itself says, which a rule here would overrule: twice what a cable is, and twice
   the band of what flows along it while the drawing is live. What flows along it lies over
   it. */
.g-line.wifi {
  stroke: var(--line-wifi);
  stroke-linecap: round;
}

.g-line.down,
.g-line.off {
  stroke: var(--lamp-off);
  stroke-dasharray: 4 4;
}

.g-joint {
  fill: var(--text-muted);
}

/* While the drawing is live: what flows along a line, as a band under it that is the wider
   the more flows, and as points that move the way the traffic goes. */
.g-flow-line {
  fill: none;
  stroke: var(--accent);
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.3;
}

/* The points are the dashes of one line: each without length and with round ends, so as
   wide as the line is. How far they are apart, the line itself says. */
.g-flow-dots {
  fill: none;
  stroke: var(--accent);
  stroke-width: 7.2;
  stroke-linecap: round;
  /* How long a round takes, the script says for each line: as long as a point needs to the
     place of the one before it, at the one speed all of them have. */
  animation: g-flow linear infinite;
}

/* The dashes pushed along the line, away from the router: by the distance from one point to
   the next, which the script says for each line (`setFlowing` in topology-graph.js). */
@keyframes g-flow {
  from {
    stroke-dashoffset: var(--flow-gap, 240px);
  }
  to {
    stroke-dashoffset: 0;
  }
}

/* What comes in through a port moves towards the router. */
.g-flow-dots.rx {
  animation-direction: reverse;
}

/* The two directions can be told apart where both are shown: what goes out of a port,
   away from the router, has the colour of a warning that is none. */
.g-flow-line.tx {
  stroke: var(--lamp-warn);
}

.g-flow-dots.tx {
  stroke: var(--lamp-warn);
}

/* What flows along a radio link has colours of its own, for each of the two directions. */
.g-flow-dots.wifi {
  stroke: var(--flow-wifi-in);
}

.g-flow-dots.tx.wifi {
  stroke: var(--flow-wifi-out);
}

/* Each button says by a point in that colour which direction it switches. */
#graph-way button::before {
  content: "";
  display: inline-block;
  width: 0.55rem;
  height: 0.55rem;
  margin-right: 0.4rem;
  background: var(--accent);
  border-radius: 50%;
  box-shadow: 0 0 0 1.5px var(--surface);
}

#graph-way button[data-way="tx"]::before {
  background: var(--lamp-warn);
}

/* How much flows through a port, below its name and smaller than it (`FONT.far` in
   views/topology-graph.js), so that the router is no wider live than it is otherwise. */
.graph-view .g-rate {
  font-size: 11px;
  fill: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* A node that holds an address the drawing did not have at the reading before: behind it a
   shade that comes and goes once a second, for two minutes. Where its round is, the script says, so
   that a drawing made anew goes on with it. */
.g-halo {
  fill: var(--lamp-warn);
  filter: blur(4px);
  opacity: 0.4;
  animation: g-new 0.5s ease-in-out infinite alternate;
}

@keyframes g-new {
  from {
    opacity: 0.12;
  }
  to {
    opacity: 0.6;
  }
}

/* Whoever asked the system for less movement gets the widths and the figures, and a shade
   that stays as it is. */
@media (prefers-reduced-motion: reduce) {
  .g-flow-dots {
    display: none;
  }

  .g-halo {
    animation: none;
  }
}

/* The colour of a network fills the left of its node, as far as the text begins; pale grey
   where the network has none. The rim is drawn once more over it, so that it stays whole. */
.g-band.none {
  fill: var(--lamp-off-fill);
}

.g-rim {
  fill: none;
  stroke: var(--border);
  stroke-width: 1;
}

/* What stands between a port and several devices, where nobody knows what it is. */
.g-hub > .g-box {
  stroke: var(--text-muted);
  stroke-dasharray: 5 3;
}

/* The same once somebody has named it: a device like the others that are known. */
.g-named > .g-box,
.g-relay > .g-box {
  stroke: var(--text-muted);
  stroke-width: 1.5;
}

/* The button beside a node that folds what is behind it, and unfolds it again. */
.g-fold {
  cursor: pointer;
}

.g-fold rect {
  fill: var(--surface);
  stroke: var(--text-muted);
  stroke-width: 1;
}

.g-fold path {
  fill: none;
  stroke: var(--text);
  stroke-width: 1.5;
}

.g-fold:hover rect,
.g-fold:focus-visible rect {
  stroke: var(--accent);
  stroke-width: 2;
}

.g-fold:focus {
  outline: none;
}

/* A port, as the lamp of a state: filled green with a link, pale grey with a rim without
   one, and the rim dashed where the port is switched off. */
.g-socket {
  fill: var(--lamp-off-fill);
  stroke: var(--lamp-off);
  stroke-width: 1.5;
}

.g-port.up .g-socket {
  fill: var(--lamp-ok);
  stroke: var(--lamp-ok);
}

.g-port.off .g-socket {
  stroke-dasharray: 2 2;
}

.graph-view .g-port.down text,
.graph-view .g-port.off text {
  fill: var(--text-muted);
}
