/* Palette lifted from the source React component's Tailwind classes so the
   look matches without pulling in Tailwind: zinc-800/900/950, white/5 and
   white/10 hairlines, and the indigo-600 -> violet-600 accent gradient. */
:root {
  --zinc-950: #09090b;
  --zinc-900: #18181b;
  --zinc-800: #27272a;
  --white-05: rgba(255, 255, 255, 0.05);
  --white-10: rgba(255, 255, 255, 0.10);
  --white-20: rgba(255, 255, 255, 0.20);
  --white-30: rgba(255, 255, 255, 0.30);
  --white-40: rgba(255, 255, 255, 0.40);
  --white-60: rgba(255, 255, 255, 0.60);
  --indigo-500: #6366f1;
  --indigo-600: #4f46e5;
  --violet-600: #7c3aed;
  --text: #f4f4f5;
  --danger: #f87171;

  /* The component's motion signature: a decelerating ease used on every
     entrance so bubbles settle rather than snap. */
  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---- Animated background -------------------------------------------- */

.bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: var(--zinc-950);
}

/* Two blooms only. Research on this pattern is consistent that past three or
   four hues the overlaps muddy to brown instead of reading as light, and the
   page already has exactly two accents. */
.bg__aurora {
  position: absolute;
  /* Oversized so the drift never exposes an edge. */
  inset: -25%;
  background:
    radial-gradient(38% 42% at 22% 28%, rgba(79, 70, 229, 0.42), transparent 68%),
    radial-gradient(34% 38% at 78% 68%, rgba(124, 58, 237, 0.34), transparent 66%),
    radial-gradient(46% 30% at 62% 12%, rgba(49, 46, 129, 0.30), transparent 70%);
  filter: blur(70px) saturate(1.25);
  will-change: transform;
  /* Isolated so the compositor can treat this as its own layer. */
  contain: strict;
}

/* The animation is switched on by a class, not declared here, because app.js
   measures the real frame rate on load and only enables it when the device can
   afford it. A static aurora still looks deliberate; a stuttering one does not. */
.bg--motion .bg__aurora {
  animation: aurora-drift 34s ease-in-out infinite alternate;
}

/* Translate only. Scale and rotate measured the same cost here, but translate
   is the one most likely to stay on the compositor across engines, and the
   drift reads no differently without them. */
@keyframes aurora-drift {
  from { transform: translate3d(-3%, -2%, 0); }
  to   { transform: translate3d( 3%,  2%, 0); }
}

/* Follows the pointer, so it never moves on its own while someone is reading.
   Hidden until the first move, and only on devices that actually have a
   pointer. */
