/* chat.css — page layer (owner: S4).
   Loaded after studio.css by base.html.  Tokens only: no colour or length
   literals outside design-tokens.css.

   S4 · the /chat surface is a column that owns the viewport under the sticky
   app header:

     .chat-railbar   — Chat | Memory, the three rail chips, the model caption
     .chat-messages  — the only scroller
     .chat-composer  — the column's last child, pinned above the floating tab bar

   Nothing here hardcodes a viewport height: the column is a flex child with
   `min-height: 0`, and the bottom inset is the tab bar's own token. */

/* ── The column ─────────────────────────────────────────────────────────── */

/* The chat page is the one surface that is not a document.  `:has()` scopes
   every one of these rules to a page that actually draws a .chat-layout, so
   the shell keeps its own behaviour everywhere else. */
.main-content:has(.chat-layout) {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
  overscroll-behavior-y: contain;
  padding-top: 0;
}

/* The header sits at the top of the column, not inset from a padding it no
   longer has. */
.main-content:has(.chat-layout) > .app-header {
  margin-top: 0;
  flex: 0 0 auto;
}

/* Page-shell children above the chat column (the ingest banner, the
   connection mark) keep their natural height; the column takes the rest. */
.main-content:has(.chat-layout) .page-shell {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

.main-content:has(.chat-layout) .page-shell > :not(.chat-layout) {
  flex: 0 0 auto;
}

.chat-layout {
  position: relative;
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
  height: auto;
  width: 100%;
  max-width: 52rem;
  margin: 0 auto;
  padding: 0;
  overscroll-behavior-y: contain;
}

/* ── Rail bar: view tabs, provider chips, model caption ─────────────────── */

.chat-railbar {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2xs);
  padding: var(--space-xs) 0 var(--space-sm);
  border-bottom: var(--hairline) solid var(--border-subtle);
  background: transparent;
}

.chat-railbar-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-xs);
}

.chat-tabs {
  align-self: flex-start;
  background: var(--glass-bg);
  border: var(--hairline) solid var(--glass-border);
}

.chat-tab {
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.02em;
  min-height: var(--tap-min);
}

.chat-tab.is-active {
  background: var(--color-accent);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-sm);
}

.chat-rails {
  gap: var(--space-2xs);
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  max-width: 100%;
  padding-bottom: var(--space-3xs);
}

.chat-rails::-webkit-scrollbar {
  display: none;
}

.chat-rail-chip {
  flex: 0 0 auto;
  gap: var(--space-xs);
  min-height: var(--tap-min);
  padding: var(--space-3xs) var(--space-sm);
  border: var(--hairline) solid var(--glass-border);
  border-radius: var(--radius-full);
  background: var(--glass-bg);
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.01em;
  transition:
    background var(--transition),
    border-color var(--transition),
    color var(--transition);
}

.chat-rail-chip:hover {
  border-color: var(--border-strong);
  background: var(--color-surface-card-hover);
  color: var(--color-text);
}

/* The active rail is the accent fill the rest of the app uses for a chosen
   state; the dot keeps a ring so a green dot on green still reads. */
.chat-rail-chip.is-active,
.chat-rail-chip[aria-pressed="true"] {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-accent);
}

.chat-rail-chip.is-active .chat-rail-dot,
.chat-rail-chip[aria-pressed="true"] .chat-rail-dot {
  box-shadow: 0 0 0 var(--hairline) var(--color-text-inverse);
}

.chat-rail-dot {
  width: var(--space-xs);
  height: var(--space-xs);
  box-shadow: none;
  transition: background var(--transition);
}

.chat-rail-dot[data-state="unknown"] {
  background: var(--color-text-muted);
  opacity: 0.6;
}

.chat-railbar-meta {
  display: flex;
  align-items: center;
  min-width: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
}

/* The model panel is the caption line at rest: one quiet row, no form. */
.chat-railbar-meta .ui-chat-model {
  width: 100%;
  margin: 0;
  border: 0;
  background: transparent;
  padding: 0;
}

.chat-railbar-meta .ui-chat-model-summary {
  justify-content: flex-start;
  gap: var(--space-xs);
  padding: var(--space-3xs) 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* It is the only way into the model picker, so it clears the tap floor. */
  min-height: var(--tap-min);
  text-align: left;
}

