/* CIB assistant widget — colours and logo from cibeg.com: flat CIB blue and
   navy, white cards, square-ish 4px corners, orange used only as a highlight.
   Every colour lives in the tokens below; rules never carry a raw hex. */
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@400;600;700&display=swap");
:root {
  /* Brand, sampled from cibeg.com */
  --cib-blue: #1858a8;
  --cib-navy: #032d62;
  --cib-divider: #124481;
  --cib-orange: #f58423;
  --blue-rgb: 24, 88, 168;
  --shadow-rgb: 3, 45, 98;

  /* Neutrals */
  --text: #333333;
  --muted: #6b7280;
  --faint: #94a3b8;
  --faint-strong: #64748b;
  --bg: #f3f5f8;
  --surface: #ffffff;
  --border: #dde3ec;
  --border-strong: #c9d3e1;
  --tint: #e8eef7;
  --tint-2: #d8e3f2;

  /* Status */
  --online: #a8e6b0;
  --warning: #f5d76e;
  --offline: #f0a5a5;
  --danger: #e5484d;
  --danger-rgb: 229, 72, 77;
  --danger-text: #b3261e;
  --danger-deep: #8b1f1f;
  --danger-bg: #fff4f4;
  --danger-border: #f2caca;
  --rec-bg: #fff2f2;
  --rec-border: #f6dcdc;

  /* Shape */
  --radius: 8px;
  --radius-sm: 4px;
  --shadow: 0 10px 30px rgba(var(--shadow-rgb), 0.22);
  --font: "IBM Plex Sans Arabic", "Segoe UI", Tahoma, sans-serif;
  font-size: 16px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* No grey tap-flash on mobile; consistent press feedback lives on :active below. */
button, textarea, input { -webkit-tap-highlight-color: transparent; }
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* Keyboard focus: the one place the brand orange appears in the widget. */
button:focus-visible, a:focus-visible, input:focus-visible, .chip:focus-visible {
  outline: 2px solid var(--cib-orange);
  outline-offset: 2px;
}

body {
  font-family: var(--font);
  background: var(--cib-blue);
  min-height: 100vh;
  color: var(--surface);
}

/* ---- demo hero ---- */
.hero {
  max-width: 760px;
  margin: 0 auto;
  padding: 12vh 24px 0;
  text-align: center;
}
.hero-logo { display: block; margin: 0 auto 28px; }
.hero-badge {
  display: inline-block;
  border: 1px solid rgba(255, 255, 255, 0.5);
  color: var(--surface);
  border-radius: var(--radius-sm);
  padding: 6px 18px;
  font-size: 0.85rem;
  letter-spacing: 1px;
  margin-bottom: 24px;
}
.hero h1 { font-size: clamp(1.6rem, 4vw, 2.6rem); line-height: 1.35; }
.hero p { margin-top: 18px; color: rgba(255, 255, 255, 0.82); line-height: 1.8; }
.hero-en { direction: ltr; font-size: 0.9rem; opacity: 0.75; }
/* White with blue text, like the "Log in" button on cibeg.com. */
.hero-cta {
  margin-top: 32px;
  background: var(--surface);
  color: var(--cib-blue);
  border: none;
  font-family: inherit;
  font-weight: 700;
  font-size: 1rem;
  padding: 14px 34px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease;
}
.hero-cta:hover { background: var(--tint); }

/* ---- floating toggle ---- */
/* Physical `right`, not inset-inline-end: the widget must stay on the right
   side of the screen in both Arabic (RTL) and English (LTR). */
#chat-toggle {
  position: fixed;
  right: 24px;
  bottom: 24px;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  border: none;
  background: var(--cib-blue);
  font-size: 1.6rem;
  cursor: pointer;
  /* White ring so the button reads on a blue page as well as a white one. */
  box-shadow: 0 0 0 3px var(--surface), var(--shadow);
  z-index: 40;
  transition: transform 0.15s ease, background 0.15s ease;
}
#chat-toggle:hover { transform: scale(1.08); background: var(--cib-navy); }
#chat-toggle:active { transform: scale(0.92); }