.bg__spot {
  position: absolute;
  top: 0;
  left: 0;
  width: 620px;
  height: 620px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(99, 102, 241, 0.16), transparent 62%);
  opacity: 0;
  will-change: transform;
}
@media (hover: none), (pointer: coarse) {
  .bg__spot { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .bg--motion .bg__aurora { animation: none; }
  .bg__spot { display: none; }
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }

body {
  background: transparent;
  color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.icon { width: 16px; height: 16px; display: block; }
.icon--sm { width: 14px; height: 14px; }

/* ---- Card ---------------------------------------------------------- */

/* ---- Shell: nav above the stage -------------------------------------- */

.shell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 100%;
}

.nav {
  display: flex;
  gap: 6px;
  max-width: 100%;
  padding: 5px;
  border: 1px solid var(--white-10);
  border-radius: 999px;
  background: rgba(24, 24, 27, 0.7);
  backdrop-filter: blur(10px);
  /* Never wraps: a two-line pill bar reads as a broken control. Past the
     available width it scrolls, which is what a row of tabs should do. */
  overflow-x: auto;
  scrollbar-width: none;
}
.nav::-webkit-scrollbar { display: none; }

.navbtn {
  flex: none;
  font: inherit;
  font-size: 12.5px;
  white-space: nowrap;
  color: var(--white-60);
  background: transparent;
  border: 0;
  border-radius: 999px;
  padding: 7px 14px;
  cursor: pointer;
  transition: color 0.15s, background-color 0.15s;
}
.navbtn:hover { color: #fff; background: var(--white-05); }
.navbtn:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }
.navbtn.is-on {
  color: #fff;
  background: linear-gradient(to right, var(--indigo-600), var(--violet-600));
  box-shadow: 0 6px 18px -6px rgba(99, 102, 241, 0.6);
}

/* ---- Stage: chat centred, profile panel slides in beside it ---------- */

/* The shift is animated on grid-template-columns rather than a margin or a
   transform: the right column genuinely grows from zero, so the chat is pushed
   left by the layout itself and stays centred against the pair at every frame.
   A transform would slide the chat over the panel instead of beside it. */
.stage {
  display: grid;
  grid-template-columns: minmax(0, 520px) 0px;
  gap: 0;
  align-items: center;
  justify-content: center;
  width: 100%;
  transition:
    grid-template-columns 0.7s var(--ease-out-quint),
    gap 0.7s var(--ease-out-quint);
}
.stage--open { grid-template-columns: minmax(0, 520px) 360px; gap: 20px; }

.profile {
  min-width: 0;
  overflow: hidden;
  opacity: 0;
  transform: translateX(-14px);
  /* Delayed so the column has started opening before the card fades in, fading into a zero-width column just looks like a flicker. */
  transition:
    opacity 0.5s var(--ease-out-quint) 0.18s,
    transform 0.5s var(--ease-out-quint) 0.18s;
}
.stage--open .profile { opacity: 1; transform: none; }

.chat {
  width: 100%;
  height: min(960px, calc(100dvh - 96px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--white-10);
  border-radius: 16px;
  background: linear-gradient(to bottom, var(--zinc-900), var(--zinc-950));
  box-shadow: 0 24px 64px -16px rgba(0, 0, 0, 0.5);
}
/* Below the two-column breakpoint the panel stacks under the chat and the page
   scrolls, rather than the chat shrinking to make room for it. */
@media (max-width: 1023px) {
  body { align-items: flex-start; }
  .stage {
    grid-template-columns: minmax(0, 520px);
    grid-template-rows: auto 0fr;
    transition: grid-template-rows 0.7s var(--ease-out-quint), gap 0.7s var(--ease-out-quint);
  }
  .stage--open { grid-template-columns: minmax(0, 520px); grid-template-rows: auto 1fr; gap: 16px; }
  .chat { height: min(960px, calc(100dvh - 96px)); }
  .stage--open .chat { height: min(620px, 72dvh); }
  .profile { transform: translateY(-14px); }
}

@media (max-width: 560px) {
  body { padding: 0; }
  .shell { gap: 10px; }
  .nav {
    align-self: stretch;
    margin: 10px 10px 0;
    max-width: calc(100vw - 20px);
  }
  .chat { height: calc(100dvh - 66px); border: 0; border-radius: 0; }
  .stage--open .chat { height: 72dvh; }
  .profile { padding: 0 12px 16px; }
}

/* ---- Profile card --------------------------------------------------- */

.profile__card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: min(960px, calc(100dvh - 96px));
  overflow-y: auto;
  padding: 22px 20px;
  border: 1px solid var(--white-10);
  border-radius: 28px;
  background: rgba(24, 24, 27, 0.6);
  backdrop-filter: blur(20px);
  box-shadow: 0 24px 64px -16px rgba(0, 0, 0, 0.5);
  scrollbar-width: thin;
  scrollbar-color: var(--white-10) transparent;
}
/* Fade the last few pixels when there is more below, so a clipped card reads as
   "scroll me" rather than as a rendering bug. The class is toggled in JS
   because CSS cannot ask whether an element overflows. */
.profile__card.is-scrollable {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent 100%);
}
.profile__card.is-scrollable.is-at-end {
  -webkit-mask-image: none;
  mask-image: none;
}
/* Glass sheen, matching the reference's gradient overlay. */
.profile__card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.05), transparent 55%);
  pointer-events: none;
}