.chat-railbar-meta .ui-chat-model-label {
  color: var(--color-text-muted);
}

.chat-railbar-meta .ui-chat-model-current {
  color: var(--color-text-secondary);
  font-family: var(--font-mono);
  font-size: var(--font-size-2xs);
  letter-spacing: 0;
  text-transform: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Transcript ─────────────────────────────────────────────────────────── */

.chat-messages {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-md) 0 var(--space-sm);
  scroll-behavior: smooth;
  /* The welcome shrinks a tier at a time when the transcript is short (a
     phone whose shell banner is taking the height), instead of pushing the
     starter chips under the fold.  The tier rules themselves live at the end
     of this sheet: an @container rule has no extra specificity, so source
     order is what lets it win. */
  container-type: size;
}

.chat-welcome {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  /* auto margins centre the welcome in the transcript while there is room and
     collapse to zero when the transcript is shorter than it — no clipped top
     on a phone whose banner has taken the height. */
  margin-block: auto;
  padding: var(--space-sm) var(--space-xs);
  text-align: center;
}

.chat-welcome-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-2xl);
  height: var(--space-2xl);
  border-radius: var(--radius-full);
  background: var(--accent-gradient);
  color: var(--color-text-inverse);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  box-shadow: var(--shadow-accent);
}

.chat-welcome-title {
  margin: 0;
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  letter-spacing: -0.01em;
  color: var(--color-text);
}

.chat-welcome-copy {
  margin: 0;
  max-width: 34ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-relaxed);
}

/* Starter chips: a short stack of real prompts, each one tap wide enough to
   read on a phone.  They fill the composer — they do not send. */
.chat-suggestions {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  width: 100%;
  max-width: 34rem;
  margin-top: var(--space-xs);
}

.suggestion-chip {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--tap-min);
  padding: var(--space-xs) var(--space-md);
  border: var(--hairline) solid var(--glass-border);
  border-radius: var(--radius-lg);
  background: var(--glass-bg);
  color: var(--color-text-secondary);
  font: inherit;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--transition),
    background var(--transition),
    color var(--transition);
}

.suggestion-chip::before {
  content: "";
  flex: 0 0 auto;
  width: var(--space-2xs);
  height: var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--color-accent);
}

.suggestion-chip:hover,
.suggestion-chip:focus-visible {
  border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
  background: var(--color-accent-dim);
  color: var(--color-text);
}

/* ── Bubbles ────────────────────────────────────────────────────────────── */

.chat-msg {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  max-width: 78%;
  animation: chat-bubble-in var(--transition) var(--transition-ease-out) both;
}

@keyframes chat-bubble-in {
  from {
    opacity: 0;
    transform: translateY(var(--space-2xs));
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.chat-msg.user {
  align-self: flex-end;
  align-items: flex-end;
}

.chat-msg.assistant {
  position: relative;
  align-self: flex-start;
  align-items: flex-start;
  max-width: min(100%, 44rem);
  padding-left: calc(var(--space-lg) + var(--space-xs));
}

/* The avatar mark: a small brand disc in the gutter, so an assistant turn is
   recognisable before a single word is read. */
.chat-msg.assistant::before {
  content: "♪";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: var(--space-md);
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-lg);
  height: var(--space-lg);
  border-radius: var(--radius-full);
  background: var(--color-accent-dim);
  color: var(--color-accent);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  box-shadow: inset 0 0 0 var(--hairline) color-mix(in srgb, var(--color-accent) 40%, transparent);
}

.chat-msg-role {
  display: flex;
  align-items: baseline;
  gap: var(--space-xs);
  padding: 0 var(--space-2xs);
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.chat-msg.user .chat-msg-role {
  color: var(--color-text-muted);
  text-align: right;
}

.chat-msg.assistant .chat-msg-role {
  color: var(--color-text-muted);
}

/* Timestamps are relative ("2 min ago") with the exact instant on hover. */
.chat-msg-time {
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-regular);
  letter-spacing: 0.02em;
  text-transform: none;
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
}

.chat-msg-content {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-xl);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-relaxed);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.chat-msg.user .chat-msg-content {
  border: var(--hairline) solid color-mix(in srgb, var(--color-accent) 32%, transparent);
  background: var(--color-accent-dim);
  color: var(--color-text);
  border-bottom-right-radius: var(--radius-sm);
}