/* ---- widget ---- */
#chat-widget {
  position: fixed;
  right: 24px;
  bottom: 100px;
  width: 400px;
  max-width: calc(100vw - 32px);
  height: 600px;
  max-height: calc(100vh - 130px);
  background: var(--bg);
  color: var(--text);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: 50;
  animation: widget-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
#chat-widget.closing { animation: widget-out 0.16s ease forwards; }
@keyframes widget-in {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes widget-out {
  to { opacity: 0; transform: translateY(14px) scale(0.98); }
}
/* While a finger is dragging the widget, JS drives transform directly — the
   open animation has already finished, so there's no conflict. */
#chat-widget.dragging { animation: none; transition: none; }
#chat-widget.hidden, .hidden { display: none !important; }

/* Grabber bar for swipe-to-close — only meaningful on the fullscreen mobile
   layout, so it's hidden until the mobile media query reveals it. */
#drag-handle { display: none; }

/* The CIB logo (cib-logo.png, cropped from cibeg.com, drawn on CIB blue) set
   as a background, so the extension's markup needs no change and the "CIB"
   text stays in the DOM, transparent. A relative url() resolves against this
   stylesheet, which in the extension is the chrome-extension:// origin; the
   image is listed in web_accessible_resources for that reason. */
.bot-avatar, #login-avatar {
  background: var(--cib-blue) url("cib-logo.png") center / contain no-repeat;
  color: transparent;
  border-radius: var(--radius-sm);
  user-select: none;
}

/* Blue bar over a navy strip: the site's two header bands, compressed. */
#chat-header {
  background: var(--cib-blue);
  border-bottom: 4px solid var(--cib-navy);
  color: var(--surface);
  padding: 14px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  touch-action: none; /* header is the swipe-to-close surface on mobile */
}
.header-title { display: flex; align-items: center; gap: 10px; }
.bot-avatar { width: 66px; height: 31px; }
#header-name { font-weight: 700; font-size: 0.95rem; }
#header-status { font-size: 0.75rem; color: var(--online); }
/* Backend still loading its knowledge base / warmup failed — set from
   renderStatus() in chat.js, which also locks the composer. */
#header-status.warming { color: var(--warning); }
#header-status.offline { color: var(--offline); }
.header-actions { display: flex; gap: 6px; }
.header-actions button {
  background: rgba(255,255,255,0.12);
  border: none; color: var(--surface);
  width: 34px; height: 34px;
  border-radius: var(--radius-sm); cursor: pointer;
  font-size: 0.85rem;
}
.header-actions button:hover { background: rgba(255,255,255,0.25); }
.header-actions button:active { transform: scale(0.9); }

#messages {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.msg {
  max-width: 85%;
  padding: 10px 14px;
  border-radius: var(--radius);
  line-height: 1.7;
  font-size: 0.92rem;
  /* Not pre-wrap: renderMarkdown emits real <p>/<ol>/<li>/<br>, so pre-wrap
     would double every gap. It survives only where literal whitespace is still
     the content — see .msg.user and .msg.error below. */
  white-space: normal;
  word-break: break-word;
  animation: msg-in 0.18s ease both;
}
/* What someone typed never goes through the renderer (see addMessage), so their
   own line breaks are the only thing carrying them. The error detail is set as
   textContent and is not rendered either. */
.msg.user, .msg.error { white-space: pre-wrap; }
@keyframes msg-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.msg.bot {
  background: var(--surface);
  border: 1px solid var(--border);
  border-start-start-radius: 2px;
  align-self: flex-start;
}
.msg.user {
  background: var(--cib-blue);
  color: var(--surface);
  border-start-end-radius: 2px;
  align-self: flex-end;
}
.msg a {
  color: var(--cib-blue);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  overflow-wrap: anywhere; /* long links wrap instead of overflowing the bubble */
}
/* Small outward affordance so a link reads as "opens in a new tab"; inline-block
   keeps the arrow itself out of the underline. */