.profile__head { display: flex; flex-direction: column; align-items: center; text-align: center; }

.profile__avatar {
  position: relative;
  display: grid;
  place-items: center;
  width: 88px; height: 88px;
  margin-bottom: 14px;
  border-radius: 50%;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #fff;
  background: linear-gradient(135deg, var(--indigo-500), var(--violet-600));
}
/* The reference puts a blurred halo behind the portrait; same idea, no photo. */
/* Photo variant: the gradient stays underneath as the backdrop while the image
   decodes, so there is no flash of empty circle. */
.avatar--photo { background: var(--zinc-800); overflow: hidden; }
.avatar--photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A hairline stops the portrait bleeding into the dark card behind it. */
.profile__avatar.avatar--photo { border: 1px solid var(--white-10); }

.profile__avatar::after {
  content: '';
  position: absolute;
  inset: -14px;
  border-radius: 50%;
  background: rgba(99, 102, 241, 0.35);
  filter: blur(22px);
  z-index: -1;
}

.profile__name { font-size: 19px; font-weight: 600; letter-spacing: -0.01em; color: #fff; }
.profile__role {
  margin-top: 5px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--white-40);
}
.profile__bio { margin-top: 10px; font-size: 12.5px; line-height: 1.55; color: #a1a1aa; }

.profile__body { display: flex; flex-direction: column; gap: 12px; }

.profile__viewtitle {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--white-40);
  padding-bottom: 2px;
  border-bottom: 1px solid var(--white-10);
}

.profile__items { display: flex; flex-direction: column; gap: 7px; }

/* Re-staggered on every topic switch so the swap reads as a change rather than
   a silent content substitution. Capped so a six-item view does not crawl. */
.profile__items.is-fresh > .pitem {
  animation: pitem-in 0.4s var(--ease-out-quint) both;
}
.profile__items.is-fresh > .pitem:nth-child(1) { animation-delay: 0.02s; }
.profile__items.is-fresh > .pitem:nth-child(2) { animation-delay: 0.06s; }
.profile__items.is-fresh > .pitem:nth-child(3) { animation-delay: 0.10s; }
.profile__items.is-fresh > .pitem:nth-child(4) { animation-delay: 0.14s; }
.profile__items.is-fresh > .pitem:nth-child(5) { animation-delay: 0.18s; }
.profile__items.is-fresh > .pitem:nth-child(n + 6) { animation-delay: 0.22s; }

@keyframes pitem-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

.pitem {
  position: relative;
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  padding: 10px 13px;
  border: 1px solid var(--white-10);
  border-radius: 14px;
  background: rgba(39, 39, 42, 0.5);
  transition: transform 0.2s var(--ease-out-quint), border-color 0.2s;
}
.pitem:hover { transform: translateY(-3px); border-color: var(--white-20); }
.pitem__title { font-size: 12.5px; font-weight: 600; color: #fff; line-height: 1.4; }
.pitem__meta {
  margin-top: 2px;
  font-size: 10.5px;
  color: var(--indigo-500);
  letter-spacing: 0.02em;
}
.pitem__detail { margin-top: 5px; font-size: 12px; line-height: 1.5; color: #a1a1aa; }

/* Only items with more to show are interactive. */
.pitem--open { cursor: pointer; padding-right: 28px; }
.pitem--open:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }
.pitem__arrow {
  position: absolute;
  top: 11px; right: 11px;
  color: var(--white-30);
  transition: transform 0.2s var(--ease-out-quint), color 0.2s;
}
.pitem--open:hover .pitem__arrow { transform: translate(2px, -2px); color: var(--indigo-500); }

/* ---- Detail modal ---------------------------------------------------- */

.modal {
  /* margin: auto is what centres a <dialog> in the top layer, and the
     `* { margin: 0 }` reset at the top of this file wipes the UA default, which
     pins the modal to the top-left corner. Restore it explicitly. */
  margin: auto;
  width: min(640px, calc(100vw - 32px));
  max-height: min(80dvh, 760px);
  padding: 0;
  border: 1px solid var(--white-10);
  border-radius: 22px;
  background: transparent;
  color: var(--text);
  overflow: visible;
}
.modal::backdrop { background: rgba(0, 0, 0, 0.62); backdrop-filter: blur(3px); }

.modal__card {
  display: flex;
  flex-direction: column;
  max-height: inherit;
  border-radius: inherit;
  background: linear-gradient(to bottom, var(--zinc-900), var(--zinc-950));
  box-shadow: 0 32px 80px -20px rgba(0, 0, 0, 0.7);
  overflow: hidden;
}

.modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  padding: 20px 20px 14px;
  border-bottom: 1px solid var(--white-05);
}
.modal__heading { min-width: 0; }
.modal__title { font-size: 17px; font-weight: 600; color: #fff; line-height: 1.3; }
.modal__meta { margin-top: 4px; font-size: 12px; color: var(--indigo-500); }
.modal__meta[hidden] { display: none; }

.modal__close {
  flex: none;
  display: grid; place-items: center;
  width: 32px; height: 32px;
  border: 0; border-radius: 9px;
  color: var(--white-60);
  background: var(--white-05);
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s;
}
.modal__close:hover { background: var(--white-10); color: #fff; }
.modal__close:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }

/* Diagram and prose scroll together: a diagram pinned above a scrolling body
   would eat vertical space permanently on a short screen. */
.modal__scroll {
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--white-10) transparent;
}