.chat-msg.assistant .chat-msg-content {
  border: var(--hairline) solid var(--glass-border);
  background: var(--glass-bg);
  color: var(--color-text);
  border-bottom-left-radius: var(--radius-sm);
  backdrop-filter: blur(var(--glass-blur)) saturate(140%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%);
}

/* Code the engine returns (text/sql blocks) stays inside the bubble and keeps
   its own surface, in both themes. */
.chat-msg-content pre code {
  background: var(--color-surface);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
}

.chat-msg-content code {
  background: var(--surface-tint-strong);
  border-radius: var(--radius-sm);
  font-size: 0.9em;
}

/* The streaming caret the engine appends, and the "thinking" tail chat-page.js
   puts inside the assistant bubble while a turn is in flight. */
.chat-msg-content.typing-cursor::after {
  content: "";
  display: inline-block;
  width: var(--space-3xs);
  height: 1em;
  margin-inline-start: var(--space-3xs);
  background: var(--color-accent);
  vertical-align: text-bottom;
  animation: chat-caret 1s steps(2, start) infinite;
}

@keyframes chat-caret {
  to {
    visibility: hidden;
  }
}

.chat-typing {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  margin-top: var(--space-2xs);
  padding: 0 var(--space-2xs);
}

.chat-typing-dot {
  width: var(--space-2xs);
  height: var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--color-accent);
  opacity: 0.55;
  animation: chat-typing-pulse 1.1s ease-in-out infinite;
}

.chat-typing-dot:nth-child(2) {
  animation-delay: 0.15s;
}

.chat-typing-dot:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes chat-typing-pulse {
  0%,
  60%,
  100% {
    opacity: 0.25;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(calc(var(--space-3xs) * -1));
  }
}

/* Per-message actions stay quiet until the message is hovered or focused. */
.chat-msg .ui-chat-msg-actions {
  opacity: 0;
  transition: opacity var(--transition);
}

.chat-msg:hover .ui-chat-msg-actions,
.chat-msg:focus-within .ui-chat-msg-actions {
  opacity: 1;
}

/* ── Composer ───────────────────────────────────────────────────────────── */

.chat-composer {
  flex: 0 0 auto;
  position: relative;
  z-index: var(--z-raised);
  padding: var(--space-sm) 0 var(--space-xs);
  border-top: 0;
  background: transparent;
}

.chat-input-form {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3xs);
  width: 100%;
  max-width: none;
}

.chat-composer-field {
  display: flex;
  align-items: flex-end;
  gap: var(--space-xs);
  padding: var(--space-3xs) var(--space-3xs) var(--space-3xs) var(--space-md);
  border: var(--hairline) solid var(--glass-border);
  border-radius: var(--radius-2xl);
  background: var(--glass-bg-strong);
  box-shadow: var(--shadow-float);
  backdrop-filter: blur(var(--glass-blur)) saturate(160%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(160%);
  transition: border-color var(--transition);
}

.chat-composer-field:focus-within {
  border-color: color-mix(in srgb, var(--color-accent) 55%, transparent);
}

.chat-input {
  flex: 1 1 auto;
  min-height: var(--tap-min);
  max-height: calc(var(--space-3xl) * 2);
  padding: var(--space-xs) 0;
  border: 0;
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
  resize: none;
}

.chat-input:focus {
  outline: none;
  border-color: transparent;
}

.chat-input::placeholder {
  color: var(--color-text-muted);
}

.chat-composer .chat-send-btn {
  width: var(--tap-min);
  height: var(--tap-min);
  border: 0;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-accent);
}

.chat-composer .chat-send-btn:disabled {
  background: var(--surface-tint-strong);
  color: var(--color-text-muted);
  box-shadow: none;
  opacity: 1;
}

.chat-composer .chat-stop-btn {
  min-height: var(--tap-min);
}

.chat-composer-hint {
  margin: 0;
  padding: 0 var(--space-sm);
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  letter-spacing: 0.03em;
}

