/* The conversation, shared by both pairing paths.
   
   These rules used to live in p.css, where only the phone-to-phone page could
   see them, while the LAN page kept a screen of cards. One product, one
   surface: both pages load this, and what stays in their own stylesheet is the
   screen that comes before a connection exists.
   
   A transfer is something that happened between two people, in order, with a
   direction — which is what a message list is for. Cards made it look like
   configuration. */

/* ---- The conversation --------------------------------------------------- */

/* Fills the viewport so the composer sits on the bottom edge rather than after
   the content, which is what makes it read as a chat and not a page.
   
   Height is dvh rather than inset:0 or vh. Both of those use the layout
   viewport, which on a phone is taller than what you can actually see — the
   composer ends up under the browser's own bar. dvh follows the visible area as
   that bar grows and shrinks; vh is kept above it for anything too old. */
.chat {
  position: fixed; left: 0; right: 0; top: 0;
  height: 100vh;
  height: 100dvh;
  display: flex; flex-direction: column;
  background: var(--bg);
  overflow: hidden;
}

/* A fixed height so it cannot grow with its contents, plus whatever the notch
   needs above it. The previous rule let long status text push the bar taller
   and squeeze the conversation. */
.bar {
  display: flex; align-items: center; gap: 12px;
  height: 60px; flex: none; box-sizing: content-box;
  padding: 0 16px;
  padding-top: env(safe-area-inset-top, 0px);
  border-bottom: 1px solid var(--line);
}
.avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  background: var(--track);
  display: flex; align-items: center; justify-content: center;
}
.sdot { width: 10px; height: 10px; border-radius: 50%; background: var(--faint); }
.sdot.work { background: var(--wait); animation: pulse 1.1s ease-in-out infinite; }
.sdot.on   { background: var(--ok); }
.sdot.bad  { background: var(--bad); }
@media (prefers-reduced-motion: reduce) { .sdot { animation: none !important; } }

