/* Opaque type, shape and hue the Speech bubble may share with the default Chat
   panel. Chrome, layout and alpha stay in chat-ui.css so a future Chat UI
   cannot repaint Character expression (#545, ADR-0019). */
:root {
  --shared-panel: #14171e;
  --shared-ink: #e8ebf2;
  --shared-accent: #5cc9b5;
  --shared-radius: 12px;
  --shared-type-size: 13.5px;
  --shared-type-leading: 1.55;
}
.bubble {
  --bubble-panel: var(--shared-panel);
  --bubble-ink: var(--shared-ink);
  --bubble-accent: var(--shared-accent);

  position: absolute;
  max-width: 260px;

  /* The Character's line in the chat log is `.row.them .said`: bare ink at
     line-height 1.55, with no fill, radius or padding to copy — there is no
     panel out here to be bare on. So the box is the panel's own, inset at
     `.composer input`'s step, which is what that stylesheet already uses for a
     bordered box holding one piece of text. Deliberately not `.row.you .said`:
     its --chat-radius-tail corner is the gesture for *you said this*, so it
     does not travel and the corner stays uniform. */
  padding: 10px 13px;
  border-radius: var(--shared-radius);
  background: var(--bubble-panel);
  color: var(--bubble-ink);

  /* The edge, and the whole answer to "a dark bubble on a dark wallpaper is a
     hole". Fill and ring fail on opposite grounds: over a blown-out ground the
     ring disappears and the dark fill carries the shape; over a near-black one
     the fill vanishes and the ring carries it. No single ground hides both,
     which a single-value edge cannot promise. The shadow closes the mid-tone
     case, where neither is at full strength. */
  border: 2px solid var(--shared-ink);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.55);

  font-family: system-ui, -apple-system, sans-serif; /* chat-ui.css --chat-sans */
  font-size: var(--shared-type-size);
  line-height: var(--shared-type-leading);

  pointer-events: none;
  opacity: 0;
  transition: opacity 300ms ease;
  word-wrap: break-word;
  /* Over the sprite, which is what the ceiling case needs now that the flip is
     gone. main.js writes a z-index per Instance every tick and puts the bubble
     on the higher of that Instance's two levels; this is the fallback for a
     bubble drawn before the first tick has stacked it. */
  z-index: 1;
}

.bubble.visible {
  opacity: 1;
}

/* One element, two modes: the Character is either saying something or waiting
   to say something. Only the child the mode names is drawn, so the two can
   never be on screen together — which as two elements anchored to the same
   head they were, for as long as their fades overlapped.

   One bubble, not two objects that happen to appear in the same place: box,
   fill, edge and tail are the same in both modes and the ellipsis alone carries
   the difference. That reverses #119's round thought silhouette and its trail
   of shrinking discs, and gives up the glance they bought — the argument is in
   ADR-0013's "One bubble for both states". */
.bubble:not([data-mode="speech"]) .bubble-content,
.bubble:not([data-mode="speech"]) .bubble-more,
.bubble:not([data-mode="thinking"]) .thinking-dots {
  display: none;
}

/* #547: the way to the rest of a turn that did not fit. `data-more` is written
   by main.js only when the wrap truncated, so a line that fits shows nothing at
   all — the bubble is the Character speaking, and a control on every line would
   make it chrome (ADR-0019).

   A word in the accent colour rather than a filled button, for the same reason:
   the accent is already what this surface uses for "the app is saying
   something" (the thinking ellipsis), and a raised button would be the chat
   surface's furniture standing on the desktop. */
.bubble:not([data-more]) .bubble-more {
  display: none;
}

.bubble-more {
  display: block;
  margin-top: 6px;
  padding: 0;
  border: none;
  background: none;
  color: var(--bubble-accent);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
  /* The one place the overlay takes a click that is not the art. The frame loop
     is told this rectangle (`overlay_hotspots`) so the window stops passing
     clicks through while the cursor is over it. */
  pointer-events: auto;
}

.bubble-content {
  white-space: pre-wrap;
}

/* The tail is the same sandwich as the box: a ring triangle with a fill
   triangle over it. A lone --bubble-panel spike would vanish on exactly the
   grounds the ring exists for. 10px tall, which is the gap placeBubble leaves
   over the sprite's head, and drawn in both modes because both modes are the
   same bubble. */
.bubble::before,
.bubble::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 0;
  height: 0;
  transform: translateX(calc(-50% + var(--tail-offset, 0px)));
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
}

.bubble::before {
  bottom: -10px;
  border-top: 10px solid var(--shared-ink);
}

.bubble::after {
  bottom: -7px;
  border-left-width: 8px;
  border-right-width: 8px;
  border-top: 8px solid var(--bubble-panel);
}

.thinking-dots {
  display: flex;
  gap: 5px;
  align-items: center;
}

.thinking-dots span {
  width: 6px;
  height: 6px;
  /* The chat surface's own `.caret` is --chat-accent blinking in the log while
     it waits. This ellipsis is that caret, so the two surfaces say *waiting*
     the same way — and three small saturated marks on an opaque ground read on
     any wallpaper. */
  background: var(--bubble-accent);
  border-radius: 50%;
  animation: thinking-bounce 1.2s infinite ease-in-out both;
}

.thinking-dots span:nth-child(1) {
  animation-delay: -0.24s;
}

.thinking-dots span:nth-child(2) {
  animation-delay: -0.12s;
}

/* A dip and a lift, never `scale(0)`: a dot that leaves reads as art failing
   to load, where a dot that dims and rises reads as waiting. Opacity and
   transform only, so this composites without repainting — the overlay sits on
   top of everything the user is doing. */
@keyframes thinking-bounce {
  0%, 60%, 100% {
    opacity: 0.35;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(-3px);
  }
}