.chat-composer-hint kbd {
  padding: 0 var(--space-3xs);
  border-radius: var(--radius-sm);
  background: var(--surface-tint-strong);
  font-family: var(--font-mono);
  font-size: var(--font-size-2xs);
}

/* ── The error strip sits between the transcript and the composer ───────── */

.chat-layout .chat-error {
  flex: 0 0 auto;
  margin: 0 0 var(--space-2xs);
}

/* ── Wide viewports: one row of chrome, a readable column ───────────────── */

@media (min-width: 768px) {
  .main-content:has(.chat-layout) {
    padding-bottom: var(--space-lg);
  }

  .chat-railbar-row {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  .chat-rails {
    flex-wrap: wrap;
    overflow-x: visible;
    justify-content: flex-end;
  }

  .chat-msg {
    max-width: 78%;
  }

  .chat-composer {
    padding-bottom: var(--space-sm);
  }
}

@media (max-width: 767px) {
  /* The tab bar floats 8px off the bottom edge; the composer clears it by one
     --space-sm step, exactly as the brief specifies. */
  .main-content:has(.chat-layout) {
    padding-bottom: calc(
      var(--tabbar-height) + var(--space-xs) + var(--space-sm) +
        env(safe-area-inset-bottom)
    );
  }

  .chat-msg {
    max-width: 88%;
  }

  .chat-composer-hint {
    display: none;
  }

  .chat-welcome {
    padding: var(--space-md) 0;
  }
}

/* Container queries last: an @container rule carries no extra specificity, so
   it has to come after the plain rules it overrides (source order decides). */

/* ── Tiered welcome (container queries, hence last) ─────────────────────── */

@container (max-height: 26rem) {
  .chat-welcome-mark {
    display: none;
  }
}

/* The explanatory line stays: it is the only sentence on the page that says
   what the surface is for, and hiding it in a short column left a bare heading
   (the chat area scrolls, so a tight fit costs nothing). */
@container (max-height: 11rem) {
  .chat-welcome-mark {
    display: none;
  }
}

@container (max-height: 18rem) {
  .chat-welcome {
    justify-content: flex-start;
    padding-block: 0;
  }

  .chat-welcome-title {
    font-size: var(--font-size-md);
  }

  .chat-suggestions {
    gap: var(--space-2xs);
  }

  .suggestion-chip {
    min-height: var(--tap-min);
    padding-block: var(--space-3xs);
  }
}

/* The rail a turn takes when nothing has been chosen explicitly: same weight as
   the active chip, with a quiet "default" mark so the two are distinguishable. */
.chat-rail-chip[data-rail-default="true"]::after {
  content: "auto";
  margin-inline-start: var(--space-2xs);
  padding: 0 var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--surface-tint-strong);
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ── S3 · the playlist card the write tool streams ───────────────────────── */
/*
  Tokens only, as the page layer requires: colours through var(--color-*), every
  length through var(--space-*) / var(--radius-*) / a viewport unit.  The one
  non-token value is the spring curve, which is a *shape*, not a size or a
  colour — it is what makes the pill and the card feel like the rest of the
  surface rather than a late arrival.
*/

@keyframes chat-playlist-in {
  from {
    opacity: 0;
    transform: translateY(calc(var(--space-2xs) * -1)) scale(0.985);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.chat-playlist-slot {
  display: block;
  margin-block-start: var(--space-sm);
}

.chat-playlist-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--glass-bg);
  box-shadow: var(--shadow-card);
  animation: chat-playlist-in var(--transition-slow) cubic-bezier(0.34, 1.4, 0.64, 1) both;
}

.chat-playlist-head {
  display: flex;
  gap: var(--space-sm);
  align-items: flex-start;
}

.chat-playlist-covers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3xs);
  flex: 0 0 auto;
  inline-size: var(--tap-comfortable);
  block-size: var(--tap-comfortable);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface-tint);
}

.chat-playlist-covers[data-covers="1"] {
  grid-template-columns: minmax(0, 1fr);
}

.chat-playlist-cover {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

.chat-playlist-cover-empty {
  display: grid;
  place-items: center;
  grid-column: 1 / -1;
  color: var(--color-text-muted);
  font-size: var(--font-size-lg);
}

.chat-playlist-meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  min-inline-size: 0;
}