.modal__diagram {
  padding: 16px 20px 0;
  overflow-x: auto;
}
.modal__diagram[hidden] { display: none; }
/* Inline <svg>, not <img>: GSAP cannot reach inside an image. */
.modal__diagram svg {
  display: block;
  width: 100%;
  height: auto;
  /* Below this the labels stop being readable, so the diagram scrolls
     sideways inside the modal rather than shrinking into illegibility. */
  min-width: 460px;
  border: 1px solid var(--white-05);
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.25);
}

.modal__body {
  padding: 16px 20px 22px;
  font-size: 14px;
  line-height: 1.65;
  color: #d4d4d8;
  scrollbar-width: thin;
  scrollbar-color: var(--white-10) transparent;
}

/* The modal body reuses the chat bubble's Markdown rules, same renderer, so
   the same element shapes arrive here. */
.modal__body p { margin: 0 0 10px; }
.modal__body p:last-child { margin-bottom: 0; }
.modal__body strong { color: #fff; }
.modal__body ul, .modal__body ol { margin: 0 0 10px; padding-left: 20px; }
.modal__body ul:last-child, .modal__body ol:last-child { margin-bottom: 0; }
.modal__body li { margin: 4px 0; }
.modal__body li::marker { color: var(--indigo-500); }
.modal__body .md-head {
  margin: 18px 0 8px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--white-60);
}
.modal__body > .md-head:first-child { margin-top: 0; }
.modal__body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--white-10);
}
.modal__body a { color: var(--indigo-500); text-decoration: underline; text-underline-offset: 2px; }

/* The original artefact, offered rather than shown. */
.pitem--full { border-style: dashed; }
.pitem--full:hover { border-style: solid; }

/* ---- Full-diagram viewer -------------------------------------------- */

.viewer {
  margin: auto;
  width: min(1200px, calc(100vw - 24px));
  height: min(86dvh, 900px);
  padding: 0;
  border: 1px solid var(--white-10);
  border-radius: 18px;
  background: linear-gradient(to bottom, var(--zinc-900), var(--zinc-950));
  color: var(--text);
  overflow: hidden;
}
.viewer::backdrop { background: rgba(0, 0, 0, 0.72); backdrop-filter: blur(3px); }
.viewer[open] { display: flex; flex-direction: column; }

.viewer__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--white-05);
}
.viewer__note { font-size: 11.5px; color: var(--white-40); }

