/* web/styles.css
 *
 * Support desk, the web surface for Stader and Kelp.
 *
 * Direction: a ledger, not a chat toy. People arrive here mid transaction with money on
 * the line, so the surface reads like a running log of timestamped entries: monospace for
 * everything the system asserts (times, ids, hashes, counters), a neutral grotesque for
 * the words a person actually reads, and colour reserved for status rather than
 * decoration. The repeated motif is the tick meter: it is the wordmark, and eight ticks burn
 * down while a case waits for a human. The agent's own thinking indicator is the orb, because a
 * 13s wait needs something that reads as considered rather than as a progress bar. Same grammar, 
 * clocks.
 *
 * Colour is a status ramp, not a palette: signal (answered, healthy), pending (waiting on
 * a human), urgent (security, or an SLA about to break). Brand tint appears only where it
 * says which product you are asking about.
 *
 * Both themes are first class. The default follows the operating system, and the toggle
 * in the masthead wins over it in either direction.
 */

/* ------------------------------------------------------------------ tokens */

/* ------------------------------------------------------------------ tokens
 *
 * The brand tokens live in tokens.css (loaded first), copied from the kelp-design system.
 * This block only maps THIS sheet's semantic names onto them, so the 1200 lines below did
 * not have to change when the brand landed. Anything not in the brand system (the status
 * ramp: pending, urgent) is derived from it here rather than invented next to it.
 */

