/* ─────────────────────────────────────────────────────────────────────────
   chat-widget.css — the support assistant, native to the landing page.

   Design system: Steep. The widget is an overlay, so it is one of the few
   places allowed elevation (DESIGN.md § Elevation → Modal / Overlay Card).
   Everything inside stays achromatic: the outgoing bubble is ink, the
   incoming bubble is mist. No accent colour is spent here — the page's one
   peach surface belongs to the pricing card.

   CONTRACT — chat-widget.js toggles `.open` on #bubble, #panel and
   #widgetMenu, writes only `style.display` on #widgetLogin / #widgetChatArea,
   and generates .msg-row / .msg / .msg-avatar / .thinking / .dot /
   .welcome-* / .suggestion-chips / .chip / .qr-attachment markup. Every one
   of those must be styled here or the widget breaks visually.
   ───────────────────────────────────────────────────────────────────────── */

/* ═══ Launcher ═══════════════════════════════════════════════════════════
   The filled ink lozenge, per the primary-action rule. Sits bottom-right on
   its own — it is NOT nested inside #panel, so the panel's hidden state can
   never hide the launcher (which is exactly the bug the old build had). */

.chat-fab {
  position: fixed;
  right: var(--spacing-24);
  bottom: var(--spacing-24);
  z-index: 999;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 1px solid var(--color-ink-black);
  border-radius: var(--radius-buttons);
  background: var(--color-ink-black);
  color: var(--color-paper-white);
  box-shadow: var(--shadow-overlay);
  cursor: pointer;
  transition: transform var(--transition-normal), opacity var(--transition-normal),
    background var(--transition-fast);
}

.chat-fab:hover {
  background: #000;
  transform: translateY(-2px);
}

.chat-fab:active {
  transform: translateY(0);
}

.chat-fab svg {
  width: 24px;
  height: 24px;
}

/* One button, two glyphs — cross-fade rather than swapping nodes. */
.chat-fab__close {
  display: none;
}

/* Contract: JS adds .open to #bubble while the panel is showing. */
.chat-fab.open {
  opacity: 0;
  transform: scale(0.6);
  pointer-events: none;
}

/* ═══ Panel ══════════════════════════════════════════════════════════════
   Hidden state must be non-interactive, or the invisible panel swallows
   clicks meant for the page underneath. */

.chat-panel {
  position: fixed;
  right: var(--spacing-24);
  bottom: var(--spacing-24);
  z-index: 998;
  display: flex;
  flex-direction: column;
  width: 384px;
  max-width: calc(100vw - 2 * var(--spacing-20));
  height: min(620px, calc(100dvh - 2 * var(--spacing-24)));
  background: var(--surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-cards);
  box-shadow: var(--shadow-overlay);
  overflow: hidden;
  opacity: 0;
  transform: translateY(12px) scale(0.98);
  transform-origin: bottom right;
  pointer-events: none;
  transition: opacity var(--transition-normal), transform var(--transition-normal);
}

/* Contract: the ONLY signal that the panel is open — the Escape handler
   reads this class. */
.chat-panel.open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* ═══ Panel header ═══════════════════════════════════════════════════════
   White and quiet, with a hairline rule — not a coloured bar. */

.chat-panel__head {
  display: flex;
  align-items: center;
  gap: var(--spacing-12);
  flex-shrink: 0;
  padding: var(--spacing-16);
  border-bottom: 1px solid var(--color-border);
  background: var(--surface-elevated);
}

.chat-panel__avatar {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: var(--radius-buttons);
  background: var(--color-ink-black);
  color: var(--color-paper-white);
}

.chat-panel__avatar svg {
  width: 18px;
  height: 18px;
}

.chat-panel__titles {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
}

.chat-panel__title {
  font-size: var(--text-caption);
  font-weight: var(--font-weight-480);
  line-height: 1.3;
  color: var(--color-ink-black);
}

.chat-panel__status {
  display: flex;
  align-items: center;
  gap: var(--spacing-8);
  font-size: var(--text-sm);
  color: var(--color-slate-gray);
}

.chat-panel__dot {
  width: 6px;
  height: 6px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--color-ink-black);
}

.chat-icon-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--radius-buttons);
  color: var(--color-slate-gray);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.chat-icon-btn:hover {
  background: var(--surface-card-mist);
  color: var(--color-ink-black);
}

.chat-icon-btn svg {
  width: 16px;
  height: 16px;
}

/* ═══ Options menu ═══════════════════════════════════════════════════════
   CONTRACT — #widgetMenuBtn and #widgetMenu must both live inside
   .chat-menu-wrap (#widgetMenuWrap): the document click handler uses
   closest('#widgetMenuWrap') to decide whether a click was "outside". */