.viewer__stage {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  display: grid;
  place-items: center;
  cursor: grab;
  touch-action: none;
  background:
    radial-gradient(circle at 50% 40%, rgba(99, 102, 241, 0.06), transparent 60%),
    var(--zinc-950);
}
.viewer__stage:active { cursor: grabbing; }
.viewer__img {
  max-width: 100%;
  max-height: 100%;
  transform-origin: center center;
  /* No transition: it is dragged, and easing every pointermove reads as lag. */
  user-select: none;
  -webkit-user-drag: none;
}

@media (max-width: 560px) {
  .modal { width: calc(100vw - 20px); max-height: 86dvh; }
  .viewer { width: calc(100vw - 12px); height: 90dvh; }
  .modal__head { padding: 16px 16px 12px; }
  .modal__body { padding: 14px 16px 18px; }
  .modal__diagram { padding: 14px 16px 0; }
}

.profile__links { display: flex; flex-direction: column; gap: 6px; }
.plink {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 11px;
  border: 1px solid var(--white-10);
  border-radius: 14px;
  background: rgba(39, 39, 42, 0.55);
  text-decoration: none;
  transition: transform 0.2s var(--ease-out-quint), border-color 0.2s, background-color 0.2s;
}
.plink:hover {
  transform: translateY(-2px);
  border-color: var(--white-20);
  background: rgba(39, 39, 42, 0.8);
}
.plink:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }
.plink__left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.plink__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 31px; height: 31px;
  border-radius: 50%;
  color: #e4e4e7;
  border: 1px solid var(--white-10);
  background: rgba(24, 24, 27, 0.7);
}
.plink__text { display: flex; flex-direction: column; min-width: 0; }
.plink__label { font-size: 12.5px; font-weight: 600; color: #fff; }
.plink__handle {
  font-size: 11px;
  color: var(--white-40);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.plink__arrow { flex: none; color: var(--white-30); transition: transform 0.2s, color 0.2s; }
.plink:hover .plink__arrow { transform: translate(2px, -2px); color: var(--white-60); }

/* ---- Header -------------------------------------------------------- */

.chat__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--white-05);
}
.chat__id { display: flex; align-items: center; gap: 10px; min-width: 0; }
.chat__idtext { min-width: 0; }

.avatar {
  display: grid;
  place-items: center;
  flex: none;
  width: 28px; height: 28px;
  border-radius: 50%;
  color: #fff;
  background: linear-gradient(135deg, var(--indigo-500), var(--violet-600));
}
.avatar--lg { width: 32px; height: 32px; }

.chat__name {
  display: block;
  font-size: 14px; font-weight: 500;
  color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chat__tagline {
  display: block;
  font-size: 12px;
  color: var(--white-40);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.chat__actions { display: flex; align-items: center; gap: 6px; flex: none; }

.pill {
  font: inherit;
  font-size: 12px;
  color: var(--white-60);
  background: var(--white-05);
  border: 0;
  border-radius: 8px;
  padding: 6px 10px;
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, transform 0.12s var(--ease-out-quint);
}
.pill:hover { background: var(--white-10); color: #fff; }
.pill:active { transform: scale(0.95); }
.pill:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }

.pill--btn { display: inline-flex; align-items: center; gap: 6px; }
@media (max-width: 430px) {
  .chat__tagline { display: none; }
  .pill--btn span { display: none; }
}

/* ---- Message log --------------------------------------------------- */

.chat__log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--white-10) transparent;
  /* Deliberately no scroll-behavior: smooth. The typewriter nudges scrollTop
     every frame, and a smooth scroll would spend each frame easing toward a
     target that has already moved, which reads as lag. The steps are one line
     at most, so direct assignment already looks smooth. */
  overscroll-behavior: contain;
}
/* Anchor a short conversation to the bottom so the first message sits just
   above the composer instead of stranded under a tall empty gap. Done with
   margin-top on the first child rather than justify-content: flex-end, which
   makes overflowing content unreachable above the scroll origin. */
.chat__log > :first-child { margin-top: auto; }

.chat__log::-webkit-scrollbar { width: 6px; }
.chat__log::-webkit-scrollbar-track { background: transparent; }
.chat__log::-webkit-scrollbar-thumb { background: var(--white-10); border-radius: 3px; }