.chat-playlist-kicker {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.chat-playlist-title {
  margin: 0;
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
  color: var(--color-text);
}

.chat-playlist-sub,
.chat-playlist-count {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

.chat-playlist-reason {
  margin: 0;
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius-sm);
  background: var(--color-danger-dim);
  color: var(--color-text);
  font-size: var(--font-size-sm);
}

.chat-playlist-tracks {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  margin: 0;
  padding: 0;
  list-style: none;
  max-block-size: 40vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.chat-playlist-track {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: var(--radius-sm);
  transition: background var(--transition-fast);
}

.chat-playlist-track:hover {
  background: var(--surface-tint);
}

.chat-playlist-track-index {
  inline-size: var(--space-lg);
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  text-align: end;
  font-variant-numeric: tabular-nums;
}

.chat-playlist-track-meta {
  display: flex;
  flex-direction: column;
  min-inline-size: 0;
  flex: 1 1 auto;
}

.chat-playlist-track-name {
  font-size: var(--font-size-sm);
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-playlist-track-artist,
.chat-playlist-track-time {
  font-size: var(--font-size-2xs);
  color: var(--color-text-muted);
}

.chat-playlist-track-actions {
  display: flex;
  gap: var(--space-3xs);
  flex: 0 0 auto;
}

.chat-playlist-track-btn {
  min-inline-size: var(--tap-min);
  min-block-size: var(--tap-min);
  justify-content: center;
}

.chat-playlist-track-btn[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

.chat-playlist-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.chat-playlist-action {
  min-height: var(--tap-min);
}

.chat-playlist-fallback {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  overflow-wrap: anywhere;
}

/* ── S3 · the card's full-screen viewer ─────────────────────────────────── */

.chat-playlist-viewer {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  padding: var(--space-md);
  background: var(--scrim);
  animation: chat-playlist-in var(--transition) var(--transition-ease-out) both;
}

.chat-playlist-viewer-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  inline-size: min(100%, var(--content-max));
  max-block-size: 85vh;
  padding: var(--space-lg);
  border: var(--hairline) solid var(--glass-border);
  border-radius: var(--radius-xl);
  background: var(--bg-card);
  box-shadow: var(--shadow-float);
}

.chat-playlist-viewer-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
}

.chat-playlist-viewer-title {
  margin: 0;
  flex: 1 1 auto;
  font-size: var(--font-size-xl);
  line-height: var(--line-height-tight);
}

.chat-playlist-viewer-meta {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}

.chat-playlist-viewer-close {
  flex: 0 0 auto;
}

.chat-playlist-viewer-tracks {
  flex: 1 1 auto;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.chat-playlist-viewer-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

/* ── S3 · tool-progress pills ───────────────────────────────────────────── */

@keyframes chat-tool-shimmer {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(100%);
  }
}

.chat-tool-progress {
  position: relative;
  align-self: flex-start;
  max-inline-size: 100%;
  margin-block-end: var(--space-2xs);
  padding: var(--space-3xs) var(--space-sm);
  overflow: hidden;
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-full);
  background: var(--surface-tint);
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
  transition:
    opacity var(--transition-fast),
    transform var(--transition) cubic-bezier(0.34, 1.4, 0.64, 1);
}

/* The shimmer is a swept highlight rather than an animated background, so it
   costs one composited layer and no repaint of the text. */
.chat-tool-progress::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 20%,
    var(--skeleton-highlight) 50%,
    transparent 80%
  );
  animation: chat-tool-shimmer 1.4s linear infinite;
  pointer-events: none;
}

.chat-tool-progress:empty {
  display: none;
}

.chat-memory-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--surface-tint);
  color: var(--color-text-secondary);
  font-size: var(--font-size-2xs);
}

@media (prefers-reduced-motion: reduce) {
  .chat-playlist-card,
  .chat-playlist-viewer {
    animation: none;
  }

  .chat-tool-progress::after {
    animation: none;
    opacity: 0;
  }

  .chat-tool-progress {
    transition: none;
  }
}

