/* --- the video call ---------------------------------------------------------

   Shares app.css for the bar and the palette, and adds only what a call
   needs. Dark on purpose beyond the theme: faces are the only thing on this
   page, and anything bright beside them competes with them. */

/* The call takes the window, rather than sitting in the middle of it. The
   stage is whatever is left once the bar, the status line and the dock have
   had theirs — `min-height: 0` so it is allowed to shrink, without which a
   flex child refuses to go below its contents and the dock is pushed off. */
.call {
  height: calc(100dvh - 3.25rem);
  padding: 0.75rem 0.75rem 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
}

/* Tiles are given their size by the script, which measures this box and
   works out the roomiest arrangement. Wrapping does the rest: fixed sizes
   plus wrap is a grid, without having to tell CSS what shape it is. */
.stage {
  position: relative;
  flex: 1;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  justify-content: center;
  gap: 10px;
}

.tile {
  position: relative;
  width: var(--tile-width, 20rem);
  height: var(--tile-height, 11.25rem);
  flex: none;
  background: #000;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Your own picture, the way a mirror shows you — which is the way you expect
   to see yourself, and the opposite of how everyone else sees you. */
.tile--me video { transform: scaleX(-1); }

/* --- the small pictures ---

   Whichever tiles are not the focus live in one floating strip, so that the
   six positions are a single positioning problem. Empty, it takes no room and
   catches no clicks. */
.overlays {
  position: absolute;
  z-index: 2;
  display: flex;
  gap: 0.5rem;
  max-width: calc(100% - 1.5rem);
  flex-wrap: wrap;
  cursor: grab;
  touch-action: none;
}
.overlays:empty { display: none; }
.overlays[data-dragging="true"] { cursor: grabbing; opacity: 0.85; }

/* Sized against the stage rather than in pixels, so it stays a corner of the
   picture on a phone as much as on a monitor — and floored, so it never
   becomes a stamp too small to tell whether a camera is even on. */
.overlays .tile {
  width: clamp(6.5rem, 15vw, 13rem);
  height: auto;
  aspect-ratio: 16 / 9;
  border-color: rgb(255 255 255 / 0.22);
  box-shadow: 0 6px 22px rgb(0 0 0 / 0.5);
}

/* At that size a name and a reading are illegible clutter. */
.overlays .tile__bar,
.overlays .tile__stats { display: none; }

/* The six. Each is a corner or a midpoint, and each stays sensible when the
   window changes shape. */
.stage[data-corner="top-left"] .overlays { top: 0.75rem; left: 0.75rem; }
.stage[data-corner="top-right"] .overlays { top: 0.75rem; right: 0.75rem; }
.stage[data-corner="bottom-left"] .overlays { bottom: 0.75rem; left: 0.75rem; }
.stage[data-corner="bottom-right"] .overlays { bottom: 0.75rem; right: 0.75rem; }

.stage[data-corner="top-middle"] .overlays {
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  justify-content: center;
}
.stage[data-corner="bottom-middle"] .overlays {
  bottom: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  justify-content: center;
}

/* While held, it follows the pointer rather than any anchor. Written with the
   stage in the selector so it outranks the six rules above. */
.stage .overlays[data-dragging="true"] {
  top: var(--drag-y);
  left: var(--drag-x);
  right: auto;
  bottom: auto;
  transform: translate(-50%, -50%);
}

/* A tap on your own picture swaps you with everyone else. */
.tile--me { cursor: pointer; }

/* A stopped camera should read as deliberate, not as a connection that died. */
.tile[data-off="true"] video { visibility: hidden; }
.tile[data-off="true"]::after {
  content: "Camera off";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Still negotiating: dimmed, so a black square is visibly not a frozen one. */
.tile[data-state="connecting"], .tile[data-state="new"] { opacity: 0.45; }
.tile[data-state="failed"] { border-color: var(--bad); }

/* Name and signal along the bottom, over a wash rather than a chip: a solid
   box on top of a face is a box on top of a face. */
.tile__bar {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 1.6rem 0.7rem 0.5rem;
  background: linear-gradient(transparent, rgb(0 0 0 / 0.72));
  pointer-events: none;
}

.tile__name {
  font-size: 0.82rem;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.6);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The reading itself sits above the bar, small and out of the way until it
   is wanted — which, on a call across the world, is most of the time. */
.tile__stats {
  position: absolute;
  left: 0.6rem;
  bottom: 1.9rem;
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  color: rgb(255 255 255 / 0.72);
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
  pointer-events: none;
}

/* Four bars, filled by how good the link is. The same vocabulary the board's
   presence panel uses, so one reading teaches you to read the other. */
.signal { display: flex; align-items: flex-end; gap: 2px; height: 0.75rem; flex: none; }

.signal i {
  width: 3px;
  background: rgb(255 255 255 / 0.28);
  border-radius: 1px;
}
.signal i:nth-child(1) { height: 30%; }
.signal i:nth-child(2) { height: 50%; }
.signal i:nth-child(3) { height: 75%; }
.signal i:nth-child(4) { height: 100%; }

.signal[data-signal="strong"] i { background: var(--good); }
.signal[data-signal="good"] i:nth-child(-n+3) { background: var(--good); }
.signal[data-signal="weak"] i:nth-child(-n+2) { background: var(--accent); }
.signal[data-signal="poor"] i:nth-child(1) { background: var(--bad); }

/* Going through a relay is worth knowing and not worth shouting about. */
.tile[data-route="relayed"]::before {
  content: "relayed";
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  padding: 0.1rem 0.4rem;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  background: rgb(0 0 0 / 0.6);
  border-radius: 3px;
  z-index: 1;
}

.call__status { margin: 0; color: var(--text-dim); font-size: 0.9rem; text-align: center; flex: none; }
.call__status[data-kind="bad"] { color: var(--bad); }

.call__warn { font-size: 0.75rem; color: var(--accent); }

/* Tabular figures so the seconds do not shove the minutes about as they
   count, which on a number that changes every second is very visible. */
.call__clock {
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.call__join {
  flex: none;
  padding: 0.8rem 2rem;
  font-size: 1rem;
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}

/* --- the controls ---

   A dock of round buttons, the shape every video call has settled on, and
   the one people already know how to read.

   The glyph is drawn by the button itself rather than by a span inside it.
   That is the pattern the board's controls use and it is the one that works:
   a masked element whose image fails to resolve paints as a solid block of
   `currentColor`, and a stray block is exactly what an extra element gets
   you when anything about it is off. One element, one glyph, nothing to
   disagree with. */

.dock {
  flex: none;
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.5rem 0.65rem;
  background: rgb(0 0 0 / 0.35);
  border: 1px solid var(--border);
  border-radius: 1.75rem;
  backdrop-filter: blur(8px);
}

.control {
  display: grid;
  justify-items: center;
  gap: 0.4rem;
  width: 4.6rem;
  padding: 0;
  background: none;
  border: none;
  color: var(--text);
}

/* The button is the circle; the label hangs below it. */
.control::before {
  content: "";
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background-color: var(--surface-raised);
  border: 1px solid var(--border);
  transition: background-color 140ms ease, border-color 140ms ease;
  /* The glyph, punched out of the circle by a second layer. */
  background-image: var(--icon);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 1.4rem;
}

.control:hover::before { background-color: #38342a; border-color: var(--text-dim); }
.control:active::before { transform: translateY(1px); }

.control__label {
  font-size: 0.72rem;
  color: var(--text-dim);
  white-space: nowrap;
}
.control:hover .control__label { color: var(--text); }

/* Off is a state worth reading across a room, so it is the loud one. */
.control[data-off="true"]::before {
  background-color: #4a2420;
  border-color: var(--bad);
}
.control[data-off="true"] .control__label { color: var(--bad); }

.control--leave::before { background-color: #4a2420; border-color: #6b3630; }
.control--leave:hover::before { background-color: var(--bad); border-color: var(--bad); }
.control--leave .control__label { color: var(--bad); }

/* The glyphs. Tinted by the SVG itself rather than masked: these are drawn
   dark, so they are inverted to sit on a dark circle. */
.control--mic { --icon: url("../icon/mic.svg"); }
.control--camera { --icon: url("../icon/video.svg"); }
.control--leave { --icon: url("../icon/leave.svg"); }
.control--popout { --icon: url("../icon/popout.svg"); }


.control--mic[data-off="true"] { --icon: url("../icon/mic-off.svg"); }
.control--camera[data-off="true"] { --icon: url("../icon/video-slash.svg"); }

/* --- the call, popped out --------------------------------------------------

   The stage is moved bodily into a floating always-on-top window, which is
   why this is worth doing at all: the same elements keep playing, so there is
   no renegotiation, no second stream and no gap in the picture. It is the
   same call, in a different window.

   The stage sizes itself from its parent, so in there it needs one. */
body.popped {
  margin: 0;
  background: var(--bg);
  overflow: hidden;
}

body.popped .stage {
  width: 100vw;
  height: 100dvh;
  padding: 0;
  gap: 4px;
}

/* Nothing worth reading at that size, and the strip would cover a face. */
body.popped .tile__stats,
body.popped .overlays { display: none; }