.msg a[target="_blank"]::after {
  content: "↗";
  display: inline-block;
  margin-inline-start: 3px;
  font-size: 0.8em;
  opacity: 0.6;
  text-decoration: none;
}
.msg.user a { color: var(--surface); }

/* ---------- markdown inside a bubble ----------

   Everything here is sized in em and uses logical properties, for two reasons.
   The em is what lets this block stay character-for-character identical in
   frontend/chat.css and extension/widget.css, which otherwise differ in unit
   throughout (rem vs px, because rem resolves against the host page in the
   extension). Copy it between the two wholesale; never retype it. The logical
   properties (padding-inline-start, border-inline-start, text-align: start)
   are because the widget is Arabic first: a physical margin puts the
   indentation on the wrong side of an RTL bubble. And nothing may be left to a
   browser default — the reset at the top of the file zeroes every margin and
   padding, which would otherwise clip a list marker clean off. */

/* Trim the leading and trailing margin so the rendered text sits flush against
   the bubble's own padding. The chips and the error detail are excluded: they
   are appended after the text as siblings and carry a deliberate margin-top of
   their own, and :first-child counts only elements — so a chip following a bare
   text node (a user bubble with an attachment) would otherwise match and lose
   its spacing. */
.msg > :first-child:not(.sources):not(.msg-attach):not(code) { margin-block-start: 0; }
.msg > :last-child:not(.sources):not(.msg-attach):not(code) { margin-block-end: 0; }

.msg p { margin: 0 0 8px; }
.msg h3, .msg h4, .msg h5, .msg h6 {
  margin: 12px 0 6px;
  font-size: 1em; /* a chat bubble is not a document page */
  font-weight: 700;
  line-height: 1.5;
}
.msg h3 { font-size: 1.08em; }

/* The whole point of emitting <ol>: the number becomes a ::marker that list
   layout places on the start side of the item, so under dir="rtl" it stays
   glued to its own line. A literal "1. " left in the text is just another LTR
   run inside an RTL paragraph, and the bidi algorithm drags it to the far end —
   which is what "the steps are not numbered" actually was. */
.msg ol, .msg ul {
  margin: 6px 0 8px;
  padding-inline-start: 22px;
  padding-inline-end: 0;
}
.msg ol { list-style: decimal; }
.msg ul { list-style: disc; }
.msg li { margin: 3px 0; }
.msg li > ol, .msg li > ul { margin: 3px 0; }
.msg li::marker { font-weight: 600; color: var(--cib-blue); }
.msg li.md-task { list-style: none; margin-inline-start: -18px; }
.msg li.md-task input { margin-inline-end: 6px; vertical-align: baseline; }

/* Identifiers, paths and code read left-to-right even inside an Arabic
   sentence, so they get their own embedding rather than inheriting the
   bubble's. */
.msg code {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.88em;
  background: rgba(var(--blue-rgb), 0.09);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
  direction: ltr;
  unicode-bidi: embed;
}
.msg pre {
  margin: 8px 0;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--tint);
  border: 1px solid var(--border);
  direction: ltr;
  text-align: left;
  white-space: pre;
  overflow-x: auto;
  line-height: 1.5;
}
.msg pre code { background: none; padding: 0; font-size: 0.85em; }

.msg blockquote {
  margin: 8px 0;
  padding-inline-start: 10px;
  border-inline-start: 3px solid rgba(var(--blue-rgb), 0.3);
  color: var(--muted);
}
.msg hr { border: 0; border-top: 1px solid rgba(0, 0, 0, 0.12); margin: 10px 0; }

/* display:block so a wide table scrolls inside the bubble instead of stretching
   it past the 85% max-width. */
.msg table {
  display: block;
  overflow-x: auto;
  max-width: 100%;
  border-collapse: collapse;
  margin: 8px 0;
  font-size: 0.94em;
}
.msg th, .msg td { border: 1px solid var(--border); padding: 4px 8px; text-align: start; }
.msg th { background: var(--tint); color: var(--cib-navy); font-weight: 700; }
.msg del { opacity: 0.65; }