/* ── S3 · the searchable model picker ───────────────────────────────────── */

.ui-chat-model-picker {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-block-end: var(--space-sm);
}

/* Active Model Pill */
.ui-chat-model-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-3xs) var(--space-xs);
  border-radius: var(--radius-full);
  background: var(--surface-tint);
  border: var(--hairline) solid var(--border-subtle);
  color: var(--color-text);
  font-size: var(--font-size-2xs);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.ui-chat-model-pill:hover {
  background: var(--surface-tint-strong);
  border-color: var(--border-strong);
}

.ui-chat-model-pill-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-success, #22c55e);
}

/* Bottom Sheet Presentation */
.ui-chat-model[open] .ui-chat-model-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: 1000;
  opacity: 1;
}

.ui-chat-model[open] .ui-chat-model-picker {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: 640px;
  margin: 0 auto;
  max-height: min(78vh, 600px);
  background: var(--surface-elevated, #181818);
  border-top-left-radius: var(--radius-lg, 16px);
  border-top-right-radius: var(--radius-lg, 16px);
  border-top: var(--hairline) solid var(--border-strong);
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.6);
  padding: var(--space-sm) var(--space-md) calc(var(--space-md) + env(safe-area-inset-bottom, 0px));
  z-index: 1001;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  animation: slideUpSheet 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes slideUpSheet {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

.ui-chat-model-sheet-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  margin-bottom: var(--space-3xs);
}

.ui-chat-model-sheet-grip {
  width: 36px;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-text-muted);
  opacity: 0.5;
}

.ui-chat-model-sheet-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.ui-chat-model-sheet-title {
  margin: 0;
  font-size: var(--font-size-md, 1rem);
  font-weight: 600;
  color: var(--color-text);
}

.ui-chat-model-sheet-close {
  padding: var(--space-3xs) var(--space-xs);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.ui-chat-model-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs);
}

.ui-chat-model-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: var(--tap-min);
  min-block-size: var(--tap-min);
  padding: var(--space-3xs) var(--space-2xs);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-secondary);
  font-size: var(--font-size-2xs);
  cursor: pointer;
  transition:
    background var(--transition-fast),
    border-color var(--transition-fast),
    color var(--transition-fast);
}

.ui-chat-model-tab:hover,
.ui-chat-model-tab:focus-visible {
  border-color: var(--border-strong);
  color: var(--color-text);
}

.ui-chat-model-tab.is-active {
  background: var(--surface-tint-strong);
  border-color: var(--accent);
  color: var(--color-text);
}

.ui-chat-model-search {
  inline-size: 100%;
  min-height: var(--tap-min);
}

.ui-chat-model-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  max-block-size: 40vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.ui-chat-model-option {
  display: flex;
  align-items: baseline;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-sm);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-sm);
  background: var(--surface-tint);
  color: var(--color-text);
  text-align: start;
  cursor: pointer;
  min-height: var(--tap-min);
  transition:
    background var(--transition-fast),
    border-color var(--transition-fast);
}

.ui-chat-model-option:hover,
.ui-chat-model-option:focus-visible {
  border-color: var(--border-strong);
  background: var(--surface-tint-strong);
}

.ui-chat-model-option.is-active {
  border-color: var(--accent);
}

.ui-chat-model-option-name {
  font-size: var(--font-size-sm);
}

.ui-chat-model-option-id {
  font-family: var(--font-mono);
  font-size: var(--font-size-2xs);
  color: var(--color-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ui-chat-model-option-hint {
  margin-inline-start: auto;
  color: var(--color-warning);
  font-size: var(--font-size-2xs);
}

.ui-chat-model-empty {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-xs);
}

/* ── L2 · the composer is the page ─────────────────────────────────────────
   The transcript is where the answer lives; the box is where the owner lives.
   So the dock is a little more present than the card it grew out of: the same
   glass, a real shadow, and one row of tools that says what can be done
   without typing.  Tokens only. */

.chat-composer {
  padding-bottom: calc(var(--space-xs) + env(safe-area-inset-bottom));
}

.chat-composer-row {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2xs);
  width: 100%;
}

.chat-composer-field {
  flex: 1 1 auto;
  min-width: 0;
  transition:
    border-color var(--transition),
    box-shadow var(--transition);
}

