/* ==========================================================================
   teialoom chat panel.

   THE LOOK IS A TRANSCRIPT, NOT A CHAT APP
   No opposing speech bubbles, no avatars, no tails. Each turn is a small
   uppercase attribution followed by the words, which is how a printed record of
   a conversation is set and is the one visual decision that keeps this from
   looking like every other bolt-on widget. The customer's own turns get a tinted
   block so the eye can find them when scrolling back; that is the only fill in
   the log.

   THE SIGNATURE DETAIL: THE STREAM CARET
   One animation in the whole panel, and it is the thin accent-coloured bar that
   sits at the end of the reply while tokens are still arriving. It earns its
   place because it is the only moment the widget is asking somebody to wait, and
   because it answers the question a spinner cannot: not "is it working" but "is
   it still coming, and where has it got to". It disappears on the last token
   rather than fading, because a fade would keep suggesting that more is coming.
   Nothing else here moves.

   COLOUR
   Paper, card, ink, muted and hairline are fixed. The accent is the client's,
   arriving as a custom property from panel.js, and the text colour on top of it
   is computed from its luminance rather than assumed to be white.
   ========================================================================== */

:root {
  --accent: #12775e;
  --accent-rgb: 18 119 94;
  --on-accent: #ffffff;
  /* The accent where it has to be READ rather than filled: link text and every
     focus ring. Computed in panel.js, see readableText(). */
  --accent-text: #12775e;
  --paper: #faf8f5;
  --card: #ffffff;
  --ink: #1c2620;
  --muted: #5e6b64;
  --line: #dfe3df;
  --danger: #9a2b1e;
  --danger-bg: #fdf3f1;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/**
 * `hidden` MEANS HIDDEN, AND WITHOUT THIS IT DOES NOT.
 *
 * The attribute works by way of a UA rule, `[hidden] { display: none }`, which any
 * author rule setting `display` outranks. So `.dev { display: flex }` quietly beat
 * `devBar.hidden = true` and the failure-state switcher rendered on every visitor's
 * panel as an 18px black strip under the composer. Found by measuring, not by
 * reading: the element reported `hidden` as true the whole time.
 *
 * Anything toggled with `.hidden` in panel.js relies on this line.
 */
[hidden] {
  display: none !important;
}

html,
body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--paper);
  color: var(--ink);
  /* System stack only. A webfont here is a request the client's page did not
     ask for, on a widget sold on the promise that their site opens fast. */
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The trap ends. Zero size, still focusable, never visible. */
.sentinel {
  position: fixed;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

/* ── Header ───────────────────────────────────────────────────────────────── */
.hd {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 14px 12px 18px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  flex: none;
}

.hd__who {
  flex: 1;
  min-width: 0;
}

.hd__name {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
}

.hd__sub {
  margin: 1px 0 0;
  font-size: 13px;
  color: var(--muted);
}

.iconbtn {
  flex: none;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 4px;
  background: none;
  color: var(--muted);
  cursor: pointer;
  padding: 0;
}

.iconbtn:hover {
  background: var(--paper);
  color: var(--ink);
}

.iconbtn svg {
  width: 19px;
  height: 19px;
  display: block;
}

:where(button, textarea, a, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}

/* ── The transparency line ────────────────────────────────────────────────
   14px on a tinted band, not 10px grey in a corner. Contrast measured against
   the band rather than against the paper it sits on. */
.disclose {
  margin: 0;
  padding: 10px 18px;
  background: rgb(var(--accent-rgb) / 7%);
  border-bottom: 1px solid rgb(var(--accent-rgb) / 14%);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink);
  flex: none;
}

.linkish {
  font: inherit;
  font-weight: 600;
  color: var(--accent-text);
  background: none;
  border: 0;
  border-bottom: 1px solid currentColor;
  padding: 0;
  margin-left: 4px;
  cursor: pointer;
}

/* ── The log ──────────────────────────────────────────────────────────────── */
.log {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.turn {
  display: block;
  max-width: 100%;
}

.turn__who {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}

.turn__body {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.turn--you .turn__body {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  border-radius: 0 4px 4px 0;
  padding: 9px 12px;
}

/* The caret. `currentColor` would put it in the text colour; it is deliberately
   the accent, so that the one moving thing in the panel is also the one thing
   wearing the client's brand. */
.caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 1px;
  vertical-align: text-bottom;
  background: var(--accent);
  animation: blink 1.1s steps(2, start) infinite;
}

@keyframes blink {
  to {
    visibility: hidden;
  }
}

/* Waiting for the first token. Three dots, same rhythm as the caret. */
.dots {
  display: inline-flex;
  gap: 4px;
  padding: 3px 0;
}

.dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--muted);
  animation: pulse 1.2s ease-in-out infinite;
}