/* The user bubble is CIB blue, so every skin above has to be re-tinted or it
   disappears into the background. Only reachable if a future change routes user
   text through the renderer — kept so that decision stays independent of this
   stylesheet. */
.msg.user code { background: rgba(255, 255, 255, 0.18); }
.msg.user pre { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.25); }
.msg.user li::marker { color: rgba(255, 255, 255, 0.75); }
.msg.user blockquote {
  border-inline-start-color: rgba(255, 255, 255, 0.4);
  color: rgba(255, 255, 255, 0.85);
}
.msg.user hr { border-top-color: rgba(255, 255, 255, 0.3); }
.msg.user th, .msg.user td { border-color: rgba(255, 255, 255, 0.3); }
.msg.user th { background: rgba(255, 255, 255, 0.15); color: inherit; }

/* Error bubbles. The upstream reason has to reach the user verbatim, but it is
   English technical text in an Arabic conversation — so the sentence stays
   readable and the detail is demoted to a small LTR block beneath it. */
.msg.bot.error { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger-deep); }
.msg.error code {
  display: block;
  margin-top: 6px;
  /* Undo the .msg code skin above: this block is a wall of upstream text, not
     an inline identifier, and the tint and padding read as a second bubble. */
  background: none;
  padding: 0;
  direction: ltr;
  text-align: left;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.72rem;
  line-height: 1.5;
  opacity: 0.75;
  overflow-wrap: anywhere;
}

.msg .typing { display: inline-flex; gap: 4px; padding: 4px 2px; }
.typing i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--muted);
  animation: blink 1.2s infinite;
}
.typing i:nth-child(2) { animation-delay: 0.2s; }
.typing i:nth-child(3) { animation-delay: 0.4s; }
@keyframes blink { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }

.sources { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.sources a, .sources span {
  font-size: 0.78rem;
  background: var(--tint);
  border: 1px solid var(--border);
  color: var(--cib-blue);
  border-radius: var(--radius-sm);
  padding: 4px 12px;
  text-decoration: none;
}
.sources a:hover { background: var(--tint-2); }

#quick-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 14px 8px;
}
/* Outlined like the site's white "Log in" button; fills blue on hover. */
.chip {
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  background: var(--surface);
  border: 1px solid var(--cib-blue);
  color: var(--cib-blue);
  border-radius: var(--radius-sm);
  padding: 6px 13px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.chip:hover { background: var(--cib-blue); color: var(--surface); }
.chip:active { transform: scale(0.96); }

/* Jump-to-latest affordance: floats above the input row, appears (via JS) only
   when the user has scrolled up away from the newest message. */
#scroll-bottom {
  position: absolute;
  left: 50%;
  bottom: 88px;
  transform: translateX(-50%);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--cib-blue);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(var(--shadow-rgb), 0.18);
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: pop-in 0.15s ease;
}
#scroll-bottom:active { transform: translateX(-50%) scale(0.9); }
@keyframes pop-in {
  from { opacity: 0; transform: translateX(-50%) scale(0.8); }
  to   { opacity: 1; transform: translateX(-50%) scale(1); }
}