.chat-composer-field:focus-within {
  box-shadow:
    var(--shadow-float),
    0 0 0 var(--hairline) color-mix(in srgb, var(--color-accent) 35%, transparent);
}

.chat-input {
  max-height: calc(var(--font-size-sm) * var(--line-height-normal) * 6);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The send button *becomes* the stop button: the slot never moves, only what
   fills it changes.  Both roles stay in the DOM for the streaming tests. */
[data-chat-streaming="true"] .chat-composer .chat-send-btn {
  display: none;
}

[data-chat-streaming="true"] .chat-composer .chat-stop-btn {
  display: inline-flex;
  align-items: center;
}

/* ── The leading + and its menu ─────────────────────────────────────────── */

.chat-plus {
  position: relative;
  flex: 0 0 auto;
}

.chat-plus-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border: var(--hairline) solid var(--glass-border);
  border-radius: var(--radius-full);
  background: var(--glass-bg-strong);
  color: var(--color-text-secondary);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-tight);
  transition:
    border-color var(--transition),
    color var(--transition);
}

.chat-plus-btn:hover,
.chat-plus-btn[aria-expanded="true"] {
  border-color: color-mix(in srgb, var(--color-accent) 55%, transparent);
  color: var(--color-text);
}

.chat-plus-menu {
  position: absolute;
  bottom: calc(var(--tap-min) + var(--space-2xs));
  left: 0;
  z-index: var(--z-raised);
  display: flex;
  flex-direction: column;
  min-width: calc(var(--space-3xl) * 2);
  padding: var(--space-2xs);
  border: var(--hairline) solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-float);
}

.chat-plus-menu[hidden] {
  display: none;
}

.chat-plus-item {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-sm);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text);
  font-size: var(--font-size-sm);
  text-align: start;
}

.chat-plus-item:hover,
.chat-plus-item:focus-visible {
  background: var(--color-surface-card-hover);
}

/* ── Command chips and the model pill ───────────────────────────────────── */

.chat-composer-tools {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
  width: 100%;
  padding: 0 var(--space-2xs);
}

.chat-command-chips {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  overflow-x: auto;
  scrollbar-width: none;
  padding-block: var(--space-3xs);
}

.chat-command-chips::-webkit-scrollbar {
  display: none;
}

.chat-command-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-inline-size: var(--tap-min);
  min-block-size: var(--tap-min);
  padding: var(--space-3xs) var(--space-sm);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  letter-spacing: 0.02em;
  white-space: nowrap;
  transition:
    border-color var(--transition),
    background var(--transition),
    color var(--transition);
}

.chat-command-chip[data-active="true"],
.chat-command-chip.is-active {
  border-color: color-mix(in srgb, var(--color-accent) 55%, transparent);
  background: var(--color-accent-dim);
  color: var(--color-text);
}

.chat-composer-model {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  min-inline-size: var(--tap-min);
  min-block-size: var(--tap-min);
  max-width: calc(var(--space-3xl) * 2);
  overflow: hidden;
  padding: var(--space-3xs) var(--space-sm);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-full);
  background: var(--surface-tint-strong);
  color: var(--color-text-secondary);
  font-family: var(--font-mono);
  font-size: var(--font-size-2xs);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-composer-model:hover,
.chat-composer-model:focus-visible {
  border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
  color: var(--color-text);
}

/* ── The progress strip ─────────────────────────────────────────────────── */

.chat-progress {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  width: 100%;
  padding: 0 var(--space-sm) var(--space-2xs);
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  letter-spacing: 0.03em;
}

.chat-progress[hidden] {
  display: none;
}

.chat-progress-dot {
  width: var(--space-2xs);
  height: var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--color-accent);
  animation: chat-progress-pulse 1.2s ease-in-out infinite;
}

.chat-progress-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

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

/* ── Starters: a claim, and the data behind it ──────────────────────────── */

.chat-suggestions .suggestion-chip,
.chat-action-chip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3xs);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--space-xs) var(--space-md);
  border: var(--hairline) solid var(--glass-border);
  border-radius: var(--radius-lg);
  background: var(--glass-bg);
  color: var(--color-text);
  text-align: start;
  transition:
    border-color var(--transition),
    background var(--transition);
}