.msg { display: flex; width: 100%; }
.msg--them { justify-content: flex-start; }
.msg--me { justify-content: flex-end; }

.msg__row { display: flex; align-items: flex-end; gap: 8px; max-width: 100%; }
.msg--me .msg__row { flex-direction: row-reverse; }

.bubble {
  max-width: 75%;
  padding: 10px 16px;
  font-size: 14px;
  line-height: 1.6;
  border-radius: 16px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  transition: transform 0.2s ease-out;
}
.bubble:hover { transform: translateY(-1px) scale(1.01); }

.msg--me .bubble {
  border-top-right-radius: 6px;
  color: #fff;
  background: linear-gradient(to right, var(--indigo-600), var(--violet-600));
  box-shadow: 0 8px 24px -4px rgba(99, 102, 241, 0.4);
}
.msg--them .bubble {
  border-top-left-radius: 6px;
  color: #e4e4e7;
  background: rgba(39, 39, 42, 0.9);
  border: 1px solid var(--white-10);
  backdrop-filter: blur(4px);
  box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.3);
}
.msg--error .bubble { color: var(--danger); border-color: rgba(248, 113, 113, 0.35); }

/* Entrance: fade up, settle in from the side the bubble belongs to. */
@keyframes msg-in-left {
  from { opacity: 0; transform: translate3d(-20px, 12px, 0) scale(0.96); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}
@keyframes msg-in-right {
  from { opacity: 0; transform: translate3d(20px, 12px, 0) scale(0.96); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}
.msg--them { animation: msg-in-left 0.35s var(--ease-out-quint) both; }
.msg--me   { animation: msg-in-right 0.35s var(--ease-out-quint) both; }

/* ---- Markdown inside assistant bubbles ------------------------------ */

/* The bubble sets white-space: pre-wrap for plain text, including the
   streaming phase. Rendered Markdown brings its own block structure, so the
   pre-wrap has to be switched off or every source newline doubles as a break. */
.bubble:has(p, ul, ol, table, pre) { white-space: normal; }

.bubble > p { margin: 0 0 8px; }
.bubble > p:last-child { margin-bottom: 0; }
.bubble .md-head {
  margin: 14px 0 6px;
  font-size: 12.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--white-60);
}
.bubble > .md-head:first-child { margin-top: 0; }

.bubble ul,
.bubble ol { margin: 0 0 8px; padding-left: 20px; }
.bubble ul:last-child,
.bubble ol:last-child { margin-bottom: 0; }
.bubble li { margin: 2px 0; }
.bubble li::marker { color: var(--white-30); }

.bubble a { color: var(--indigo-500); text-decoration: underline; text-underline-offset: 2px; }
.bubble a:hover { color: #fff; }

.bubble code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--white-10);
}
.bubble pre {
  margin: 0 0 8px;
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--white-10);
  overflow-x: auto;
}
.bubble pre:last-child { margin-bottom: 0; }
.bubble pre code { padding: 0; background: none; font-size: 0.85em; }

/* A wide table scrolls inside the bubble instead of stretching it past the
   chat column. */
.md-table { margin: 0 0 8px; overflow-x: auto; }
.md-table:last-child { margin-bottom: 0; }
.bubble table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
  line-height: 1.5;
}
.bubble th,
.bubble td {
  padding: 6px 10px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--white-10);
  /* The bubble sets overflow-wrap: anywhere so long URLs cannot burst the
     chat column. Inherited into a narrow table cell it breaks ordinary words
     mid-syllable ("Architectu / re"), so cells wrap on spaces only and the
     table scrolls horizontally instead. */
  overflow-wrap: normal;
  word-break: normal;
}
.bubble th {
  font-weight: 600;
  min-width: 96px;
  color: #fff;
  white-space: nowrap;
  border-bottom-color: var(--white-20);
}
.bubble td { color: #d4d4d8; }
.bubble tbody tr:last-child th,
.bubble tbody tr:last-child td { border-bottom: 0; }

/* Structured replies need more room than a chat line. */
.msg--them .bubble:has(table) { max-width: 96%; }

/* ---- Typing indicator ---------------------------------------------- */

.typing {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 12px 16px;
  border-radius: 16px;
  border-top-left-radius: 6px;
  background: rgba(39, 39, 42, 0.9);
  border: 1px solid var(--white-10);
  backdrop-filter: blur(4px);
}
.typing i {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--white-60);
  animation: blink 0.8s infinite ease-in-out;
}
.typing i:nth-child(2) { animation-delay: 0.15s; }
.typing i:nth-child(3) { animation-delay: 0.30s; }
@keyframes blink {
  0%, 100% { opacity: 0.4; transform: translateY(0); }
  50%      { opacity: 1;   transform: translateY(-4px); }
}