:root {
  color-scheme: light dark;

  /* Geist for everything a person reads, Clash Display (Gilroy on Stader) for the marks
     and headings, mono ONLY for machine facts: timestamps, ids, hashes, counters. */
  --font-text: var(--font-sans);

  /* One height for the composer's textarea and its Send button. Without it the textarea
     was 24px and the button ~36px, both bottom aligned, so a single line of text sat about
     6px below the button's centre and the whole box looked off. */
  --control-h: 40px;

  --radius-sm: 6px;
  --radius-md: var(--radius-chip);
  --radius-lg: var(--radius-card);

  /* One shell width drives the masthead AND the chat column. Two different widths is what
     put the header and the conversation on different centre lines. */
  --shell-chat: var(--shell);

  --text-dim: var(--text-soft);
  --text-faint: var(--text-muted);

  /* Status ramp. Signal is the brand accent; the other two are tuned to sit beside it. */
  --signal: var(--accent);
  --signal-soft: color-mix(in srgb, var(--accent) 12%, transparent);
  --pending: #b26a09;
  --pending-soft: color-mix(in srgb, #b26a09 12%, transparent);
  --urgent: #c1442f;
  --urgent-soft: color-mix(in srgb, #c1442f 12%, transparent);
  --brand: var(--accent);
  --shadow: var(--shadow-card);
}

:root[data-theme='dark'],
:root[data-theme='dark'][data-brand='stader'] {
  --pending: #f0b357;
  --pending-soft: color-mix(in srgb, #f0b357 16%, transparent);
  --urgent: #ff8f7e;
  --urgent-soft: color-mix(in srgb, #ff8f7e 16%, transparent);
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.03), 0 14px 34px rgba(0, 0, 0, 0.42);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --pending: #f0b357;
    --pending-soft: color-mix(in srgb, #f0b357 16%, transparent);
    --urgent: #ff8f7e;
    --urgent-soft: color-mix(in srgb, #ff8f7e 16%, transparent);
    --shadow: 0 1px 0 rgba(255, 255, 255, 0.03), 0 14px 34px rgba(0, 0, 0, 0.42);
  }
}

/* ------------------------------------------------------------------ base */

html[data-view='queue'] .masthead-row {
  max-width: var(--shell-queue);
}

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

html,
body {
  margin: 0;
  padding: 0;
}

body {
  /* HEIGHT, not min-height. This is the whole scroll architecture in one line.
     With min-height the body grows past the viewport as messages arrive, .stage and
     .transcript grow with it (flex:1 of an unbounded parent), the transcript therefore
     never overflows and never becomes a scroll container, the PAGE scrolls instead, and
     the sticky composer parks over the newest messages. Bounding the height here is what
     makes .transcript the real scroller and keeps the composer below it in flow. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(120% 60% at 50% -10%, var(--bg-grain) 0%, var(--bg) 62%),
    var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

[hidden] {
  display: none !important;
}

button,
input,
textarea {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

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

.skip-link {
  position: absolute;
  left: var(--space-3);
  top: -100px;
  z-index: 20;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--signal);
  border-radius: var(--radius-md);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  text-decoration: none;
  transition: top 140ms ease;
}

.skip-link:focus {
  top: var(--space-3);
}

/* ------------------------------------------------------------------ masthead */

.masthead {
  /* Never squeezed by the flex chain: the stage takes the remaining space, not this. */
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

.masthead-row {
  /* Same centre line as the conversation. The queue view is wider, so the header widens
     with it rather than the header always sitting on the widest possible axis. */
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.masthead-row-top {
  min-height: 54px;
}

.masthead-row-bottom {
  min-height: 40px;
  justify-content: space-between;
  border-top: 1px solid var(--line);
}

.mark {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-right: auto;
  min-width: 0;
}

/* The tick motif doubles as the wordmark. Five ticks, rising like a meter. */
.mark-glyph {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 18px;
}

.mark-glyph span {
  width: 3px;
  border-radius: 1px;
  background: var(--brand);
  transition: background 200ms ease;
}

.mark-glyph span:nth-child(1) { height: 6px; opacity: 0.45; }
.mark-glyph span:nth-child(2) { height: 10px; opacity: 0.6; }
.mark-glyph span:nth-child(3) { height: 14px; opacity: 0.78; }
.mark-glyph span:nth-child(4) { height: 18px; }
.mark-glyph span:nth-child(5) { height: 11px; opacity: 0.6; }

.mark-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.mark-name {
  /* the wordmark carries the brand face */
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 500;
  /* Clash Display sets tight. At -0.02em the word space in "support desk" disappeared and
     it read as one word. Display faces need their tracking set per size, not inherited. */
  letter-spacing: 0;
  word-spacing: 0.12em;
  line-height: 1.15;
  white-space: nowrap;
}

.mark-sub {
  font-family: var(--font-sans);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
  line-height: 1.6;
}

.disclosure {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 5px var(--space-3) 5px var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  white-space: nowrap;
}

.disclosure-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--signal);
  box-shadow: 0 0 0 3px var(--signal-soft);
}

.icon-button {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, transform 140ms ease;
}

.icon-button:hover {
  color: var(--text);
  border-color: var(--line-strong);
}

.icon-button:active {
  transform: translateY(1px);
}

.theme-glyph {
  font-size: 15px;
  line-height: 1;
}

.brand-switch {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
}

.brand-option {
  padding: 3px var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-faint);
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: color 140ms ease, background 140ms ease;
}

.brand-option:hover {
  color: var(--text-dim);
}

.brand-option.is-on {
  background: var(--surface-2);
  color: var(--brand);
}

.tabs {
  display: inline-flex;
  gap: var(--space-1);
}

.tab {
  position: relative;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease;
}

.tab:hover {
  color: var(--text-dim);
}

.tab.is-on {
  color: var(--text);
  border-bottom-color: var(--brand);
}

.tab-badge {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--urgent-soft);
  color: var(--urgent);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ stage */

.stage {
  flex: 1;
  display: flex;
  width: 100%;
  min-height: 0;
}

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

.panel:focus {
  outline: none;
}

.panel-chat {
  max-width: var(--shell-chat);
  padding: 0 var(--space-4);
}

.panel-queue {
  max-width: var(--shell-queue);
  padding: var(--space-5) var(--space-4) var(--space-6);
  overflow-y: auto;
  gap: var(--space-5);
}

/* ------------------------------------------------------------------ transcript */

.transcript {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  margin: 0;
  padding: var(--space-5) 0 var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* A short conversation sits on the composer instead of floating in a void, and grows
   upward from there. margin-top on the first child rather than justify-content, which
   makes overflowing content unreachable at the top in some browsers. */
.transcript > :first-child {
  margin-top: auto;
}

.msg {
  --rule: var(--line-strong);
  position: relative;
  padding-left: var(--space-4);
  border-left: 2px solid var(--rule);
  animation: entry 240ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.msg-agent {
  --rule: var(--brand);
}

.msg-user {
  --rule: var(--line-strong);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 2px solid var(--line-strong);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.msg-notice {
  --rule: var(--pending);
}

.msg-error {
  --rule: var(--urgent);
}

.msg-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.msg-who {
  color: var(--rule);
  font-weight: 600;
}

.msg-stamp {
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
}

.msg-body {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.msg-body .word {
  animation: word-in 300ms ease both;
  animation-delay: min(calc(var(--i) * 14ms), 620ms);
}

/* The case receipt. It is the one thing a worried person screenshots, so it carries the
   identifiers a mod can act on and nothing else. */
.case-chit {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-left: 2px solid var(--pending);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  background: var(--surface);
  display: grid;
  gap: var(--space-1);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

.case-chit.is-urgent {
  border-left-color: var(--urgent);
}

.chit-row {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  flex-wrap: wrap;
}

.chit-label {
  color: var(--text-faint);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 10px;
}

.chit-value {
  color: var(--text);
  overflow-wrap: anywhere;
}

/* ------------------------------------------------------------------ working meter */

.working {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0 var(--space-3) var(--space-4);
  border-left: 2px solid var(--brand);
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  text-transform: lowercase;
}

/*
 * Thinking orb. Eight dots on a circle, each breathing on a phase offset, the whole ring turning
 * slowly. Replaces five bouncing bars.
 *
 * WHY THIS AND NOT THE npm ONE. `thinking-orbs` is MIT and good, but it peer-depends on React 18,
 * and web/ is static HTML with no framework, no bundler and no build step. Adding React and a
 * build pipeline to render a loading indicator on a support desk is a bad trade, and this repo
 * runs one runtime dependency on purpose. The idea is free; forty lines of CSS is cheaper than a
 * toolchain.
 *
 * WHY IT EARNS ITS PLACE. Otto's p50 turn is ~13s. The UX research is blunt: with a status line
 * users tolerate ten seconds or more, with a blank screen patience runs out in two to three. This
 * indicator plus the staged label is what makes the wait survivable while the latency waterfall
 * gets fixed underneath.
 */
.orb {
  position: relative;
  display: inline-block;
  width: 18px;
  height: 18px;
  flex: none;
  animation: orb-spin 3.4s linear infinite;
}

.orb i {
  position: absolute;
  inset: 0;
}

/*
 * Angles and phases are written out rather than derived from a --n custom property with calc().
 * The calc() form silently produced `transform: none` on every dot, because an unresolved var()
 * invalidates the whole declaration at computed-value time and it falls back to the initial value.
 * All eight dots then stacked in one spot and the "orb" rendered as a single dot. Verified with
 * getComputedStyle before rewriting: `rotations distinct: 1`.
 */
.orb i:nth-child(1) { transform: rotate(0deg); }
.orb i:nth-child(2) { transform: rotate(45deg); }
.orb i:nth-child(3) { transform: rotate(90deg); }
.orb i:nth-child(4) { transform: rotate(135deg); }
.orb i:nth-child(5) { transform: rotate(180deg); }
.orb i:nth-child(6) { transform: rotate(225deg); }
.orb i:nth-child(7) { transform: rotate(270deg); }
.orb i:nth-child(8) { transform: rotate(315deg); }

.orb i:nth-child(1)::before { animation-delay: 0s; }
.orb i:nth-child(2)::before { animation-delay: -0.2s; }
.orb i:nth-child(3)::before { animation-delay: -0.4s; }
.orb i:nth-child(4)::before { animation-delay: -0.6s; }
.orb i:nth-child(5)::before { animation-delay: -0.8s; }
.orb i:nth-child(6)::before { animation-delay: -1s; }
.orb i:nth-child(7)::before { animation-delay: -1.2s; }
.orb i:nth-child(8)::before { animation-delay: -1.4s; }

.orb i::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: var(--brand);
  animation: orb-breathe 1.6s ease-in-out infinite;
}

/* ------------------------------------------------------------------ composer */

.composer {
  position: sticky;
  bottom: 0;
  display: flex;
  gap: var(--space-3);
  align-items: flex-end;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
  transition: border-color 160ms ease;
}

.composer:focus-within {
  border-color: var(--line-strong);
}

.composer textarea {
  flex: 1;
  /* Height matches the Send button at one line, and the vertical padding centres that
     line inside it. Growing past one line extends upward, which is why the composer
     stays align-items: flex-end. */
  min-height: var(--control-h);
  max-height: 40vh;
  padding: 8px 0;
  border: 0;
  background: transparent;
  resize: none;
  font-family: var(--font-text);
  font-size: 15px;
  line-height: 1.55;
}

.composer textarea::placeholder {
  color: var(--text-faint);
}

.composer textarea:focus {
  outline: none;
}

.composer-side {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.composer-count {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.send {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-h);
  gap: var(--space-2);
  padding: 0 var(--space-4);
  border: 1px solid var(--brand);
  border-radius: var(--radius-md);
  background: var(--brand);
  color: var(--bg);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: opacity 140ms ease, transform 140ms ease;
}

.send-arrow {
  transition: transform 160ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.send:hover:not(:disabled) .send-arrow {
  transform: translateX(3px);
}

.send:active:not(:disabled) {
  transform: translateY(1px);
}

.send:disabled {
  cursor: not-allowed;
  border-color: var(--line);
  background: transparent;
  color: var(--text-faint);
}

.composer-note {
  margin: var(--space-2) 0 var(--space-4);
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.01em;
  color: var(--text-soft);
  text-align: center;
}

/* ------------------------------------------------------------------ queue */

.queue-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: flex-end;
  justify-content: space-between;
}

.queue-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.03em;
}

.queue-sub {
  margin: var(--space-1) 0 0;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.queue-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.field-label {
  font-family: var(--font-sans);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.field input {
  width: 11rem;
  padding: 6px var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 12px;
}

.field input:focus {
  outline: none;
  border-color: var(--line-strong);
}

.ghost-button,
.danger-button {
  padding: 7px var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 12px;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}

.ghost-button:hover {
  color: var(--text);
  border-color: var(--text-faint);
}

.danger-button {
  border-color: var(--urgent);
  color: var(--urgent);
}

.danger-button:hover {
  background: var(--urgent-soft);
  color: var(--urgent);
  border-color: var(--urgent);
}

.killswitch {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
}

.killswitch-state {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-dim);
}

.killswitch-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--signal);
  box-shadow: 0 0 0 3px var(--signal-soft);
}

.killswitch-dot.is-stopped {
  background: var(--urgent);
  box-shadow: 0 0 0 3px var(--urgent-soft);
}

.killswitch-buttons {
  display: flex;
  gap: var(--space-2);
}

.cases {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.case {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-left: 2px solid var(--pending);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  background: var(--surface);
  display: grid;
  gap: var(--space-3);
  animation: entry 240ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.case.is-urgent {
  border-left-color: var(--urgent);
}

.case.is-acked {
  border-left-color: var(--signal);
}

.case.is-focus {
  border-color: var(--brand);
}

.case-top {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: baseline;
  justify-content: space-between;
}

.case-id {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text);
  overflow-wrap: anywhere;
}

.case-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.tag {
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.tag-urgent {
  background: var(--urgent-soft);
  color: var(--urgent);
}

.tag-acked {
  background: var(--signal-soft);
  color: var(--signal);
}

.case-summary {
  margin: 0;
  font-size: 14px;
  color: var(--text);
  overflow-wrap: anywhere;
}

.case-next {
  margin: 0;
  font-size: 13px;
  color: var(--text-dim);
}

.case-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}

.case-clock {
  display: grid;
  gap: 5px;
  min-width: 12rem;
  flex: 1;
}

.sla-meter {
  display: flex;
  gap: 2px;
}

.sla-meter i {
  flex: 1;
  height: 4px;
  border-radius: 1px;
  background: var(--line-strong);
  opacity: 0.5;
  transition: background 300ms ease, opacity 300ms ease;
}

.sla-meter i.is-on {
  opacity: 1;
}

.sla-meter i.is-on {
  background: var(--signal);
}

.sla-meter.is-warn i.is-on {
  background: var(--pending);
}

.sla-meter.is-breach i.is-on {
  background: var(--urgent);
}

.sla-meter.is-breach i:last-child {
  animation: pulse 2.4s ease-in-out infinite;
}

.case-clock-label {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.case-clock-label.is-breach {
  color: var(--urgent);
}

.case-links {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.case-permalink {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-faint);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}

.case-permalink:hover {
  color: var(--text-dim);
  border-bottom-color: var(--line-strong);
}

/* Human takeover. Sending a reply here stands the bot down on that conversation. */
.case-takeover {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--line);
}

.case-reply {
  width: 100%;
  min-height: 56px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  line-height: 1.5;
  resize: vertical;
}

.case-reply::placeholder {
  color: var(--text-faint);
}

.case-reply:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 1px;
}

.empty {
  padding: var(--space-6) var(--space-4);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-md);
  text-align: center;
  color: var(--text-faint);
  font-family: var(--font-sans);
  font-size: 12px;
}

/* ------------------------------------------------------------------ analytics */

.analytics {
  display: grid;
  gap: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.analytics-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.analytics-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.analytics-window {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-faint);
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--space-3);
}

.stat {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  display: grid;
  gap: var(--space-1);
}

.stat-label {
  font-family: var(--font-sans);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.stat-value {
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.stat-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.breakdowns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--space-4);
}

.breakdown {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  overflow: hidden;
}

.breakdown h4 {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.breakdown-scroll {
  overflow-x: auto;
}

.breakdown table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.breakdown th,
.breakdown td {
  padding: var(--space-2) var(--space-4);
  text-align: right;
  white-space: nowrap;
  border-bottom: 1px solid var(--line);
}

.breakdown th:first-child,
.breakdown td:first-child {
  text-align: left;
}

.breakdown th {
  color: var(--text-faint);
  font-weight: 400;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.breakdown tr:last-child td {
  border-bottom: 0;
}

/* ------------------------------------------------------------------ footnote */

.footnote {
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--space-3) var(--space-4) var(--space-5);
  font-family: var(--font-sans);
  /* 10px on the faintest token failed contrast badly. Legal and safety copy is the last
     thing that should be hard to read. */
  font-size: 12px;
  line-height: 1.7;
  letter-spacing: 0.01em;
  color: var(--text-soft);
  text-align: center;
}

/* ------------------------------------------------------------------ motion */

@keyframes entry {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes word-in {
  from {
    opacity: 0;
    filter: blur(2px);
  }
  to {
    opacity: 1;
    filter: none;
  }
}

@keyframes orb-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Opacity carries the motion, not size, so the ring never looks like it is wobbling. */
@keyframes orb-breathe {
  0%,
  100% {
    opacity: 0.32;
    transform: scale(0.82);
  }
  45% {
    opacity: 1;
    transform: scale(1.08);
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .msg-body .word {
    animation-delay: 0ms !important;
  }
  .orb {
    animation: none;
  }

  .orb i::before {
    background: var(--brand);
    opacity: 0.55;
    animation: none;
    transform: none;
  }
}

/* ------------------------------------------------------------------ narrow */

@media (max-width: 40rem) {
  body {
    font-size: 15px;
  }
  .masthead-row {
    padding: 0 var(--space-3);
    gap: var(--space-2);
  }
  .masthead-row-top {
    min-height: 48px;
  }
  .disclosure {
    padding: 4px var(--space-2);
    font-size: 10px;
  }
  .panel-chat {
    padding: 0 var(--space-3);
  }
  .panel-queue {
    padding: var(--space-4) var(--space-3) var(--space-5);
  }
  .transcript {
    padding-top: var(--space-4);
  }
  .field input {
    width: 100%;
  }
  .field {
    flex: 1;
    min-width: 8rem;
  }
  .queue-actions {
    width: 100%;
  }
  .stat-value {
    font-size: 22px;
  }
  /* Enter and Shift plus Enter is desktop information. A phone keyboard has its own
     return key, and the small print below the composer is already two lines long. */
  .composer-note {
    display: none;
  }
}

@media (max-width: 23.5rem) {
  .mark-name {
    font-size: 14px;
  }
  .mark-sub {
    display: none;
  }
  .disclosure {
    font-size: 9px;
    letter-spacing: 0;
  }
  .send-label {
    display: none;
  }
  .send {
    padding: var(--space-2);
  }
}

/* Rating control. Quiet until the answer is settled: it asks for an opinion, it does not
   demand one. The label carries the question so the glyphs never have to. */
.msg-rate {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  opacity: 0;
  transition: opacity 160ms ease;
}
.msg-agent:hover .msg-rate,
.msg-rate:focus-within,
.msg-rate[data-done] {
  opacity: 1;
}
.msg-rate-label {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.msg-rate-btn {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-chip);
  cursor: pointer;
  font-size: 0.7rem;
  line-height: 1;
  padding: 0.28rem 0.42rem;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.msg-rate-btn:hover {
  color: var(--text);
  border-color: var(--line-strong);
}
.msg-rate-btn[aria-pressed='true'] {
  color: var(--text);
  border-color: var(--accent);
  background: var(--surface-2);
}
@media (prefers-reduced-motion: reduce) {
  .msg-rate { transition: none; }
}
/* A touch device has no hover, so a hover-only control is an invisible control. Support chat skews
   mobile, so gating the feedback affordance behind :hover would have hidden it from most of the
   people we are asking. Shown outright wherever hover does not exist. */
@media (hover: none) {
  .msg-rate { opacity: 1; }
}