/* ---- attachments ---- */
/* The picked file, parked above the composer until it is sent. */
#attachment-tray { padding: 0 14px 8px; }
.attach-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 8px 10px;
  animation: msg-in 0.18s ease both;
}
.attach-chip img {
  flex: none;
  width: 38px; height: 38px;
  object-fit: cover;
  border-radius: var(--radius-sm);
}
.attach-icon { flex: none; font-size: 1.4rem; line-height: 1; }
/* min-width:0 is what lets the filename below actually ellipsize. */
.attach-meta { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.attach-name {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--cib-blue);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attach-size { font-size: 0.72rem; color: var(--muted); }
.attach-remove {
  flex: none;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  background: var(--tint);
  color: var(--cib-blue);
  font-size: 0.75rem;
  cursor: pointer;
}
.attach-remove:hover { background: var(--tint-2); }
.attach-remove:active { transform: scale(0.9); }

/* The same file once it is part of a sent message. */
.msg-attach {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-top: 8px;
  font-size: 0.78rem;
  opacity: 0.9;
}
.msg-attach img {
  display: block;
  max-width: 100%;
  max-height: 150px;
  border-radius: var(--radius-sm);
}

#chat-input-row {
  display: flex;
  gap: 8px;
  padding: 12px 14px;
  background: var(--surface);
  border-top: 1px solid var(--border);
  align-items: flex-end; /* buttons stay pinned to the bottom as the textarea grows */
}
#chat-input {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  /* 16px keeps iOS Safari from auto-zooming the viewport on focus. */
  font-size: 16px;
  line-height: 1.4;
  font-family: inherit;
  color: var(--text);
  outline: none;
  resize: none;
  overflow-y: auto;
  max-height: 120px; /* ~5 lines, then it scrolls internally */
  display: block;
}
#chat-input:focus { border-color: var(--cib-blue); box-shadow: 0 0 0 1px var(--cib-blue); }
#send-btn, #mic-btn, #attach-btn {
  flex-shrink: 0;
  width: 42px; height: 42px;
  border-radius: var(--radius-sm);
  border: none;
  cursor: pointer;
  font-size: 1rem;
}
#send-btn:active, #mic-btn:active, #attach-btn:active { transform: scale(0.9); }
#send-btn { background: var(--cib-blue); color: var(--surface); }
#send-btn:hover:enabled { background: var(--cib-navy); }
#send-btn:disabled, #mic-btn:disabled, #attach-btn:disabled { opacity: 0.4; cursor: default; }
#send-btn:disabled:active, #mic-btn:disabled:active, #attach-btn:disabled:active { transform: none; }
#attach-btn { background: var(--tint); }
#attach-btn:hover:enabled { background: var(--tint-2); }
#chat-input:disabled { background: var(--bg); cursor: default; }
#mic-btn { background: var(--tint); transition: background 0.15s ease, color 0.15s ease; }
#mic-btn:hover:enabled { background: var(--tint-2); }
/* Three states, and every one of them means "the microphone is open": opening
   the call, the agent listening to you, the agent replying. Only a *voice*
   session sets them — a typed conversation must leave the button alone. See
   syncVoiceUi() in chat.js. */
#mic-btn.connecting { background: var(--cib-blue); color: var(--surface); animation: mic-connecting 1s ease-in-out infinite; }
#mic-btn.recording { background: var(--danger); color: var(--surface); animation: pulse 1.2s infinite; }
#mic-btn.speaking { background: var(--cib-navy); color: var(--surface); animation: pulse-navy 1.2s infinite; }
@keyframes pulse { 50% { box-shadow: 0 0 0 8px rgba(var(--danger-rgb), 0.25); } }
@keyframes pulse-navy { 50% { box-shadow: 0 0 0 8px rgba(var(--blue-rgb), 0.25); } }
@keyframes mic-connecting { 50% { opacity: 0.55; } }

/* Login gate: opaque layer over the widget body; the header (z-index above)
   stays usable so the widget can still be closed before logging in. */
#chat-header { position: relative; z-index: 2; }
#login-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
}
#login-box {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 78%;
  max-width: 300px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 4px solid var(--cib-blue);
  border-radius: var(--radius-sm);
  padding: 22px 20px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.10);
  text-align: center;
}
/* The logo at its native size, centred on its blue tile. */
#login-avatar {
  align-self: center;
  width: 107px;
  height: 51px;
  background-size: 83px 39px;
}
#login-title { font-weight: 600; color: var(--text); font-size: 0.95rem; }
#login-box input {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  font-size: 16px; /* avoid iOS zoom-on-focus */
  font-family: inherit;
  color: var(--text);
}
#login-box input:focus { outline: none; border-color: var(--cib-blue); box-shadow: 0 0 0 1px var(--cib-blue); }
#login-consent-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text);
  cursor: pointer;
  text-align: start;
}
#login-consent {
  width: 16px;
  height: 16px;
  flex: none;
  accent-color: var(--cib-blue);
  cursor: pointer;
}
#login-btn {
  background: var(--cib-blue);
  color: var(--surface);
  border: none;
  border-radius: var(--radius-sm);
  padding: 11px;
  font-size: 0.92rem;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}
