/* ===========================================================================
   OPS WALL — the theme.

   This is a screen that lives on a wall, two or three metres from the nearest
   pair of eyes, in a room where somebody glances up every few minutes. That
   single fact decides everything below.

   - Near-black ground, not dark grey. A wall screen in a lit room reflects;
     a true black ground keeps the panels floating instead of greying out.
   - Colour is reserved. Four signals and nothing else: green running, amber
     at risk, red breached, cyan selected. If a fifth thing wanted a colour
     the answer is that it does not need one.
   - Every number is tabular monospace. Figures that jitter as they count are
     unreadable at three metres, and the jitter is what you notice first.
   - Type is large and the hierarchy is brutal. A label is 10px and dim; the
     number it labels is 28px and bright. Nothing in between.
   =========================================================================== */
:root{
  --bg:#07090C;
  --panel:#0D1117;
  --panel-2:#11161E;
  --line:rgba(255,255,255,.075);
  --line-2:rgba(255,255,255,.14);
  --ink:#E9EFF6;
  --ink-2:#9FB0C0;
  --mut:#738293;
  --run:#2FD98A;
  --risk:#FFB020;
  --brk:#FF4D5E;
  --sel:#47D6FF;
  --mono:var(--f-data);
  --sans:var(--f-text);
}
*{box-sizing:border-box}
html,body{height:100%}
/* The orientation strip is injected as the first child of <body>. A child
   below it set to 100vh is therefore 100vh TALL UNDER a 40px bar, and the
   bottom 40px - here, the live ticker - is simply off the screen. It took a
   screenshot to notice, because nothing overflows and nothing scrolls. So the
   body is the flex column and the app takes what is left. */
