/* 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 (ADR-0036). */
: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 panel's own box, inset at `.composer input`'s step: the log's `.row.them
     .said` is bare ink with no panel out here to be bare on. Not `.row.you
     .said`: its tail corner is the gesture for *you said this*. */
  padding: 10px 13px;
  border-radius: var(--shared-radius);
  background: var(--bubble-panel);
  color: var(--bubble-ink);

  /* The edge is the whole answer to "a dark bubble on a dark wallpaper is a
     hole". Over a blown-out ground the ring disappears and the fill carries the
     shape; over a near-black one the reverse. The shadow closes the mid-tone case. */
  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 180ms ease;
  word-wrap: break-word;
  /* Over the sprite, for a bubble clamped onto the head at the ceiling. main.js
     writes a z-index per Instance every tick; 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. Only the child the mode names is drawn, so the two
   can never be on screen together, which as two elements with overlapping
   fades they were. One bubble, not two objects. */

.bubble:not([data-mode="speech"]) .bubble-content,
.bubble:not([data-mode="speech"]) .bubble-more,
.bubble:not([data-mode="thinking"]) .thinking-dots {
  display: none;
}

/* The way to the rest of a turn that did not fit. `data-more` is written only
   when the wrap truncated: a control on every line would make the bubble chrome
   (ADR-0036). A word in the accent colour, not a filled button, for the same reason. */

.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 pointer to a question waiting in Chat: one sentence whose last word is
   the control, so both run inline and the word takes the sentence's size. */
.bubble[data-mode="speech"][data-ask] .bubble-content,
.bubble[data-mode="speech"][data-ask][data-more] .bubble-more {
  display: inline;
}

.bubble[data-ask] .bubble-more {
  margin-top: 0;
  font-size: inherit;
}

/* The tail is the same sandwich as the box: a ring triangle with a fill
   triangle over it, since a lone --bubble-panel spike would vanish on exactly
   the grounds the ring exists for. 10px tall, the gap placeBubble leaves. */
.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);
}

/* #903: below the Character the same sandwich points up. */
.bubble.inverted::before {
  bottom: auto;
  top: -10px;
  border-top: none;
  border-bottom: 10px solid var(--shared-ink);
}

.bubble.inverted::after {
  bottom: auto;
  top: -7px;
  border-top: none;
  border-bottom: 8px solid var(--bubble-panel);
}

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

.thinking-dots span {
  width: 6px;
  height: 6px;
  /* This ellipsis is the chat surface's `.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 one that dims and rises reads as waiting. Opacity and
   transform only, so this composites without repainting. */
@keyframes thinking-bounce {
  0%, 60%, 100% {
    opacity: 0.35;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(-3px);
  }
}