/* Caret: a thin bar trailing the text while it types. Removed the moment the
   buffer drains, so a finished answer carries no leftover cursor. */
.caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 1px;
  vertical-align: -0.18em;
  border-radius: 1px;
  background: var(--indigo-500);
  animation: caret-blink 1.05s ease-in-out infinite;
}
@keyframes caret-blink {
  0%, 45%   { opacity: 1; }
  55%, 100% { opacity: 0.15; }
}

/* ---- Shortcut chips ------------------------------------------------- */

.chat__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 16px 12px;
}
.chip {
  font: inherit;
  font-size: 12.5px;
  color: var(--white-60);
  background: var(--white-05);
  border: 1px solid var(--white-10);
  border-radius: 999px;
  padding: 6px 12px;
  cursor: pointer;
  animation: msg-in-left 0.35s var(--ease-out-quint) both;
  transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}
.chip:hover {
  color: #fff;
  border-color: rgba(99, 102, 241, 0.6);
  background: rgba(99, 102, 241, 0.12);
}
.chip:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }
.chip[disabled] { opacity: 0.4; cursor: default; }

/* ---- Composer ------------------------------------------------------- */

.chat__composer { border-top: 1px solid var(--white-05); padding: 12px; }

.composer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 16px;
  border: 1px solid var(--white-10);
  border-radius: 12px;
  background: rgba(39, 39, 42, 0.5);
  backdrop-filter: blur(4px);
  transition: border-color 0.15s, background-color 0.15s;
}
.composer:focus-within {
  border-color: var(--white-20);
  background: rgba(39, 39, 42, 0.7);
}
.composer__input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 14px;
  color: #fff;
  background: transparent;
  border: 0;
  outline: none;
  padding: 6px 0;
}
.composer__input::placeholder { color: var(--white-30); }
.composer__input:disabled { cursor: not-allowed; }

.composer__send {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  border: 0;
  border-radius: 8px;
  color: var(--white-30);
  background: var(--white-05);
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, transform 0.12s var(--ease-out-quint);
}
.composer__send:not(:disabled) { color: #fff; background: var(--indigo-600); }
.composer__send:not(:disabled):hover { background: var(--indigo-500); transform: scale(1.08); }
.composer__send:not(:disabled):active { transform: scale(0.92); }
.composer__send:disabled { cursor: not-allowed; }
.composer__send:focus-visible { outline: 2px solid var(--indigo-500); outline-offset: 2px; }

.chat__note {
  padding-top: 8px;
  font-size: 11px;
  color: var(--white-30);
  text-align: center;
  min-height: 1em;
}

/* ---- Motion preferences --------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .msg--them, .msg--me, .chip { animation: none; }
  .stage, .profile { transition: none; }
  .pitem:hover, .plink:hover, .plink:hover .plink__arrow { transform: none; }
  .pitem--open:hover .pitem__arrow { transform: none; }
  .profile__items.is-fresh > .pitem { animation: none; }
  .typing i { animation: none; opacity: 0.6; }
  .caret { animation: none; opacity: 0.6; }
  .bubble:hover, .pill:active,
  .composer__send:not(:disabled):hover,
  .composer__send:not(:disabled):active { transform: none; }
  .chat__log { scroll-behavior: auto; }
}
