/*
 * The overlays sit on top of gameplay footage, so this stylesheet answers to a different master
 * than the dashboard's: the background must be nothing at all, and everything drawn has to stay
 * legible over both a bright surface and a black cave. That is what the scrims and the shadows are
 * for — the palette is the booth's, so the stream and the booth read as one system.
 *
 * Each panel draws at the top left of its own source, at its natural size. OBS positions and scales
 * the source; a page that centred itself would fight that.
 */

:root {
  /* The names stay the booth's; the values are the Broadcast Deck's, so the stream and the booth
     read as one system. */
  --phosphor: #22d3ee;
  --kelp: #34d399;
  --amber: #fbbf24;
  --coral: #f87171;
  --bone: #f2f4f8;
  --ash: #9aa3b5;
  --ghost: #5c6478;

  --scrim: #08080d99;
  --scrim-solid: #08080de6;

  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "IBM Plex Mono", "Cascadia Mono", Menlo,
    Consolas, "DejaVu Sans Mono", monospace;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  /* Transparent, not dark: OBS composites this over the capture, and any background at all would
     paint a box over the game. */
  background: transparent;
  overflow: hidden;
}

body {
  font: 400 15px/1.35 var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--bone);
  -webkit-font-smoothing: antialiased;

  /* Nothing here is interactive. A cursor or a selection highlight on a browser source is a thing
     that can end up on camera. */
  cursor: none;
  user-select: none;
}

/* One page, one panel. Everything else is not merely hidden but absent, so a stray element cannot
   claim a corner of a source that is 320px wide.
   
   The `display` of a panel lives *here* and nowhere else. A component rule like `.strip` carries a
   class, which outranks `body > section` — two type selectors — so setting display there would show
   every panel at once whatever this said. It did, until it was looked at. */
body > section,
body > ol {
  display: none;
}

body[data-panel="status"] .strip,
body[data-panel="toasts"] .toasts {
  display: flex;
}

body[data-panel="board"] .board,
body[data-panel="table"] .board--table,
body[data-panel="map"] .map,
body[data-panel="setup"] .setup {
  display: block;
}

/* The board and the table share a stylesheet and a page, so the panel that was not asked for has to
   stay absent even though both carry `.board`. Two attributes and a class, which outranks the block
   above rather than fighting it. */
body[data-panel="board"] .board--table,
body[data-panel="table"] .board:not(.board--table) {
  display: none;
}

/* Absent rather than wrong: with no match running, an overlay shows nothing at all. "No match" on
   a stream is worse than a clean frame. The setup card is the exception — it is a mistake being
   pointed out, and it has to be visible before a match starts.
   
   One attribute and one :not() more specific than the rules above, which is what makes it win. */
body[data-showing="no"] > section:not(.setup):not(.map),
body[data-showing="no"] > ol {
  display: none;
}

/* The map is the second exception, and it is blanked rather than removed. A canvas in a box that is
   not laid out measures zero, and `map3d.js` sizes the renderer from that box — so a source added to
   a scene between matches would build the map at the renderer's default size and still be that size
   when the group loaded in. Transparent keeps the element laid out and keeps the frame clean, and it
   costs nothing to leave rendering: the solid map draws on demand rather than sixty times a second
   (ADR-0008 addendum 4). */
body[data-showing="no"] > .map {
  opacity: 0;
}

/* ---- the status strip ------------------------------------------------------------------ */

.strip {
  position: relative;
  align-items: stretch;
  gap: 1px;
  width: max-content;
  background: #0c202c99;
  border: 1px solid #5fe3f033;
  backdrop-filter: blur(6px);
  box-shadow: 0 6px 26px #0009;
}

.strip__cell {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 9px 18px 11px;
  background: var(--scrim);
  min-width: 96px;
}

.strip__label {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ash);
  text-shadow: 0 1px 3px #000;
}

.strip__value {
  font-size: 24px;
  line-height: 1;
  color: var(--bone);
  text-shadow: 0 2px 6px #000c;
}

.strip__cell--alive .strip__value {
  color: var(--kelp);
  font-size: 30px;
}

.strip__cell--next .strip__value {
  color: var(--phosphor);
}

/* The last minute of a phase, said in colour rather than in words. */
.strip__cell--next[data-soon="true"] .strip__value {
  color: var(--amber);
}

.strip__rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: #0a1a2466;
}

.strip__rule > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--phosphor);
  box-shadow: 0 0 10px var(--phosphor);
  transition: width 1s linear;
}

/* ---- the elimination cards ------------------------------------------------------------- */

.toasts {
  margin: 0;
  padding: 0;
  list-style: none;
  flex-direction: column;
  gap: 8px;
  width: max-content;
}

.toast {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px 12px 16px;
  background: var(--scrim-solid);
  border-left: 4px solid var(--coral);
  box-shadow: 0 8px 30px #000b;
  font-size: 19px;
  white-space: nowrap;

  /* Entry only. A leave animation would mean holding a card in the DOM after the queue has dropped
     it, and the queue is the thing that decides what is on screen. */
  animation: arrive 320ms cubic-bezier(0.2, 0.9, 0.25, 1) both;
}