.dots i:nth-child(2) {
  animation-delay: 0.15s;
}

.dots i:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes pulse {
  0%, 60%, 100% { opacity: 0.28; }
  30% { opacity: 1; }
}

/* ── Trouble ──────────────────────────────────────────────────────────────
   Each failure gets its own words, and they all get the same treatment: a block
   the reader cannot mistake for a reply, with the next thing to try inside it. */
.notice {
  margin: 0;
  padding: 12px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 0 4px 4px 0;
  font-size: 14px;
}

.notice--bad {
  background: var(--danger-bg);
  border-color: #eccec7;
  border-left-color: var(--danger);
}

.notice__title {
  display: block;
  font-weight: 600;
  margin-bottom: 2px;
}

.notice .linkish {
  margin-left: 0;
}

.retry {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  margin-top: 8px;
  padding: 7px 14px;
  background: none;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
}

.retry:hover {
  background: var(--paper);
  border-color: var(--muted);
}

/* ── Handoff, shown when the cap is hit or a human is asked for ───────────── */
.handoff {
  flex: none;
  padding: 16px 18px;
  background: var(--card);
  border-top: 1px solid var(--line);
}

.handoff h2 {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 600;
}

.handoff p.lead {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--muted);
}

.handoff .row {
  display: grid;
  gap: 10px;
}

.handoff label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 4px;
}

.handoff input,
.handoff textarea {
  font: inherit;
  /* 16px so iOS does not zoom the panel when a field takes focus. */
  font-size: 16px;
  width: 100%;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 10px 12px;
}

.handoff textarea {
  min-height: 74px;
  resize: vertical;
}

.handoff .err {
  display: block;
  margin-top: 4px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--danger);
}

.field--bad input,
.field--bad textarea {
  border-color: var(--danger);
}

.handoff .acts {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-top: 4px;
}

.btn {
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--accent);
  border-radius: 3px;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.btn[disabled] {
  opacity: 0.55;
  cursor: default;
}

.btn--quiet {
  background: none;
  color: var(--ink);
  border-color: var(--line);
}

.btn--quiet:hover {
  background: var(--paper);
  border-color: var(--muted);
}

/* ── Composer ─────────────────────────────────────────────────────────────── */
.composer {
  flex: none;
  padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
  background: var(--card);
  border-top: 1px solid var(--line);
}

.composer textarea {
  font: inherit;
  font-size: 16px;
  width: 100%;
  max-height: 132px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 10px 12px;
  resize: none;
  overflow-y: auto;
}

.composer textarea[disabled] {
  color: var(--muted);
}

.composer__foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 8px;
}

/* Hidden until it is close to mattering. A counter from zero is a widget
   telling somebody how much they have left to say before they have started. */
.count {
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  visibility: hidden;
}

.count[data-near] {
  visibility: visible;
}

.count[data-over] {
  color: var(--danger);
  font-weight: 600;
}

.send {
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  min-height: 42px;
  padding: 0 18px;
  border: 1px solid var(--accent);
  border-radius: 3px;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
}

.send[disabled] {
  opacity: 0.5;
  cursor: default;
}

/* ── The failure-state switcher, ?dev=1 only ──────────────────────────────── */
.dev {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 9px 14px;
  background: #1c2620;
  color: #d7ded9;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 11px;
  align-items: center;
}

.dev button {
  font: inherit;
  padding: 3px 8px;
  border: 1px solid #3c4a43;
  border-radius: 2px;
  background: none;
  color: #d7ded9;
  cursor: pointer;
}

.dev button[data-on] {
  background: #d7ded9;
  color: #1c2620;
}

/* ── 320px ────────────────────────────────────────────────────────────────── */
@media (max-width: 360px) {
  .hd,
  .disclose,
  .log,
  .handoff {
    padding-inline: 14px;
  }

  .hd__name {
    font-size: 15px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .caret,
  .dots i {
    animation: none;
  }

  /* Still needs to read as "not finished". A static bar does that; a static
     row of three faded dots does not, so they go to full strength instead. */
  .dots i {
    opacity: 0.7;
  }
}