.chat-menu-wrap {
  position: relative;
  flex-shrink: 0;
}

.chat-menu {
  position: absolute;
  top: calc(100% + var(--spacing-8));
  right: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 200px;
  padding: var(--spacing-8);
  background: var(--surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-smallcards);
  box-shadow: var(--shadow-popover);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity var(--transition-fast), transform var(--transition-fast);
}

/* Contract: toggled by widgetToggleMenu(). Default MUST be
   pointer-events:none or the invisible menu blocks the header buttons. */
.chat-menu.open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.chat-menu button {
  display: flex;
  align-items: center;
  gap: var(--spacing-12);
  width: 100%;
  min-height: 0;
  padding: var(--spacing-8) var(--spacing-12);
  background: none;
  border: 0;
  border-radius: var(--radius-images);
  color: var(--color-ink-black);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-430);
  text-align: left;
  cursor: pointer;
  transition: background var(--transition-fast);
}

.chat-menu button:hover {
  background: var(--surface-card-mist);
}

.chat-menu button svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--color-slate-gray);
}

/* ═══ Login ══════════════════════════════════════════════════════════════
   CONTRACT — JS writes only `display:flex` / `display:none`, so the column
   layout, gaps and scrolling all have to come from here. Visible by default. */

.chat-login {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--spacing-24) var(--spacing-20);
}

.chat-login__lede {
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
  color: var(--color-slate-gray);
}

/* CONTRACT — JS only ever sets .textContent on #widgetError, never a class
   or a display value. The empty/non-empty visibility must therefore be pure
   CSS, or an empty box with padding sits there permanently. */
.chat-login__error {
  display: none;
  padding: var(--spacing-12) var(--spacing-16);
  border-radius: var(--radius-images);
  border: 1px solid var(--status-attention-border);
  background: var(--status-attention-surface);
  color: var(--status-attention-text);
  font-size: var(--text-sm);
  line-height: 1.45;
}

.chat-login__error:not(:empty) {
  display: block;
}

.chat-field {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-8);
}

.chat-field > span {
  font-size: var(--text-sm);
  font-weight: var(--font-weight-450);
  color: var(--color-ink-black);
}

.chat-login__submit {
  width: 100%;
  margin-top: var(--spacing-4);
}

/* ═══ Chat area ══════════════════════════════════════════════════════════
   CONTRACT — hidden at load; JS reveals it with `display:flex`. The column
   direction, flex:1 and min-height:0 must live here so the transcript can
   actually scroll once it is shown. */

.chat-area {
  display: none;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* CONTRACT — this is the scroll container, and it must stay empty in the
   template: JS reads .children.length and calls innerHTML = "". */
.chat-messages {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-12);
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: var(--spacing-20);
  background: var(--surface-section-fog);
}

/* ═══ Messages ═══════════════════════════════════════════════════════════
   Structure generated by JS:
     .msg-row.user  →  [ .msg.user ]                (no avatar)
     .msg-row.bot   →  [ .msg-avatar, .msg.bot ]    (avatar first) */

.msg-row {
  display: flex;
  align-items: flex-end;
  gap: var(--spacing-8);
}

.msg-row.user {
  justify-content: flex-end;
}

.msg-avatar {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: var(--radius-buttons);
  background: var(--surface-card-mist);
  border: 1px solid var(--color-border);
  font-size: 13px;
  line-height: 1;
}

.msg {
  max-width: 78%;
  padding: var(--spacing-12) var(--spacing-16);
  border-radius: var(--radius-inputs);
  font-size: var(--text-caption);
  line-height: 1.5;
  word-break: break-word;
}

/* Outgoing — the filled ink lozenge, echoing the primary button. */
.msg.user {
  background: var(--color-ink-black);
  color: var(--color-paper-white);
  border-bottom-right-radius: var(--spacing-4);
  /* Content is set with textContent, so newlines from history need this. */
  white-space: pre-wrap;
}

/* Incoming — quiet mist on white. */
.msg.bot {
  background: var(--surface-elevated);
  color: var(--color-ink-black);
  border: 1px solid var(--color-border);
  border-bottom-left-radius: var(--spacing-4);
  /* CONTRACT — content is innerHTML (<p>/<ul>/<li>/<strong>), so this must
     override the pre-wrap above or the injected indentation shows up. */
  white-space: normal;
}

/* The bot markdown subset is exactly four tags, joined with no whitespace,
   so every bit of vertical rhythm has to come from these rules. */
.msg.bot p {
  margin: 0 0 var(--spacing-8);
}

.msg.bot ul {
  margin: 0 0 var(--spacing-8);
  padding-left: 1.25em;
}

.msg.bot li {
  margin-bottom: var(--spacing-4);
}