.toast__mark {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--coral);
  box-shadow: 0 0 12px var(--coral);
  flex: none;
}

.toast[data-kind="won"] {
  border-left-color: var(--phosphor);
  font-size: 26px;
  letter-spacing: 0.03em;
  padding: 16px 26px 16px 20px;
}

.toast[data-kind="won"] .toast__mark {
  background: var(--phosphor);
  box-shadow: 0 0 16px var(--phosphor);
}

.toast[data-kind="won"] .toast__text {
  color: var(--phosphor);
}

.toast__text {
  text-shadow: 0 2px 6px #000c;
}

@keyframes arrive {
  from {
    opacity: 0;
    transform: translateX(-22px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    animation: none;
  }
}

/* ---- the board ------------------------------------------------------------------------- */

.board {
  width: max-content;
  background: var(--scrim-solid);
  border: 1px solid #5fe3f033;
  box-shadow: 0 8px 30px #000b;
}

.board table {
  border-collapse: collapse;
}

.board th {
  padding: 8px 14px 7px;
  text-align: left;
  font-weight: 400;
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ash);
  border-bottom: 1px solid #5fe3f033;
  white-space: nowrap;
}

.board td {
  padding: 5px 14px;
  white-space: nowrap;
  border-bottom: 1px solid #ffffff0f;
  text-shadow: 0 1px 4px #000c;
}

.board__place,
.board__num {
  text-align: right;
}

.board td.board__place {
  color: var(--ghost);
  width: 34px;
}

.board td.board__points {
  color: var(--phosphor);
}

.board tr[data-alive="false"] td {
  color: var(--ghost);
}

.board tr[data-alive="false"] .board__name {
  text-decoration: line-through;
  text-decoration-color: #ffffff33;
}

/* Whoever is holding first place on their own. */
.board tr[data-lead="true"] .board__name {
  color: var(--kelp);
}

.board__name {
  color: var(--bone);
}

/* How somebody went out, on the row rather than only in a card that lasts a few seconds (LMS-25's
   own unfinished half). Dim and small: it is context for a name a viewer is already reading, not a
   column they scan. Left-aligned against the numbers beside it, because it is words. */
.board__cause {
  color: var(--ash);
  font-size: 0.82em;
  text-align: left;
  padding-left: 10px;
  white-space: nowrap;
}

/* ---- the tournament table -------------------------------------------------------------- */

/* Padded to the same 14px the cells are, so the caption reads as the table's own heading rather
   than as something sitting behind it. It carries the panel's own rule under it for the same
   reason the header row has one. */
.board__caption {
  margin: 0;
  padding: 8px 14px 7px;
  color: var(--ash);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  border-bottom: 1px solid #5fe3f033;
}

/* A place shared with somebody else is drawn the same as any other: the number is the mod's answer
   and the equals sign is the tell, so a caster reading two firsts sees two firsts. */
.board--table tr[data-shared="true"] .board__place::before {
  content: "=";
  opacity: 0.55;
}

/* ---- the map --------------------------------------------------------------------------- */

/* The one panel that fills its source rather than drawing at its natural size in the corner: it is
   the shot a stream cuts to, not a readout laid over one (LMS-77a).

   Fixed rather than in flow, because the canvas is sized from this element's own box and a box that
   is sized by its content would be sized by a canvas that is sized by the box. */
.map {
  position: fixed;
  inset: 0;
}

.map canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- the setup card -------------------------------------------------------------------- */

.setup {
  max-width: 62ch;
  margin: 18px;
  padding: 22px 26px;
  background: var(--scrim-solid);
  border: 1px solid #5fe3f033;
  cursor: auto;
  user-select: text;
}

.setup h1 {
  margin: 0 0 12px;
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--phosphor);
}

.setup p {
  margin: 0 0 14px;
  color: var(--ash);
  font-size: 13px;
  line-height: 1.6;
}

.setup ul {
  margin: 0 0 14px;
  padding: 0;
  list-style: none;
}

.setup li {
  padding: 4px 0;
  font-size: 13px;
  overflow-wrap: anywhere;
}

.setup li b {
  color: var(--phosphor);
  font-weight: 400;
}

.setup__link b {
  color: var(--bone);
  font-weight: 400;
}

html[data-link="live"] .setup__link b {
  color: var(--kelp);
}

html[data-link="refused"] .setup__link b,
html[data-link="blocked"] .setup__link b,
html[data-link="mismatched"] .setup__link b {
  color: var(--coral);
}

/* The AGPL §13 offer of source (ADR-0024, ADR-0042). It sits inside the setup card, so
   `body > section { display: none }` already keeps it off every panel that is a browser source —
   this only says what it looks like to the person who opened the URL. */
.setup__source {
  margin: 0;
  font-size: 11px;
  color: var(--ash);
  opacity: 0.7;
}

.setup__source a {
  color: inherit;
}