.whobox { min-width: 0; }
.whobox b { display: block; font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
.sub2 {
  display: block; font-size: 12px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.stream {
  flex: 1; overflow-y: auto; padding: 18px 14px;
  display: flex; flex-direction: column; gap: 10px;
  -webkit-overflow-scrolling: touch;
}
.empty {
  margin: auto; color: var(--faint); font-size: 13px; text-align: center;
}

/* Not from either person: something that happened to the connection. */
.sys {
  align-self: center; max-width: 82%; margin: 6px 0;
  font-size: 12px; line-height: 1.5; text-align: center; color: var(--muted);
}

/* ---- Bubbles ------------------------------------------------------------ */

.msg { display: flex; max-width: 100%; }
.msg.out { justify-content: flex-end; }
.msg.in  { justify-content: flex-start; }

.bubble {
  max-width: 78%; min-width: 0;
  border-radius: 18px; padding: 8px 14px 8px 8px;
  background: var(--track);
  border: 1px solid transparent;
}
.msg.out .bubble {
  background: var(--accent); color: var(--bg);
  border-bottom-right-radius: 6px;
}
.msg.in .bubble { border-bottom-left-radius: 6px; }

/* A file row is a glyph and two lines, and the glyph is the button.
   
   Named .ficon and not .lead: app.css already owns .lead for the intro card on
   the first screen, and the two sheets load together. Taking the name turned
   that card into a 38px circle with its text spilling out one word per line.
   
   This is what every messaging app converged on, and the reason is that a file
   has exactly one thing you can do to it. Given a row and a separate Save
   button underneath, the row becomes a form; given the action on the file's own
   icon, it stays a message. */
.fline { display: flex; align-items: center; gap: 10px; }

.ficon {
  width: 38px; height: 38px; flex: none; padding: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: default;
  background: rgba(128, 128, 128, .22); color: inherit;
}
.ficon .i { width: 17px; height: 17px; opacity: .8; }
/* Only when there is something to be done to it. */
.msg.in .bubble .ficon.can {
  background: var(--accent); color: var(--bg); cursor: pointer;
}
.msg.in .bubble .ficon.can .i { opacity: 1; }
.msg.in .bubble .ficon.took { background: transparent; color: var(--ok); }
.msg.in .bubble .ficon.took .i { opacity: 1; }

.ftext { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.fname {
  font-size: 14px; font-weight: 600; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fmeta { font-size: 11.5px; opacity: .75; }
.msg.in .fmeta { color: var(--muted); opacity: 1; }

/* Progress reads as part of the bubble rather than a widget dropped into it. */
.bar2 {
  height: 3px; border-radius: 3px; margin-top: 9px; overflow: hidden;
  background: rgba(128, 128, 128, .28);
}
.bar2 i { display: block; height: 100%; width: 0; background: currentColor; transition: width .12s linear; }

.receipt {
  display: flex; align-items: center; gap: 5px;
  font-size: 11.5px; margin-top: 8px; opacity: .85;
}
.vi { width: 13px; height: 13px; flex: none; }
/* No outline on a delivered file. A green box around a message reads as a form
   that validated, not as something someone sent you; the word on the row and
   the tick on the icon already say it arrived intact. */
.msg.in .bubble.done .receipt { color: var(--ok); opacity: 1; }
.bubble.err { border-color: var(--bad); }
.msg.in .bubble.err .receipt, .msg.in .bubble.err .fmeta { color: var(--bad); }

.acts { display: flex; gap: 8px; margin-top: 11px; flex-wrap: wrap; }
.acts button {
  font: 600 13px ui-sans-serif, system-ui, sans-serif;
  padding: 8px 14px; border-radius: 9px; margin: 0;
  border-color: var(--line);
}
.msg.in .acts button.yes { background: var(--accent); border-color: transparent; color: var(--bg); }

/* ---- Composer ----------------------------------------------------------- */

/* Attach on the left, the field in the middle, send on the right — the shape
   every messaging app has settled on, and the one thumbs already know. */
.composer {
  flex: none; display: flex; align-items: flex-end; gap: 4px;
  padding: 7px 8px;
  padding-bottom: max(7px, env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  background: var(--bg);
}
.composer.off { opacity: .45; pointer-events: none; }
.composer.hot .field,
.field:focus-within { border-color: var(--accent); }

/* What this connection carries, applied to the surface. */
.composer.nofiles .attach { display: none; }
.composer.notext .field, .composer.notext .sendbtn { display: none; }

/* With no message field there is nothing for a bare glyph to sit beside, and a
   lone plus on an empty bar reads as something half-loaded. It becomes the
   control it actually is. */
.composer.notext .attach {
  flex: 1; width: auto; height: 46px; gap: 9px;
  border-radius: 23px; background: var(--track); color: var(--ink);
  font: 600 15px ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
}
.composer.notext .attach::after { content: 'Send a file'; }
.composer.notext .attach .i { width: 21px; height: 21px; }

.attach, .sendbtn {
  flex: none; padding: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 0; background: transparent;
}

/* A bare glyph. The field beside it is the only filled shape down here, and
   that is what keeps the row reading as one control rather than three. */
.attach { width: 44px; height: 44px; color: var(--muted); }
.attach .i { width: 26px; height: 26px; }

/* Send arrives with the first character and leaves with the last. A permanently
   greyed circle is a control that spends most of its life saying no. */
.sendbtn {
  width: 36px; height: 36px; margin: 0 2px 4px;
  background: var(--accent); color: var(--bg);
}
.sendbtn .i { width: 19px; height: 19px; }
.composer:not(.ready) .sendbtn { display: none; }

.field {
  flex: 1; min-width: 0; display: flex; align-items: center;
  min-height: 44px; box-sizing: border-box;
  background: var(--track); border: 1px solid transparent; border-radius: 22px;
  padding: 10px 16px;
  transition: border-color .15s;
}
.field textarea {
  display: block; width: 100%; border: 0; outline: 0; resize: none;
  background: transparent; color: var(--ink);
  font: 15px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Grows with what is typed, then scrolls rather than eating the screen. */
  max-height: 96px;
}
.field textarea::placeholder { color: var(--faint); }

#pick { display: none; }

/* ---- Pictures and video -------------------------------------------------

   Shown at the top of the bubble, above the name, because the picture is the
   message and the filename is the footnote. Width is the bubble's; height is
   capped so one tall photo cannot take the whole conversation with it. */

.shot {
  margin: 1px 0 9px; border-radius: 12px; overflow: hidden;
  background: rgba(128, 128, 128, .16);
}
/* Letterboxed rather than cropped, both of them. Cropping is what a gallery
   does, where the point is the grid; here the point is whether this is the
   right file, and a screenshot cropped to its middle answers nothing. */
.thumb {
  display: block; width: 100%; height: auto; max-height: 380px;
  object-fit: contain;
}
video.thumb { background: #000; }

/* Once there is a picture, the bubble is the picture.

   Nothing is drawn on top of it. Chips over an image are how a gallery marks up
   a thumbnail, and they turn a message back into an item in a list — the thing
   the file row was already doing wrong. What still has to be said goes into a
   caption underneath, where a timestamp would go, and the picture is left to be
   a picture. */
.msg .bubble.media {
  position: relative;
  padding: 0; background: transparent; border: 0;
  min-width: 0; max-width: 268px;
}
.bubble.media .fline,
.bubble.media .fmeta,
.bubble.media .acts { display: none; }

.bubble.media .shot { margin: 0; border-radius: 16px; }
.msg.out .bubble.media .shot { border-bottom-right-radius: 6px; }
.msg.in .bubble.media .shot { border-bottom-left-radius: 6px; }
.shot.tap { cursor: pointer; }

/* The one exception, and only while it is true: during a transfer there is
   nothing else to look at yet. */
.bubble.media .bar2 {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  height: 3px; border-radius: 0; background: rgba(0, 0, 0, .38);
}
.bubble.media .bar2 i { background: #fff; }

/* The caption. Quiet enough to ignore, present enough to be checked. */
.msg.in .bubble.media .receipt,
.msg.out .bubble.media .receipt,
.msg.in .bubble.media.done .receipt {
  position: static; margin: 6px 3px 0; padding: 0;
  background: transparent; color: var(--muted); opacity: 1;
  font-size: 11.5px; font-weight: 500;
}
.msg.out .bubble.media .receipt { justify-content: flex-end; }
.msg.in .bubble.media.err .receipt { color: var(--bad); }

/* Saving is a word in the caption, not a button over the picture. */
.receipt .link {
  font: inherit; background: none; border: 0; padding: 0; margin: 0;
  color: var(--accent); font-weight: 650; cursor: pointer;
}

/* If it will not decode it is a file again, and everything it was hiding
   comes back. */
.bubble.media.plain .fline { display: flex; }
.bubble.media.plain .fmeta, .bubble.media.plain .acts { display: block; }
.bubble.media.plain { padding: 11px 13px 10px; background: var(--track); }

/* ---- Text messages ------------------------------------------------------ */

.bubble.text {
  min-width: 0; padding: 9px 13px 8px;
  font-size: 15px; line-height: 1.4; word-break: break-word;
  white-space: pre-wrap;
}