.msg.bot li::marker {
  color: var(--color-slate-gray);
}

.msg.bot strong {
  font-weight: var(--font-weight-medium);
}

.msg.bot p:last-child,
.msg.bot ul:last-child {
  margin-bottom: 0;
}

/* A non-2xx reply can yield an empty bot bubble; keep it from looking like
   a broken render. */
.msg.bot:empty {
  display: none;
}

/* ═══ QR attachment ══════════════════════════════════════════════════════
   .msg.bot.qr-attachment > img[160x160] + optional .qr-attachment-meta.
   The white plate is functional, not decorative: a QR on a tinted surface
   is less reliable to scan. */

.msg.qr-attachment {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-8);
  padding: var(--spacing-12);
  background: var(--color-paper-white);
}

.msg.qr-attachment img {
  width: 160px;
  height: 160px;
  border-radius: var(--radius-images);
}

.qr-attachment-meta {
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size: var(--text-sm);
  color: var(--color-slate-gray);
  text-align: center;
}

/* ═══ Thinking indicator ═════════════════════════════════════════════════
   .thinking is a sibling of .msg-avatar and is NOT a .msg, so it inherits
   no bubble styling — it needs its own. */

.thinking {
  display: flex;
  align-items: center;
  gap: var(--spacing-4);
  padding: var(--spacing-12) var(--spacing-16);
  background: var(--surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-inputs);
  border-bottom-left-radius: var(--spacing-4);
}

.thinking .dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-smoke-gray);
  animation: widget-blink 1.3s infinite ease-in-out;
}

.thinking .dot:nth-child(2) {
  animation-delay: 0.18s;
}

.thinking .dot:nth-child(3) {
  animation-delay: 0.36s;
}

@keyframes widget-blink {
  0%,
  80%,
  100% {
    opacity: 0.25;
  }
  40% {
    opacity: 1;
  }
}

/* ═══ Welcome block ══════════════════════════════════════════════════════
   Appended straight into #widgetMessages (not wrapped in a .msg-row).
   An empty screen is an invitation to act, hence the suggestion chips. */

.welcome-block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-12);
  padding: var(--spacing-20);
  background: var(--surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-cards);
}

.welcome-avatar {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-buttons);
  background: var(--surface-card-mist);
  border: 1px solid var(--color-border);
  font-size: 18px;
  line-height: 1;
}

.welcome-title {
  font-size: var(--text-body);
  font-weight: var(--font-weight-480);
  line-height: 1.3;
  color: var(--color-ink-black);
}

.welcome-sub {
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
  color: var(--color-slate-gray);
}

.suggestion-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-8);
  margin-top: var(--spacing-4);
}

/* Ghost pills — the matched-pair geometry, at chip scale. */
.chip {
  padding: var(--spacing-8) var(--spacing-12);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-buttons);
  color: var(--color-ink-black);
  font-family: var(--font-sohne);
  font-size: var(--text-sm);
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.chip:hover {
  background: var(--surface-card-mist);
  border-color: var(--color-ink-black);
}

/* ═══ Composer ═══════════════════════════════════════════════════════════
   DESIGN.md § Input / Composer: hairline field, 16px radius, smoke-gray
   placeholder, dark circular send button on the right. */

.chat-composer {
  display: flex;
  align-items: center;
  gap: var(--spacing-8);
  flex-shrink: 0;
  padding: var(--spacing-16);
  border-top: 1px solid var(--color-border);
  background: var(--surface-elevated);
}

.chat-composer input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
}

.chat-send {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  padding: 0;
  border: 1px solid var(--color-ink-black);
  border-radius: var(--radius-buttons);
  background: var(--color-ink-black);
  color: var(--color-paper-white);
  cursor: pointer;
  transition: background var(--transition-fast), opacity var(--transition-fast);
}

.chat-send:hover:not(:disabled) {
  background: #000;
}

/* widgetSetLoading() only flips .disabled, so these carry the busy state. */
.chat-send:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.chat-send svg {
  width: 18px;
  height: 18px;
}

.chat-composer input:disabled {
  background: var(--surface-section-fog);
}

/* ═══ Responsive ═════════════════════════════════════════════════════════
   On a phone the panel goes near-full-bleed rather than shrinking. */

@media (max-width: 480px) {
  .chat-panel {
    right: var(--spacing-12);
    left: var(--spacing-12);
    bottom: var(--spacing-12);
    width: auto;
    max-width: none;
    height: min(76dvh, calc(100dvh - 2 * var(--spacing-12)));
  }

  .chat-fab {
    right: var(--spacing-16);
    bottom: var(--spacing-16);
  }

  .msg {
    max-width: 85%;
  }
}