body{
  /* GROUND: a lifted radial behind the top bar, plus a 4px scanline at 2%
     - a wall screen is a screen, and the faintest suggestion of one reads as
     equipment rather than as a dark theme. */
  margin:0;color:var(--ink);font-family:var(--sans);
  background:
    radial-gradient(100% 46% at 50% 0%,#101620,transparent 70%),
    repeating-linear-gradient(0deg,
      rgba(255,255,255,.016) 0 1px,transparent 1px 4px),
    var(--bg);
  -webkit-font-smoothing:antialiased;overflow:hidden;
  height:100vh;display:flex;flex-direction:column;
}
button{font-family:inherit}

/* ---------------------------------------------------------------- the frame */
#wall{
  flex:1;min-height:0;display:grid;
  grid-template-rows:auto 1fr auto;
}
.bar{
  display:flex;align-items:center;gap:20px;padding:11px 18px;
  background:linear-gradient(180deg,#10161F,#0A0E13);
  border-bottom:1px solid var(--line-2);
}
.bar .who{display:flex;align-items:baseline;gap:11px;min-width:0}
.bar .who b{font-size:15px;font-weight:650;letter-spacing:-.01em;white-space:nowrap}
.bar .who span{font-size:11.5px;color:var(--mut);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.clock{
  font-family:var(--mono);font-size:26px;font-weight:600;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;color:#fff;line-height:1;
}
.clock small{display:block;font-size:10.5px;letter-spacing:.16em;color:var(--mut);
  font-weight:600;margin-top:4px;text-transform:uppercase}
.ctl{margin-left:auto;display:flex;align-items:center;gap:7px}
.ctl button{
  height:34px;padding:0 13px;border:1px solid var(--line-2);border-radius:5px;
  background:#131A23;color:var(--ink-2);font-size:12.5px;font-weight:600;cursor:pointer;
  display:inline-flex;align-items:center;gap:7px;transition:.14s;
}
.ctl button:hover{background:#1A222D;color:#fff}
.ctl button[aria-pressed=true]{background:var(--sel);border-color:var(--sel);color:#04161D}
.ctl .sp{display:flex;border:1px solid var(--line-2);border-radius:5px;overflow:hidden}
.ctl .sp button{border:0;border-radius:0;height:32px;padding:0 11px;font-family:var(--mono)}
.ctl .sp button+button{border-left:1px solid var(--line-2)}
.ctl svg{width:13px;height:13px;fill:currentColor}

/* live pip — the one piece of motion that is allowed to be decorative,
   because "is this screen frozen or is nothing happening" is a real question */
.pip{width:8px;height:8px;border-radius:50%;background:var(--run);flex:none;
  box-shadow:0 0 0 0 rgba(47,217,138,.55);animation:pulse 2.4s ease-out infinite}
.pip.off{background:var(--mut);animation:none;box-shadow:none}
@keyframes pulse{
  0%{box-shadow:0 0 0 0 rgba(47,217,138,.5)}
  70%{box-shadow:0 0 0 9px rgba(47,217,138,0)}
  100%{box-shadow:0 0 0 0 rgba(47,217,138,0)}
}

/* ------------------------------------------------------------- three panes */
#body{display:grid;grid-template-columns:372px 1fr 290px;min-height:0}
.pane{min-height:0;display:flex;flex-direction:column;background:var(--panel)}
.pane+.pane{border-left:1px solid var(--line-2)}
.phead{
  display:flex;align-items:center;gap:9px;padding:11px 15px 10px;
  border-bottom:1px solid var(--line);flex:none;
}
.phead h2{
  margin:0;font-size:10.5px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-2);font-weight:700;
}
.phead .ct{
  margin-left:auto;font-family:var(--mono);font-size:12px;color:var(--mut);
  font-variant-numeric:tabular-nums;
}
.scroll{overflow-y:auto;min-height:0;flex:1;scrollbar-width:thin;
  scrollbar-color:#28313D transparent}
.scroll::-webkit-scrollbar{width:9px}
.scroll::-webkit-scrollbar-thumb{background:#28313D;border-radius:5px}

/* --------------------------------------------------------------- the queue */
.job{
  display:grid;grid-template-columns:1fr auto;gap:2px 11px;
  padding:11px 15px;border-bottom:1px solid var(--line);cursor:pointer;
  border-left:3px solid transparent;transition:background .12s;
}
.job:hover{background:var(--panel-2)}
.job.on{background:#101C24;border-left-color:var(--sel)}
.job.s-risk{border-left-color:var(--risk)}
.job.s-brk{border-left-color:var(--brk)}
.job.s-run{border-left-color:var(--run)}
.job .t{font-size:13.5px;font-weight:600;color:var(--ink);line-height:1.3}
.job .m{font-size:11.5px;color:var(--mut);grid-column:1;font-variant-numeric:tabular-nums}
.job .cd{
  grid-row:1/3;align-self:center;text-align:right;font-family:var(--mono);
  font-size:19px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.02em;
}
.job .cd small{display:block;font-size:10.5px;letter-spacing:.13em;color:var(--mut);
  font-weight:700;text-transform:uppercase;margin-top:2px;font-family:var(--sans)}
.job.s-run .cd{color:var(--run)}
.job.s-risk .cd{color:var(--risk)}
.job.s-brk .cd{color:var(--brk)}
.job .tag{
  display:inline-block;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
  font-weight:700;padding:2px 6px;border-radius:3px;margin-left:7px;vertical-align:1px;
}
.tag-risk{background:rgba(255,176,32,.16);color:var(--risk)}
.tag-brk{background:rgba(255,77,94,.16);color:var(--brk)}
.tag-run{background:rgba(47,217,138,.14);color:var(--run)}

/* ----------------------------------------------------------------- the map */
.mapwrap{position:relative;flex:1;min-height:0;background:
  radial-gradient(120% 100% at 50% 0%,#0C1218,#06080B 70%)}
.mapwrap svg{position:absolute;inset:0;width:100%;height:100%}
.ward{fill:#0E1620;stroke:#1C2733;stroke-width:1.2;transition:fill .18s,stroke .18s}
.ward.hot{fill:#171113;stroke:#33202A}
.ward.on{fill:#0C1D26;stroke:var(--sel);stroke-width:1.8}
.wlab{font:600 10px var(--sans);fill:#56677A;letter-spacing:.08em;
  text-transform:uppercase;pointer-events:none}
.road{fill:none;stroke:#1F2B38;stroke-width:2.8;stroke-linecap:round}
.road.lit{stroke:#22313F}
.veh{transition:transform .9s linear}
.veh .bd{fill:var(--run);stroke:#05080A;stroke-width:1.1}
.veh.idle .bd{fill:var(--risk)}
.veh.off .bd{fill:var(--brk)}
.veh.on .bd{fill:var(--sel)}
.veh .ring{fill:none;stroke:currentColor;opacity:.35}
.veh .lab{font:600 9.5px var(--mono);fill:#8DA0B2;letter-spacing:.02em}
.job-pin{cursor:pointer}
.job-pin .dot{fill:var(--risk);stroke:#06090C;stroke-width:1.4}
.job-pin.s-brk .dot{fill:var(--brk)}
.job-pin.s-run .dot{fill:var(--run)}
.job-pin.on .dot{fill:var(--sel)}
.job-pin .halo{fill:none;stroke:currentColor;opacity:.5}
.link{stroke:var(--sel);stroke-width:1.4;stroke-dasharray:4 4;fill:none;opacity:.8}
.maplegend{
  position:absolute;left:14px;bottom:13px;display:flex;gap:16px;flex-wrap:wrap;
  padding:9px 13px;background:rgba(7,9,12,.82);border:1px solid var(--line);
  border-radius:6px;backdrop-filter:blur(6px);
}
.maplegend span{display:inline-flex;align-items:center;gap:7px;font-size:11px;
  color:var(--ink-2);font-weight:600;white-space:nowrap}
.maplegend i{width:9px;height:9px;border-radius:50%;flex:none}
.note{
  margin:0 13px 15px;padding:12px 13px;border-left:2px solid var(--sel);
  background:#0A1219;font-size:11.5px;line-height:1.6;color:var(--ink-2);
}
.note b{color:#fff;font-weight:650}

/* --------------------------------------------------------------- the tiles */
.tiles{padding:13px;display:grid;gap:11px}
.tile{padding:12px 13px;background:var(--panel-2);border:1px solid var(--line);
  border-radius:7px;position:relative;overflow:hidden}
.tile .k{font-size:10.5px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--mut);font-weight:700}
.tile .v{font-family:var(--mono);font-size:30px;font-weight:600;letter-spacing:-.03em;
  line-height:1.05;margin-top:7px;font-variant-numeric:tabular-nums}
.tile .u{font-size:11.5px;color:var(--ink-2);margin-top:4px;line-height:1.35}
.tile.risk .v{color:var(--risk)}
.tile.brk .v{color:var(--brk)}
.tile.run .v{color:var(--run)}
/* the sparkline is drawn as a row of bars rather than a path: at this size a
   path reads as a smudge and bars still read as bars */
.spark{display:flex;align-items:flex-end;gap:2px;height:26px;margin-top:9px}
.spark i{flex:1;background:#243040;border-radius:1px;min-height:2px}
.spark i.hi{background:var(--risk)}

/* -------------------------------------------------------------- the ticker */
.tick{
  flex:none;display:flex;align-items:center;gap:0;height:38px;
  background:#05070A;border-top:1px solid var(--line-2);overflow:hidden;
}
.tick .lb{
  flex:none;padding:0 14px;height:100%;display:flex;align-items:center;
  font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;font-weight:700;
  color:#04161D;background:var(--sel);
}
.tick .rail{flex:1;min-width:0;overflow:hidden;position:relative;height:100%}
.tick .row{
  position:absolute;inset:0;display:flex;align-items:center;gap:26px;
  padding:0 16px;font-size:12.5px;white-space:nowrap;
  transition:transform .4s cubic-bezier(.3,0,.2,1);
}
.tick .ev{display:inline-flex;align-items:center;gap:8px;color:var(--ink-2);flex:none}
.tick .ev time{font-family:var(--mono);color:var(--mut);font-size:11.5px;
  font-variant-numeric:tabular-nums}
.tick .ev b{color:#fff;font-weight:600}
.tick .ev.risk b{color:var(--risk)}
.tick .ev.brk b{color:var(--brk)}
.tick .ev.run b{color:var(--run)}

/* ------------------------------------------------------------- the detail */
.det{border-top:1px solid var(--line-2);padding:13px 15px;background:#0A1016;flex:none}
.det h3{margin:0 0 3px;font-size:14px;font-weight:650}
.det .sub{font-size:11.5px;color:var(--mut);font-variant-numeric:tabular-nums}
.det dl{display:grid;grid-template-columns:auto 1fr;gap:5px 12px;margin:11px 0 0;
  font-size:12px}
.det dt{color:var(--mut)}
.det dd{margin:0;color:var(--ink);font-variant-numeric:tabular-nums}
.det .act{display:flex;gap:8px;margin-top:12px}
.det .act button{
  flex:1;height:36px;border:0;border-radius:5px;cursor:pointer;
  font-size:12.5px;font-weight:650;background:var(--sel);color:#04161D;transition:.14s;
}
.det .act button:hover{filter:brightness(1.1)}
.det .act button.gh{background:transparent;border:1px solid var(--line-2);color:var(--ink-2)}
.det .act button.gh:hover{background:#131A23;color:#fff}
.det .act button:disabled{background:#1A222D;color:var(--mut);cursor:default;filter:none}
.empty{padding:26px 16px;text-align:center;color:var(--mut);font-size:12.5px;line-height:1.6}

/* ------------------------------------------------------------- narrow view
   A wall screen is a wall screen, but this gets opened on a laptop in a
   meeting room and on a phone in a corridor. The map is the thing that has to
   survive: below 1180px the tiles go under it, below 900px the queue does. */
@media(max-width:1180px){
  #body{grid-template-columns:340px 1fr}
  .pane.tiles-pane{grid-column:1/-1;border-left:0;border-top:1px solid var(--line-2)}
  .pane.tiles-pane .tiles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
  .pane.tiles-pane .spark{display:none}
}
@media(max-width:900px){
  body{overflow:auto;height:auto;display:block}
  #wall{height:auto;min-height:100vh}
  #body{grid-template-columns:1fr}
  .pane+.pane{border-left:0;border-top:1px solid var(--line-2)}
  .mapwrap{height:58vh}
  .pane.queue-pane .scroll{max-height:none}
  .bar{flex-wrap:wrap;gap:12px 14px}
  .ctl{margin-left:0;width:100%;flex-wrap:wrap}
  .clock{font-size:22px}
}
/* A wall screen opened on a phone. Everything here exists because at 390px
   the header alone was 435px wide: a row of controls that will not wrap and a
   subtitle that will not break are each enough to push the whole grid out and
   put a horizontal scrollbar under a full-height layout, where it is almost
   impossible to notice and impossible to use. */
@media(max-width:620px){
  .bar{padding:10px 13px;gap:9px 11px}
  .bar .who{flex:1 1 100%;flex-wrap:wrap;gap:3px 10px}
  .bar .who span{white-space:normal;line-height:1.35;flex:1 1 100%;font-size:11px}
  .clock{font-size:19px}
  .clock small{letter-spacing:.1em}
  .ctl{gap:6px}
  .ctl button{height:38px;padding:0 10px;font-size:12px}
  .ctl .sp button{height:36px;padding:0 9px}
  .ctl #step{display:none}
  .phead{padding:10px 13px}
  .job{padding:10px 13px;grid-template-columns:1fr auto}
  .job .t{font-size:13px}
  .job .cd{font-size:17px}
  .job .m{font-size:11px;word-break:break-word}
  .job .tag{margin-left:6px}
  .tiles{padding:11px;grid-template-columns:repeat(auto-fit,minmax(138px,1fr))}
  .mapwrap{height:52vh}
  .maplegend{left:10px;bottom:10px;gap:10px;padding:7px 10px}
  .maplegend span{font-size:10.5px}
  .tick .lb{padding:0 11px;font-size:10.5px}
  .det dl{grid-template-columns:1fr;gap:1px}
  .det dt{margin-top:7px;font-size:11px}
}
@media(prefers-reduced-motion:reduce){
  .veh,.tick .row{transition:none}
  .pip{animation:none}
}

/* ------------------------------------------------------- touch, on a phone
   The control strip is built for a mouse two metres from a wall. On a phone
   the same controls have to be thumb-sized. */
@media(max-width:620px) and (pointer:coarse){
  .ctl button{min-height:44px}
  .ctl .sp button{min-height:44px;height:44px}
  .job{min-height:56px}
}