.chat-suggestions .suggestion-chip::before {
  /* The dot the old chips used reads as a bullet next to two lines of text;
     the derived chips lead with their label instead. */
  display: none;
}

.chat-action-chip {
  border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
  background: var(--color-accent-dim);
}

.chat-action-chip:hover,
.chat-action-chip:focus-visible {
  border-color: var(--color-accent);
}

.suggestion-label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
}

.suggestion-why {
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
  line-height: var(--line-height-normal);
}

.chat-suggestions-note {
  max-width: 34rem;
  margin: var(--space-2xs) 0 0;
  padding: 0 var(--space-md);
  color: var(--color-text-muted);
  font-size: var(--font-size-2xs);
}

/* ── A toast: the confirmation for an act, not a second transcript ──────── */

.chat-toast {
  position: fixed;
  bottom: calc(var(--tabbar-height) + var(--space-lg));
  left: 50%;
  z-index: var(--z-overlay);
  max-width: min(calc(var(--space-3xl) * 12), calc(100vw - var(--space-xl)));
  padding: var(--space-xs) var(--space-md);
  border: var(--hairline) solid var(--border-color);
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-float);
  color: var(--color-text);
  font-size: var(--font-size-sm);
  transform: translateX(-50%);
  animation: chat-toast-in var(--transition) ease-out;
}

.chat-toast[data-tone="ok"] {
  border-color: color-mix(in srgb, var(--color-success) 45%, transparent);
}

@keyframes chat-toast-in {
  from {
    opacity: 0;
    transform: translate(-50%, var(--space-2xs));
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

.chat-error-settings {
  margin-inline-start: var(--space-sm);
  color: var(--color-accent);
  font-size: var(--font-size-xs);
  text-decoration: underline;
}

/* ── Sheet: the keyboard, not the window, decides the bottom edge ───────── */

[data-chat-surface="sheet"][data-chat-keyboard="open"] {
  bottom: var(--chat-sheet-keyboard-inset, 0);
  max-height: calc(100dvh - var(--chat-sheet-keyboard-inset, 0));
}

[data-chat-surface="sheet"] .ui-chat-sheet-input {
  padding-bottom: calc(var(--space-xs) + env(safe-area-inset-bottom));
}

/* ── Phones ─────────────────────────────────────────────────────────────── */

@media (max-width: 767px) {
  .chat-composer-tools {
    padding-inline: 0;
  }

  .chat-composer-model {
    max-width: calc(var(--space-3xl) * 1.5);
  }

  .chat-toast {
    bottom: calc(var(--tabbar-height) + var(--space-md));
  }
}

/* ── The shell's own mini player is fixed to the viewport ────────────────
   ``player.js`` mounts a root-level dock that no page lane owns.  When it is
   present it floats over the bottom edge, which on this column means over the
   composer: the dock is the one thing the owner is typing into, so it keeps
   its height and the column gives the shell's chrome its own space instead.
   The presence test is ``:has()``, so a document without the dock pays
   nothing. */
body:has(.mini-player) .main-content:has(.chat-layout) .chat-layout {
  padding-bottom: calc(var(--space-3xl) * 1.5);
}

@media (max-width: 767px) {
  body:has(.mini-player) .main-content:has(.chat-layout) {
    padding-bottom: calc(
      var(--tabbar-height) + var(--space-3xl) + var(--space-xs) +
        var(--space-sm) + env(safe-area-inset-bottom)
    );
  }
}

/* ── M1 · Android virtual keyboard open on the /chat page ──────────────── */

.chat-layout[data-chat-keyboard="open"] {
  padding-bottom: calc(var(--chat-keyboard-inset-num, 0) * 1px) !important;
}

@media (max-width: 767px) {
  body.is-keyboard-open .main-content:has(.chat-layout) {
    padding-bottom: var(--space-2xs) !important;
  }

  body.is-keyboard-open .tabbar,
  body.is-keyboard-open .mini-player {
    opacity: 0;
    pointer-events: none;
  }

  .chat-layout[data-chat-keyboard="open"] .chat-welcome {
    display: none;
  }
}