#login-btn:hover { background: var(--cib-navy); }
#login-btn:active { transform: scale(0.98); }
#login-btn:disabled { opacity: 0.6; cursor: default; }
#login-error { color: var(--danger-text); font-size: 0.8rem; min-height: 1em; }

#powered-by {
  text-align: center;
  font-size: 0.68rem;
  color: var(--faint);
  background: var(--surface);
  padding: 3px 0 7px;
  /* Physical shift — keeps the nudge leftward in RTL and LTR alike. */
  transform: translateX(-10px);
}
#powered-by strong { color: var(--faint-strong); }

/* Shown for the whole of a voice call — it is the only thing on screen that
   says the microphone is open and whose turn it is. Toggled by syncVoiceUi(). */
#recording-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--rec-bg);
  color: var(--danger-text);
  font-size: 0.82rem;
  padding: 8px 16px;
  border-top: 1px solid var(--rec-border);
}
/* While the agent replies the bar goes blue, matching the mic button, so the
   two never disagree about who is talking. */
#recording-bar.speaking {
  background: var(--tint);
  color: var(--cib-blue);
  border-top-color: var(--tint-2);
}
.rec-dot {
  flex: 0 0 auto;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--danger);
  animation: blink 1s infinite;
}
#recording-bar.speaking .rec-dot { background: var(--cib-blue); animation: none; }
/* Live level, drawn from the analyser the SDK already runs on the microphone
   and on the agent's playback. The canvas backing store is resized to this box
   in startViz(); these are the CSS pixels it measures. */
#rec-visualizer {
  flex: 0 0 auto;
  width: 84px;
  height: 22px;
  display: block;
}
#rec-label { flex: 1; min-width: 0; }

/* Enlarge tap targets on touch devices; desktop mouse UI is left untouched. */
@media (pointer: coarse) {
  .header-actions button { width: 40px; height: 40px; }
  #send-btn, #mic-btn, #attach-btn { width: 46px; height: 46px; }
  .chip, .sources a, .sources span { min-height: 34px; display: inline-flex; align-items: center; }
}

@media (max-width: 520px) {
  #chat-widget {
    right: 0;
    bottom: 0;
    width: 100vw;
    max-width: 100vw;
    /* Layered fallbacks: dvh tracks the mobile URL bar; --app-h (set from
       visualViewport in JS) additionally accounts for the on-screen keyboard. */
    height: 100vh;
    height: 100dvh;
    height: var(--app-h, 100dvh);
    max-height: none;
    border-radius: 0;
    animation: sheet-in 0.22s cubic-bezier(0.16, 1, 0.3, 1);
  }
  #chat-widget.closing { animation: sheet-out 0.18s ease forwards; }
  @keyframes sheet-in { from { transform: translateY(100%); } to { transform: none; } }
  @keyframes sheet-out { to { transform: translateY(100%); } }
  #chat-toggle { right: 16px; bottom: 16px; }

  /* Grabber bar for swipe-to-close; it also carries the top safe-area inset. */
  #drag-handle {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: calc(8px + env(safe-area-inset-top)) 0 4px;
    background: var(--cib-blue);
    touch-action: none;
    cursor: grab;
  }
  #drag-handle span {
    width: 40px; height: 4px; border-radius: 999px;
    background: rgba(255, 255, 255, 0.4);
  }
  #chat-input-row { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
  #scroll-bottom { bottom: calc(96px + env(safe-area-inset-bottom)); }
}
