/* ===========================================================================
   Sofía — design system
   A clean, modern, friendly messaging UI. Everything is driven by CSS custom
   properties so the dark theme is a single block of overrides at the bottom.
   =========================================================================== */

:root {
  /* Surfaces & ink */
  --bg: #eef0f4;
  --bg-grad-1: #eef1f6;
  --bg-grad-2: #e7eaf0;
  --surface: #ffffff;
  --surface-2: #f5f6f9;
  --surface-3: #eceef2;
  --ink: #1d2127;
  --ink-soft: #3c424c;
  --muted: #8b919c;
  --border: #e6e8ee;
  --border-strong: #d7dae2;

  /* Brand */
  --accent: #ef6c47;
  --accent-2: #e6512a;
  --accent-grad: linear-gradient(135deg, #ff7e54 0%, #e6512a 100%);
  --accent-soft: #fdeee8;
  --on-accent: #ffffff;

  /* Bubbles */
  --bubble-bot: #f1f3f7;
  --bubble-bot-ink: #1d2127;
  --bubble-user: var(--accent-grad);
  --bubble-user-ink: #ffffff;

  /* Semantic */
  --good: #2f9e57;
  --good-soft: #e7f6ec;
  --bad: #df4f3a;
  --bad-soft: #fdebe8;
  --gold: #e0a93a;
  --gold-soft: #fff3d4;

  /* Shape & motion */
  --radius-sm: 10px;
  --radius: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(20, 24, 33, 0.06), 0 1px 3px rgba(20, 24, 33, 0.04);
  --shadow-md: 0 4px 12px rgba(20, 24, 33, 0.08), 0 2px 4px rgba(20, 24, 33, 0.04);
  --shadow-lg: 0 12px 32px rgba(20, 24, 33, 0.14), 0 4px 10px rgba(20, 24, 33, 0.07);
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif;

  font-size: 16px;
  color-scheme: light;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  background: radial-gradient(1200px 800px at 80% -10%, var(--bg-grad-1), var(--bg-grad-2));
  background-attachment: fixed;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Disable double-tap-to-zoom (iOS Safari ignores user-scalable=no for this).
     `manipulation` keeps panning/scrolling while dropping the zoom gesture. */
  touch-action: manipulation;
}

/* ---------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------- */

/* The `hidden` attribute must always win, even over the id/class `display`
   rules below (otherwise e.g. `#auth-screen { display: flex }` keeps the auth
   gate visible after login). See MDN's note on the hidden attribute. */
[hidden] {
  display: none !important;
}

.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 1.25rem;
  max-width: 1140px;
  margin: 0 auto;
  padding: 1.25rem;
  height: 100vh;
  height: 100dvh;
}

#chat-pane {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

/* --- Brand header ---------------------------------------------------------- */

#chat-header {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, var(--surface), var(--surface));
}
.chat-header-meta { display: flex; flex-direction: column; line-height: 1.25; }
.chat-header-meta strong { font-size: 1.02rem; letter-spacing: -0.01em; }
.status {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.76rem;
  color: var(--muted);
}
.status-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 0 0 rgba(47, 158, 87, 0.5);
  animation: pulse-dot 2.4s var(--ease) infinite;
}
@keyframes pulse-dot {
  0% { box-shadow: 0 0 0 0 rgba(47, 158, 87, 0.45); }
  70% { box-shadow: 0 0 0 6px rgba(47, 158, 87, 0); }
  100% { box-shadow: 0 0 0 0 rgba(47, 158, 87, 0); }
}

.avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: var(--accent-grad);
  color: var(--on-accent);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: 0.01em;
  box-shadow: var(--shadow-sm);
  user-select: none;
}
.avatar-lg { width: 3.1rem; height: 3.1rem; font-size: 1.3rem; }

/* ---------------------------------------------------------------------------
   Progress strip
   --------------------------------------------------------------------------- */

#progress-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.6rem 1rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 0.76rem;
  color: var(--ink-soft);
}
#logout {
  flex: none;
  margin-left: auto;
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.25rem 0.6rem;
  font: inherit;
  font-size: 0.78rem;
  color: var(--muted);
  cursor: pointer;
}
#logout:hover { color: var(--ink); border-color: var(--muted); }

#settings-open {
  flex: none;
  margin-left: auto;
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.25rem 0.6rem;
  font: inherit;
  font-size: 0.78rem;
  color: var(--muted);
  cursor: pointer;
}
#settings-open + #logout { margin-left: 0.4rem; }
#settings-open:hover { color: var(--ink); border-color: var(--muted); }

/* TESTING ONLY: the "next day" fast-forward pill (hidden in production —
   main.ts reveals it on PR-preview and dev deployments). Dashed accent
   styling so it reads as a testing control, not product UI. */
#preview-next-day {
  flex: none;
  margin-left: auto;
  background: none;
  border: 1px dashed var(--accent);
  border-radius: 999px;
  padding: 0.25rem 0.6rem;
  font: inherit;
  font-size: 0.78rem;
  color: var(--accent);
  cursor: pointer;
}
#preview-next-day:hover { color: var(--accent-2); border-color: var(--accent-2); }
#preview-next-day:disabled { opacity: 0.5; cursor: default; }
/* When the preview pill is visible it takes over the auto margin that pushes
   the header's button group right, so Settings falls back to normal spacing. */
#preview-next-day:not([hidden]) ~ #settings-open { margin-left: 0.4rem; }

/* --- Auth screen --- */
#auth-screen {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  padding: 1rem;
}
.auth-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  width: 100%;
  max-width: 340px;
  text-align: center;
}
.auth-card h1 { margin: 0; font-size: 1.25rem; }
.auth-card #auth-message { margin: 0; }
/* Clerk injects its own card chrome; let it set the width it was designed for. */
#clerk-signin { display: flex; justify-content: center; width: 100%; }

/* Anonymous-placement sign-up wall — a plain blurred overlay (like the upgrade
   modal, so Clerk's portaled elements stack correctly), leaving the learner's
   chat shell visible-but-blurred behind it. Two steps: a congrats card, then
   Clerk's own <SignUp/> card. */
#signup-modal {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(20, 24, 33, 0.45);
  backdrop-filter: blur(3px);
}
.signup-card {
  width: 100%;
  max-width: 400px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}
#signup-modal:not([hidden]) .signup-card { animation: modal-in 0.25s var(--ease) both; }
#signup-intro {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 1.75rem 1.5rem;
  text-align: center;
}
.signup-title { margin: 0 0 0.5rem; font-size: 1.35rem; font-weight: 700; color: var(--ink); }
#signup-intro .signup-message { margin: 0 0 1.25rem; }
#signup-intro #signup-cta { width: 100%; }
#signup-clerk-view { width: 100%; display: flex; flex-direction: column; align-items: center; gap: 0.75rem; }
#signup-clerk-view .signup-message { margin: 0; text-align: center; color: #fff; }
/* Clerk injects its own card chrome; let it set the width it was designed for. */
#signup-clerk { display: flex; justify-content: center; width: 100%; }
#progress-strip b { color: var(--ink); }

/* --- Daily goal banner ---
   The headline of the strip: hitting today's due goal is *the* thing the
   learner is trying to achieve, so it gets a full-width banner (title, count,
   streak, and a big progress bar) above the level banner. Clicking it opens
   the due-today popup. */
.daily-goal {
  flex: 1 1 100%;
  /* Shrinkable below its one-line text, or narrow screens clip its right edge. */
  min-width: 0;
  /* Anchor for the floating "+1" each review releases (.goal-plus). */
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.55rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease), border-color 0.12s var(--ease);
}
.daily-goal:hover { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.daily-goal:active { transform: translateY(0); }
/* The row may wrap *between* its three pieces on narrow screens (the streak
   drops to its own line), but each piece stays on one line — never mid-phrase. */
.daily-goal-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 0.6rem;
  row-gap: 0.15rem;
}
.daily-goal-title {
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--ink);
  white-space: nowrap;
}
.daily-goal-status {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.daily-goal-streak {
  margin-left: auto;
  white-space: nowrap;
}
.daily-goal-bar,
.level-bar {
  height: 0.5rem;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
}
.daily-goal-bar span,
.level-bar span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent-grad);
  transition: width 0.6s var(--ease);
}
.daily-goal--done {
  border-color: var(--gold);
  background: var(--gold-soft);
}
.daily-goal--done .daily-goal-bar span {
  background: linear-gradient(135deg, #f0c052, #d99a1f);
}
/* Every review pumps the goal stat: the count pops, the fill flashes, and a
   "+1" floats up from the tip of the fill. */
.daily-goal-status.pct-tick { animation: streak-tick-pop 0.55s var(--ease); }
.daily-goal-bar.goal-bar-pulse span { animation: bar-glow 0.9s var(--ease); }
.goal-plus {
  position: absolute;
  bottom: 0.2rem;
  transform: translateX(-50%);
  font-size: 0.82rem;
  font-weight: 800;
  color: var(--accent-2);
  pointer-events: none;
  animation: goal-plus-float 1.1s var(--ease) forwards;
}
.daily-goal--done .goal-plus { color: #d99a1f; }
@keyframes goal-plus-float {
  0% { opacity: 0; transform: translate(-50%, 4px); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -18px); }
}

/* --- Level banner ---
   The CEFR level as the second full-width bar: the current level, the distance
   to the next one, and a fill that moves with every percentage point. Tapping
   it opens the tooltip explaining what the next level still needs. */
.level-banner {
  flex: 1 1 100%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.55rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  position: relative;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease);
}
.level-banner:hover { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.level-banner:active { transform: translateY(0); }
/* Lift the banner above later-in-DOM content (e.g. messages, whose animations
   create their own stacking contexts) so its tooltip is on top. */
.level-banner:hover,
.level-banner.tooltip-open { z-index: 1001; }
.level-banner-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 0.6rem;
  row-gap: 0.15rem;
}
.level-banner-title {
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--ink);
  white-space: nowrap;
}
.level-banner-status {
  margin-left: auto;
  position: relative;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.level-bar span { background: linear-gradient(135deg, #6aa5f8, #3d7de0); }
/* Locked level chip (multi-day placement calibration): no definitive level or
   next-level bar yet — just the unlock countdown, plus the testing-only
   "advance a day" control on PR previews and the dev deployment. */
.level-banner.level-locked .level-banner-title { color: var(--ink-soft); }
.level-banner.level-locked #advance-day-btn {
  margin-left: auto;
  font-size: 0.72rem;
  padding: 0.15rem 0.5rem;
  white-space: nowrap;
}
/* A gained percentage point: the label pops and the fill flashes brighter. */
.level-banner-status.pct-tick { animation: streak-tick-pop 0.55s var(--ease); }
.level-bar.level-bar-pulse span { animation: bar-glow 0.9s var(--ease); }
@keyframes bar-glow {
  0% { filter: brightness(1); }
  40% { filter: brightness(1.4); }
  100% { filter: brightness(1); }
}
.level-banner[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  /* The global `* { box-sizing }` reset doesn't match pseudo-elements, so make
     the padding count inside the width — otherwise the JS width cap below sizes
     only the text and the padded bubble still overflows the viewport edge. */
  box-sizing: border-box;
  top: calc(100% + 8px);
  left: 0;
  background: var(--ink);
  color: #fff;
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius-sm);
  font-size: 0.72rem;
  line-height: 1.45;
  white-space: normal;
  /* Default 250px, but JS caps it to the space remaining to the right of the
     pill so the bubble stays left-aligned with the pill without running off
     the right edge on narrow screens. */
  width: var(--tooltip-w, 250px);
  pointer-events: none;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.14s var(--ease), transform 0.14s var(--ease);
  z-index: 1000;
  box-shadow: var(--shadow-lg);
}
.level-banner[data-tooltip].tooltip-open::after { opacity: 1; transform: translateY(0); }
/* ---------------------------------------------------------------------------
   Messages
   --------------------------------------------------------------------------- */

#messages {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 1.25rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  scroll-behavior: smooth;
  overscroll-behavior: contain;
}

.msg {
  max-width: 76%;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  animation: msg-in 0.26s var(--ease) both;
}
.msg.user { align-self: flex-end; align-items: flex-end; }
.msg.bot { align-self: flex-start; align-items: flex-start; }
@keyframes msg-in {
  from { opacity: 0; transform: translateY(8px) scale(0.99); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.msg .bubble {
  padding: 0.62rem 0.9rem;
  border-radius: var(--radius-lg);
  line-height: 1.48;
  font-size: 0.97rem;
  word-wrap: break-word;
  box-shadow: var(--shadow-sm);
}
.msg.bot .bubble {
  background: var(--bubble-bot);
  color: var(--bubble-bot-ink);
  border-bottom-left-radius: 6px;
}
.msg.user .bubble {
  background: var(--bubble-user);
  color: var(--bubble-user-ink);
  border-bottom-right-radius: 6px;
}

/* Bot messages carry a coloured "?" button sitting just to the right of the
   bubble. The bubble and button share a horizontal row so the button reads as a
   clear, obvious affordance beside the message. */
.bubble-row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  max-width: 100%;
}
.msg-help {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: background 0.15s var(--ease), transform 0.1s var(--ease);
}
.msg-help:hover { background: var(--accent-2); }
.msg-help:active { transform: scale(0.9); }

/* The learner's message as the bot would spell it — visible target-language
   "autocorrect" (accents, spelling, punctuation) under their bubble, with the
   words that changed highlighted. */
.msg .correction {
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--muted);
  padding: 0.1rem 0.35rem 0;
  max-width: 100%;
  word-wrap: break-word;
}
.correction-mark {
  color: var(--good);
  font-weight: 700;
  margin-right: 0.3rem;
}
.correction-fix {
  color: var(--good);
  font-weight: 600;
}

.msg-foot {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0 0.15rem;
}
.msg-time {
  font-size: 0.66rem;
  color: var(--muted);
  padding: 0 0.35rem;
  letter-spacing: 0.02em;
}

/* Per-message "play" (text-to-speech) button — mirrors .chip button. */
.play {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  cursor: pointer;
  font-size: 0.62rem;
  line-height: 1;
  background: var(--surface-3);
  color: var(--ink-soft);
  transition: background 0.15s var(--ease), color 0.15s var(--ease), transform 0.1s var(--ease);
}
.play:hover { background: var(--accent-soft); color: var(--accent-2); }
.play:active { transform: scale(0.9); }
.play.playing { background: var(--accent-soft); color: var(--accent-2); }
.play.loading { animation: chip-pulse 1.2s ease-in-out infinite; cursor: default; }
.play.error { background: var(--bad-soft); color: var(--bad); }

/* "?" affordance inset INSIDE the composer field, like a leading icon at the
   bottom-left. Starts as a round nudge button; the first tap expands it into a
   "send an automatic reply" pill (which overlays the empty field), the second
   tap sends. Absolutely positioned so the textarea text flows past it. */
.auto-reply {
  position: absolute;
  left: 0.4rem;
  bottom: 0.4rem;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 1.9rem;
  min-width: 1.9rem;
  /* Always leave a strip of the field to the right of the (expanded) pill so it
     can be tapped to dismiss / focus the input. */
  max-width: calc(100% - 3rem);
  padding: 0;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: var(--surface-3);
  color: var(--ink-soft);
  box-shadow: var(--shadow-sm);
  transition: background 0.15s var(--ease), color 0.15s var(--ease), transform 0.1s var(--ease);
}
.auto-reply:hover { background: var(--accent-soft); color: var(--accent-2); }
.auto-reply:active { transform: scale(0.95); }
/* Expanded, the pill grows to fit its call-to-action but stays left-anchored
   and content-sized — never spanning the whole field — so the rest of the
   field stays tappable: clicking it (to dismiss) or starting to type focuses
   the textarea, which collapses the pill. When the copy is too long for the
   width left (narrow screens), it wraps onto a second line and the pill grows
   upward like a popover rather than truncating. */
.auto-reply.expanded {
  height: auto;
  min-height: 1.9rem;
  justify-content: flex-start;
  text-align: left;
  white-space: normal;
  overflow: visible;
  border-radius: 0.95rem;
  padding: 0.34rem 0.85rem;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.25;
  background: var(--accent-soft);
  color: var(--accent-2);
}
.auto-reply:disabled { opacity: 0.5; cursor: default; }

.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.2rem; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.8rem;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius-pill);
  padding: 0.18rem 0.3rem 0.18rem 0.7rem;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.chip .word { font-weight: 600; cursor: pointer; }
.chip button {
  border: none;
  border-radius: 50%;
  width: 2.1rem;
  height: 2.1rem;
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  background: var(--surface-3);
  color: var(--ink-soft);
  transition: background 0.15s var(--ease), transform 0.1s var(--ease);
}
/* Separate yes/no so a tap meant for "understood" doesn't land on "missed". */
.chip button + button { margin-left: 0.25rem; }
.chip button.grade-yes { color: var(--good); }
.chip button.grade-no { color: var(--bad); }
.chip button.grade-yes:hover { background: var(--good-soft); color: var(--good); }
.chip button.grade-no:hover { background: var(--bad-soft); color: var(--bad); }
.chip button:active { transform: scale(0.9); }
/* Buttons are removed once graded, so balance the padding to center the word. */
.chip.graded-got,
.chip.graded-missed { padding-right: 0.7rem; }
.chip.graded-got { border-color: var(--good); color: var(--good); background: var(--good-soft); }
.chip.graded-missed { border-color: var(--bad); color: var(--bad); background: var(--bad-soft); }
.chip.pending { animation: chip-pulse 1.2s ease-in-out infinite; }
.chip.pending .word { cursor: default; }
@keyframes chip-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

.explanation {
  margin-top: 0.45rem;
  padding: 0.65rem 0.85rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  font-size: 0.86rem;
  line-height: 1.5;
  animation: msg-in 0.22s var(--ease) both;
}
.explanation .form-es { font-weight: 700; color: var(--accent-2); }

/* Typing indicator — animated dots in a bot bubble */
.typing {
  align-self: flex-start;
  color: var(--muted);
  font-size: 0.88rem;
  font-style: italic;
  padding: 0.2rem 0.1rem;
}
.typing-indicator {
  display: inline-flex;
  gap: 0.28rem;
  align-items: center;
  background: var(--bubble-bot);
  border-radius: var(--radius-lg);
  border-bottom-left-radius: 6px;
  padding: 0.7rem 0.9rem;
  box-shadow: var(--shadow-sm);
  font-style: normal;
}
.typing-indicator .dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--muted);
  animation: typing-bounce 1.3s var(--ease) infinite;
}
.typing-indicator .dot:nth-child(2) { animation-delay: 0.18s; }
.typing-indicator .dot:nth-child(3) { animation-delay: 0.36s; }
/* Right-aligned variant: shown while an automatic *learner* reply is being
   composed, so the dots sit on the learner's (right) side, in a user bubble. */
.typing.mine { align-self: flex-end; }
.typing.mine .typing-indicator {
  background: var(--bubble-user);
  border-bottom-left-radius: var(--radius-lg);
  border-bottom-right-radius: 6px;
}
.typing.mine .typing-indicator .dot { background: var(--bubble-user-ink); }
@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}

/* ---------------------------------------------------------------------------
   App loading overlay — a clean spinner over the blurred app shell while the
   learner's state (stats, words, opening message) loads after onboarding.
   --------------------------------------------------------------------------- */
#app-loading {
  position: fixed;
  inset: 0;
  z-index: 2500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: rgba(238, 240, 244, 0.55);
  backdrop-filter: blur(10px) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
  animation: app-loading-in 0.2s var(--ease) both;
}
#app-loading[hidden] { display: none; }
.app-loading-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}
.app-spinner {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 3px solid var(--border-strong);
  border-top-color: var(--accent);
  animation: app-spin 0.8s linear infinite;
}
.app-loading-text {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}
@keyframes app-spin {
  to { transform: rotate(360deg); }
}
@keyframes app-loading-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

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

#composer {
  display: flex;
  flex-wrap: wrap; /* lets the accent strip take a full-width row above the field */
  align-items: flex-end;
  gap: 0.55rem;
  padding: 0.8rem 0.9rem calc(0.8rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border);
  background: var(--surface);
}

/* Tap-to-insert keys for target-language characters (á, ñ, ¿ …) the learner's
   keyboard may not offer. One scrollable row spanning the composer. */
#accent-strip {
  flex: 1 0 100%;
  display: flex;
  gap: 0.3rem;
  overflow-x: auto;
  scrollbar-width: none;
}
#accent-strip::-webkit-scrollbar { display: none; }
#accent-strip[hidden] { display: none; }
.accent-key {
  flex: none;
  min-width: 2.1rem;
  height: 2rem;
  padding: 0 0.5rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease), transform 0.1s var(--ease);
}
.accent-key:hover { border-color: var(--accent); background: var(--surface); }
.accent-key:active { transform: scale(0.92); }
/* Wraps the textarea so the auto-reply control can be inset inside the field. */
.composer-field {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
}
#composer textarea {
  flex: 1;
  min-width: 0;
  padding: 0.7rem 1.05rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  font: inherit;
  font-size: 0.97rem;
  line-height: 1.4;
  background: var(--surface-2);
  color: var(--ink);
  resize: none;
  overflow-y: auto;
  max-height: 9.5rem;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), background 0.15s var(--ease);
}
#composer textarea::placeholder { color: var(--muted); }
#composer textarea:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(239, 108, 71, 0.16);
}
/* Leave room on the left for the inset auto-reply control. The #composer id
   selector on the base rule outweighs a bare class, so scope this with the id. */
#composer .composer-field textarea { padding-left: 2.7rem; }

button.primary {
  background: var(--accent-grad);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-pill);
  padding: 0.6rem 1.1rem;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s var(--ease), box-shadow 0.15s var(--ease), opacity 0.15s var(--ease), filter 0.15s var(--ease);
}
button.primary:hover { filter: brightness(1.04); box-shadow: var(--shadow-md); }
button.primary:active { transform: scale(0.97); }
button.primary:disabled { opacity: 0.5; cursor: default; filter: none; box-shadow: none; }

.send-btn {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.85rem;
  height: 2.85rem;
  padding: 0;
  border-radius: 50%;
}
.send-btn svg { display: block; transform: translateX(-3px); }

/* ---------------------------------------------------------------------------
   Side pane — production items you're trying to use
   --------------------------------------------------------------------------- */

#side-pane {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.1rem;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
#side-pane h2 {
  margin: 0 0 0.2rem;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
}

.prod-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 0.85rem;
  margin-top: 0.65rem;
  font-size: 0.9rem;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: transform 0.14s var(--ease), box-shadow 0.14s var(--ease);
}
.prod-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.prod-card .form-es { font-weight: 700; font-size: 0.98rem; }

/* Title acts as the expand/collapse toggle but reads as plain inline text. */
.prod-toggle {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.prod-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* Compact type hint shown only while collapsed (mobile pills). */
.prod-toggle::before {
  content: "";
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  align-self: center;
  background: var(--accent-2);
}
.prod-toggle[data-kind="phrase"]::before { background: var(--accent); }
.prod-toggle[data-kind="grammar"]::before { background: var(--good); }

/* Collapsed by default; expanded shows the badge, example and reveal button. */
.prod-card .prod-body { display: none; }
#production-list.expanded .prod-card .prod-body { display: block; }

/* Desktop sidebar has room — keep today's always-open behavior. */
@media (min-width: 801px) {
  .prod-card .prod-body { display: block; }
  .prod-toggle { cursor: default; }
  .prod-toggle::before { display: none; }
}
.prod-card .example { font-size: 0.8rem; color: var(--muted); margin-top: 0.2rem; line-height: 1.4; }
.prod-card .kind {
  display: inline-block;
  font-size: 0.64rem;
  font-weight: 700;
  color: var(--accent-2);
  background: var(--accent-soft);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.12rem 0.45rem;
  border-radius: var(--radius-pill);
  margin-bottom: 0.4rem;
}
.prod-card .reveal {
  margin-top: 0.55rem;
  font: inherit;
  font-size: 0.78rem;
  background: none;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 0.32rem 0.6rem;
  cursor: pointer;
  color: var(--muted);
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease), background 0.15s var(--ease);
}
.prod-card .reveal:hover { color: var(--accent-2); border-color: var(--accent); background: var(--accent-soft); }
.prod-card .revealed { margin-top: 0.5rem; font-style: italic; }

.suggestion {
  border: 1px solid var(--border);
  border-left: 3px solid var(--good);
  border-radius: var(--radius);
  padding: 0.7rem 0.85rem;
  margin-top: 0.65rem;
  font-size: 0.86rem;
  line-height: 1.5;
  background: var(--good-soft);
  box-shadow: var(--shadow-sm);
  animation: msg-in 0.24s var(--ease) both;
}
#suggestions:not(:empty) { cursor: pointer; }
.suggestions-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.65rem;
  padding: 0.2rem 0.1rem;
  color: var(--ink-soft);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  user-select: none;
}
.suggestions-header:focus-visible { outline: 2px solid var(--good); outline-offset: 2px; border-radius: var(--radius-sm); }
.suggestions-chevron {
  width: 0.5rem;
  height: 0.5rem;
  margin: 0 0.15rem 0.2rem 0;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.2s var(--ease);
}
#suggestions.collapsed .suggestions-chevron { transform: rotate(225deg); }
#suggestions.collapsed .suggestion { display: none; }
.suggestion.incorrect { border-left-color: var(--bad); background: var(--bad-soft); }
.suggestion .confirm { display: flex; gap: 0.45rem; margin-top: 0.55rem; }
.suggestion button {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.1s var(--ease);
}
.suggestion button:hover { background: var(--surface-2); border-color: var(--muted); }
.suggestion button:active { transform: scale(0.97); }

/* ---------------------------------------------------------------------------
   Placement modal
   --------------------------------------------------------------------------- */

dialog#placement-modal,
dialog#learn-modal,
dialog#settings-modal,
dialog#goal-change-modal,
dialog#due-modal,
dialog#message-help-modal {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  max-width: 520px;
  width: calc(100% - 2rem);
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}
dialog#placement-modal[open],
dialog#learn-modal[open],
dialog#settings-modal[open],
dialog#goal-change-modal[open],
dialog#due-modal[open],
dialog#message-help-modal[open] { animation: modal-in 0.25s var(--ease) both; }
@keyframes modal-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
dialog::backdrop { background: rgba(20, 24, 33, 0.45); backdrop-filter: blur(3px); }
.modal-head { display: flex; gap: 0.85rem; align-items: flex-start; }
.modal-head h2 { margin: 0 0 0.2rem; font-size: 1.2rem; letter-spacing: -0.01em; }
.modal-head #placement-intro { margin: 0; font-size: 0.88rem; color: var(--ink-soft); line-height: 1.5; }
.modal-foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.modal-foot #placement-round { margin: 0; }

#placement-screening { margin: 1.1rem 0 0.2rem; }
#placement-screening .placement-question { margin: 0 0 0.9rem; font-size: 1.05rem; font-weight: 600; }
.screening-options { display: flex; flex-direction: column; gap: 0.5rem; }
.screening-options button {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 0.7rem 0.9rem;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.14s var(--ease), border-color 0.14s var(--ease), color 0.14s var(--ease), transform 0.1s var(--ease);
}
.screening-options button:hover { border-color: var(--muted); }
.screening-options button:active { transform: scale(0.98); }

/* In-app learn-language picker (first onboarding step, #learn-modal). */
#learn-modal .modal-head { margin-bottom: 0.4rem; }
.picker-options { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 1.1rem; }
.picker-option {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  width: 100%;
  padding: 0.75rem 1rem;
  border: none;
  border-radius: var(--radius);
  background: var(--accent-grad);
  color: #fff;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.14s var(--ease), transform 0.1s var(--ease);
}
.picker-option:hover { filter: brightness(1.05); }
.picker-option:active { transform: scale(0.98); }
.picker-option .flag { font-size: 1.1em; }
.picker-status { margin: 0.5rem 0; color: var(--muted); text-align: center; }

/* Assumed-target welcome (#learn-intro): the same primary picker-option button
   as the "Continue" action, with a quieter link-style "different language"
   escape hatch to its right. Wraps to a column on narrow screens. */
.learn-intro-body { margin: 0.3rem 0 0; font-size: 0.92rem; color: var(--ink-soft); line-height: 1.5; }
.learn-intro-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
}
.learn-intro-actions .picker-option { flex: 1 1 auto; width: auto; }
.picker-alt {
  flex: 0 0 auto;
  border: none;
  background: none;
  color: var(--ink-soft);
  font: inherit;
  font-weight: 600;
  padding: 0.5rem 0.25rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.14s var(--ease);
}
.picker-alt:hover { color: var(--accent-2); }

/* Progress through today's calibration round, above the probe. */
.placement-progress {
  height: 6px;
  margin: 0.2rem 0 0.4rem;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}
.placement-progress-fill {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--accent);
  transition: width 0.3s ease;
}
#placement-probes { display: flex; flex-direction: column; gap: 0.55rem; margin: 1.1rem 0; }
.probe {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.probe .word { font-size: 1.05rem; font-weight: 600; }
.probe .word .example { display: block; font-size: 0.8rem; font-weight: 400; color: var(--muted); margin-top: 0.15rem; }
/* Four graded levels stacked as a confidence ladder (green → grey). The long
   labels don't fit inline next to the word, so each is a full-width row. */
.probe .options { display: flex; flex-direction: column; gap: 0.35rem; }
.probe button {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: 0.42rem 0.7rem;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.14s var(--ease), border-color 0.14s var(--ease), color 0.14s var(--ease), transform 0.1s var(--ease);
}
.probe button:hover { border-color: var(--muted); }
.probe button:active { transform: scale(0.99); }
.probe button.selected-use { background: var(--good); color: #fff; border-color: var(--good); }
.probe button.selected-understand { background: #2f8f9e; color: #fff; border-color: #2f8f9e; }
.probe button.selected-recognise { background: #d98a2b; color: #fff; border-color: #d98a2b; }
.probe button.selected-none { background: var(--muted); color: #fff; border-color: var(--muted); }

/* One probe per screen: the item gets a little more room and a larger word. */
.probe.placement-single { padding: 1rem 0.9rem; gap: 0.7rem; }
.probe.placement-single .word { font-size: 1.3rem; }
/* Busy placeholder shown between rounds while answers are submitted/judged. */
.probe.placement-busy {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 1.6rem 0.7rem;
  color: var(--muted);
}
.probe.placement-busy::before {
  content: '';
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
/* Right/wrong feedback after a "recognise" guess is judged. */
.probe.placement-feedback {
  align-items: center;
  text-align: center;
  gap: 0.8rem;
  padding: 1.5rem 0.9rem;
}
.probe.placement-feedback .feedback-verdict { margin: 0; font-size: 1.25rem; font-weight: 700; }
.probe.placement-feedback.correct .feedback-verdict { color: var(--good); }
.probe.placement-feedback.incorrect .feedback-verdict { color: var(--accent); }
.probe.placement-feedback .feedback-meaning { margin: 0; font-size: 0.95rem; }

/* Floor-reached pause: "we've got a solid read — keep going or move on?" shown
   once a stage meets its answer floor, before the skip control is offered. */
.probe.placement-pause { align-items: center; text-align: center; gap: 1rem; padding: 1.4rem 0.9rem; }
.probe.placement-pause .placement-pause-prompt { margin: 0; font-size: 1.05rem; line-height: 1.45; }
.probe.placement-pause .placement-pause-actions { display: flex; gap: 0.6rem; width: 100%; }
.probe.placement-pause .placement-pause-actions button { flex: 1; padding: 0.7rem 0.6rem; }

#placement-submit.busy { cursor: default; opacity: 0.85; }
#placement-submit.busy::before {
  content: '';
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-right: 0.5em;
  vertical-align: -0.1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Skip-the-rest control on later placement rounds: a quiet text button that
   sits between the round label and the primary Continue button. */
button.ghost {
  border: none;
  background: none;
  color: var(--muted);
  font: inherit;
  padding: 0.4rem 0.5rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color 0.14s var(--ease), background 0.14s var(--ease);
}
button.ghost:hover { color: var(--ink); background: var(--surface-2); }
button.ghost:disabled { opacity: 0.5; cursor: default; }
#placement-skip { margin-left: auto; }
#placement-skip.busy { opacity: 0.7; cursor: default; }

/* End-of-placement results recap. The shared modal head is hidden for this
   view, so the level line is the top element. */
#placement-results { margin: 0.4rem 0 0.2rem; text-align: center; }
.placement-results-level {
  margin: 0 0 0.5rem;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--accent);
}
/* Final recap: per-skill CEFR breakdown (a row of "Vocabulary B1" chips) plus
   the note that the overall level is the lowest of the three. */
.placement-results-breakdown { margin: -0.15rem 0 1rem; }
.placement-results-breakdown-levels {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
  justify-content: center;
  margin: 0 0 0.55rem;
}
.placement-results-breakdown-level {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
}
.placement-results-breakdown-level .breakdown-label { font-size: 0.8rem; color: var(--muted); }
.placement-results-breakdown-level .breakdown-value {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--accent);
}
.placement-results-min-note { margin: 0; font-size: 0.88rem; line-height: 1.45; }
.placement-results-stats {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
  margin: 0 0 1rem;
}
.placement-results-stat {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.85rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.placement-results-stat .stat-value { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.01em; }
.placement-results-stat .stat-label { font-size: 0.76rem; color: var(--muted); line-height: 1.35; }
/* A lone stat tile — the provisional recap during the word-only rounds, before
   phrases/grammar are assessed — must not flex-grow to the full modal width;
   keep it a tidy centred card (the parent already centres). */
.placement-results-stat:only-child { flex-grow: 0; min-width: 12rem; }
#placement-results .modal-foot { justify-content: center; gap: 0.6rem; }
/* Provisional (calibration) recap extras: the credible range under the
   level-so-far line, and the official-level unlock countdown. */
.placement-results-range {
  margin: -0.25rem 0 0.9rem;
  font-size: 0.9rem;
  line-height: 1.45;
}
.placement-results-countdown {
  margin: 0 0 0.75rem;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--accent);
}
.probe .guess-input {
  flex-basis: 100%;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.6rem;
  font: inherit;
}
.probe .guess-input:focus { outline: none; border-color: var(--accent); }

/* ---------------------------------------------------------------------------
   Settings modal / danger zone
   --------------------------------------------------------------------------- */

#settings-modal .modal-head h2 { margin: 0 0 0.2rem; font-size: 1.2rem; letter-spacing: -0.01em; }
#settings-modal #settings-account { margin: 0; }

.modal-close {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 0.14s var(--ease), border-color 0.14s var(--ease), color 0.14s var(--ease), transform 0.1s var(--ease);
}
.modal-close:hover { border-color: var(--muted); color: var(--ink); }
.modal-close:active { transform: scale(0.95); }

.settings-section {
  margin-top: 1.1rem;
  padding: 1rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
}
.settings-section h3 { margin: 0 0 0.4rem; font-size: 1rem; letter-spacing: -0.01em; }
.settings-section .muted { color: var(--ink-soft); }
.goal-control {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.7rem;
  flex-wrap: wrap;
}
.goal-control #settings-goal {
  width: 5rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  background: var(--surface);
  color: var(--ink);
}
.goal-control #settings-goal-save {
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.9rem;
  font: inherit;
  cursor: pointer;
}
.goal-control #settings-goal-save:disabled { opacity: 0.5; cursor: not-allowed; }
.goal-control #settings-goal-status { margin: 0; }

/* Topic boosts (premium) */
.topic-control {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.7rem;
  flex-wrap: wrap;
}
.topic-control #settings-topic-input {
  flex: 1 1 12rem;
  min-width: 8rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  background: var(--surface);
  color: var(--ink);
}
.topic-control #settings-topic-add {
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.9rem;
  font: inherit;
  cursor: pointer;
}
.topic-control #settings-topic-add:disabled { opacity: 0.5; cursor: not-allowed; }
.topic-control #settings-topic-status { margin: 0; flex-basis: 100%; }
.topic-locked { margin-top: 0.7rem; display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; }
.topic-locked .muted { margin: 0; }
.topic-empty { margin: 0.6rem 0 0; }

.persona-control {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: 0.7rem;
}
.persona-control #settings-persona-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
  min-height: 4rem;
}
.persona-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.persona-actions #settings-persona-save {
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.9rem;
  font: inherit;
  cursor: pointer;
}
.persona-actions #settings-persona-save:disabled { opacity: 0.5; cursor: not-allowed; }
.persona-actions #settings-persona-clear {
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.9rem;
  font: inherit;
  cursor: pointer;
}
.persona-actions #settings-persona-clear:disabled { opacity: 0.5; cursor: not-allowed; }
.persona-actions #settings-persona-status { margin: 0; }
#settings-topics-list {
  margin-top: 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.topic-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.topic-meta {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1 1 auto;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.topic-meta:hover .topic-label { text-decoration: underline; text-decoration-color: var(--muted); }
.topic-meta:focus-visible { outline: 2px solid var(--accent, var(--muted)); outline-offset: 2px; }
.topic-label { font-weight: 600; overflow-wrap: anywhere; }
.topic-count { font-size: 0.8rem; }
.topic-remove {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
}
.topic-remove:hover { background: var(--border); color: var(--ink); }

#goal-change-modal .modal-head h2 { margin: 0; font-size: 1.2rem; letter-spacing: -0.01em; }
.goal-change-body { margin: 0.9rem 0 0.2rem; }
.goal-change-body .muted { color: var(--ink-soft); }

/* ---------------------------------------------------------------------------
   Due-today popup
   --------------------------------------------------------------------------- */

#due-modal .modal-head h2,
#topic-items-modal .modal-head h2 { margin: 0 0 0.2rem; font-size: 1.2rem; letter-spacing: -0.01em; }
#due-modal #due-summary,
#topic-items-modal #topic-items-summary { margin: 0; }
.due-explain {
  margin: 0.9rem 0 0.4rem;
  color: var(--ink-soft);
  font-size: 0.85rem;
  line-height: 1.5;
}
#due-list,
#topic-items-list {
  margin-top: 0.6rem;
  max-height: min(55vh, 26rem);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.due-section {
  margin: 0.7rem 0 0.2rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.due-section:first-child { margin-top: 0; }
.due-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.due-row-main { min-width: 0; }
.due-text { font-weight: 600; }
.due-gloss {
  display: block;
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.1rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.due-tags { display: flex; align-items: center; gap: 0.35rem; flex: none; }
.due-tag {
  font-size: 0.68rem;
  font-weight: 600;
  padding: 0.12rem 0.45rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-soft);
  white-space: nowrap;
}
.due-tag--skill { background: var(--surface-3); }
.due-check { color: var(--good); font-weight: 700; }
.due-row--done {
  background: var(--surface);
  opacity: 0.72;
}
.due-row--done .due-text { text-decoration: line-through; text-decoration-color: var(--muted); }
.due-empty {
  margin: 0.6rem 0 0.2rem;
  color: var(--ink-soft);
  text-align: center;
}

/* ---------------------------------------------------------------------------
   Message-help pop-up (the "?" on bot messages)
   --------------------------------------------------------------------------- */

/* The modal is a fixed-height flex column: the header and the ask form stay put
   while a single middle region (message + gloss + answers) scrolls. One scroll
   container, so word glosses and new answers always land somewhere predictable
   and the input never drifts out of reach. */
dialog#message-help-modal {
  max-height: calc(100vh - 2rem);
  flex-direction: column;
  overflow: hidden;
}
/* Scope the flex layout to [open] so we don't override the UA's
   `dialog:not([open]) { display: none }` and leave the modal always visible. */
dialog#message-help-modal[open] { display: flex; }
#message-help-modal .modal-head { flex: 0 0 auto; }
#message-help-modal .modal-head h2 { margin: 0 0 0.2rem; font-size: 1.2rem; letter-spacing: -0.01em; }
.msg-help-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Room for the scrollbar so glossed text doesn't sit under it. */
  margin-right: -0.5rem;
  padding-right: 0.5rem;
}
.msg-help-text {
  margin: 1rem 0 0;
  font-size: 1.05rem;
  line-height: 1.7;
  color: var(--ink);
}
/* Each word is a reset button so it can be tapped without looking like one. */
.hw-word {
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  padding: 0.02em 0.12em;
  margin: 0 -0.02em;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.hw-word:hover { background: var(--accent-soft); color: var(--accent-2); }
.hw-word.looked-up {
  color: var(--accent-2);
  background: var(--accent-soft);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.hw-word.loading { animation: chip-pulse 1.2s ease-in-out infinite; cursor: default; }

.msg-help-gloss {
  margin-top: 0.9rem;
  padding: 0.7rem 0.9rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  line-height: 1.5;
  animation: msg-in 0.2s var(--ease) both;
}
.msg-help-gloss .hw-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
}
.msg-help-gloss .hw-term { font-weight: 700; color: var(--accent-2); font-size: 1rem; }
.msg-help-gloss .hw-base { font-size: 0.78rem; color: var(--muted); }
.msg-help-gloss .hw-missed {
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--bad);
  background: var(--bad-soft);
  border-radius: var(--radius-pill);
  padding: 0.1rem 0.5rem;
}
.msg-help-gloss .hw-translation { margin-top: 0.3rem; }
.msg-help-gloss .hw-note { margin-top: 0.35rem; color: var(--ink-soft); font-size: 0.84rem; }

.msg-help-answers {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.msg-help-answers:not(:empty) { margin-top: 0.9rem; }
.hw-qa { display: flex; flex-direction: column; gap: 0.3rem; }
.hw-q {
  align-self: flex-end;
  max-width: 85%;
  padding: 0.4rem 0.7rem;
  background: var(--bubble-user);
  color: var(--bubble-user-ink);
  border-radius: var(--radius-lg);
  border-bottom-right-radius: 6px;
  font-size: 0.9rem;
}
.hw-a {
  align-self: flex-start;
  max-width: 92%;
  padding: 0.5rem 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  border-bottom-left-radius: 6px;
  font-size: 0.9rem;
  line-height: 1.5;
  white-space: pre-wrap;
}

.msg-help-ask {
  display: flex;
  align-items: flex-end;
  gap: 0.55rem;
  margin-top: 0.9rem;
  flex: 0 0 auto;
}
.msg-help-ask textarea {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  font: inherit;
  font-size: 0.95rem;
  line-height: 1.4;
  background: var(--surface-2);
  color: var(--ink);
  resize: none;
  overflow-y: auto;
  max-height: 8rem;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), background 0.15s var(--ease);
}
.msg-help-ask textarea::placeholder { color: var(--muted); }
.msg-help-ask textarea:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(239, 108, 71, 0.16);
}

/* Upgrade overlay — a plain fixed overlay rather than a top-layer <dialog>, so
   Clerk Billing's checkout drawer (portaled to <body>) can stack above it. */
#upgrade-modal {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(20, 24, 33, 0.45);
  backdrop-filter: blur(3px);
}
#upgrade-modal[hidden] { display: none; }
/* While Clerk's checkout drawer is open, step our overlay (and its blur) aside;
   the drawer becomes the modal layer. The pricing table stays mounted. */
#upgrade-modal.drawer-open { display: none; }
#upgrade-modal .overlay-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  max-width: 760px;
  width: calc(100% - 2rem);
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  animation: modal-in 0.25s var(--ease) both;
}
#upgrade-modal .modal-head .muted {
  margin: 0;
  color: var(--ink-soft);
}
#pricing-table {
  margin-top: 1rem;
  min-height: 14rem;
}

/* First-run onboarding guide (src/client/onboarding.ts). A full-screen blur
   layer de-emphasises everything irrelevant to the current step; the relevant
   element(s) are "lifted" above it (.onboarding-lift) so they stay sharp, and
   on the first step the chips throb (.onboarding-throb) to draw the eye. The
   blur layer doesn't capture pointer events, so the lifted control stays
   clickable and the learner is never trapped; only the card takes clicks.

   These z-indexes are ordered: blur (2600) < lifted elements (2650) < card
   (2700). The lift trick relies on the chat layout not creating its own
   stacking context — see the note in onboarding.ts and CLAUDE.md before
   restructuring the chat/side-pane or restyling these components. */
.onboarding-blur {
  position: fixed;
  inset: 0;
  z-index: 2600;
  pointer-events: none;
  background: rgba(20, 24, 33, 0.22);
  backdrop-filter: blur(5px) saturate(0.9);
  -webkit-backdrop-filter: blur(5px) saturate(0.9);
  animation: onboarding-fade 0.2s var(--ease) both;
}
@keyframes onboarding-fade { from { opacity: 0; } to { opacity: 1; } }
/* Raise the focused element(s) above the blur layer so they read crisply. */
.onboarding-lift {
  position: relative;
  z-index: 2650;
}
/* Blur a part back out even though it sits inside a lifted element (a lifted
   `.msg` forms a stacking context its children can't escape — see onboarding.ts).
   Matches the full-screen blur layer so it reads as "behind the focus". */
.onboarding-blur-out {
  filter: blur(5px) saturate(0.9);
  opacity: 0.85;
  pointer-events: none;
}
/* The focused element throbs (the chips on their step, the "?" button on its).
   The transform also gives it its own stacking context, but it already sits
   inside a lifted ancestor, so it stays above the blur. */
.onboarding-throb {
  animation: onboarding-throb 1.1s var(--ease) infinite;
  transform-origin: center;
}
@keyframes onboarding-throb {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
/* The "?" help button also brightens and pulses a glow ring in sync with the
   throb, so it reads as actively highlighted. */
.msg-help.onboarding-throb {
  animation: onboarding-throb 1.1s var(--ease) infinite,
    onboarding-glow 1.1s var(--ease) infinite;
}
@keyframes onboarding-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(239, 108, 71, 0); filter: brightness(1); }
  50% {
    box-shadow: 0 0 0 6px rgba(239, 108, 71, 0.35), var(--shadow-md);
    filter: brightness(1.18);
  }
}
/* On the chips step every still-unanswered chip glows as a whole pill (the word
   with its ✓ / ✗ together, so each reads as one thing to act on), the same
   brand-accent pulse the "?" gets on the help step. A chip stops glowing once
   it's graded. The row also throbs (.onboarding-throb on the .chips container). */
.onboarding-throb .chip:not(.graded-got):not(.graded-missed) {
  animation: onboarding-glow 1.1s var(--ease) infinite;
}
/* A standalone glow ring used to highlight a bar/element for a step (e.g. the
   "use them in your messages" production bar on the words step), reusing the
   same brand-accent pulse. */
.onboarding-glow-el { animation: onboarding-glow 1.1s var(--ease) infinite; }
.onboarding-card {
  position: fixed;
  z-index: 2700;
  width: min(20rem, calc(100vw - 2rem));
  /* Never taller than the viewport, so the card always leaves room beside its
     anchor (onboarding.ts places below it on short mobile screens). */
  max-height: calc(100vh - 2rem);
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  pointer-events: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 1rem 1.1rem;
  color: var(--ink);
  animation: modal-in 0.2s var(--ease) both;
}
.onboarding-card h3 {
  margin: 0 0 0.4rem;
  font-size: 1rem;
  letter-spacing: -0.01em;
}
.onboarding-card p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--ink-soft);
}
.onboarding-foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
}
.onboarding-step {
  font-size: 0.72rem;
  color: var(--muted);
  letter-spacing: 0.02em;
}
.onboarding-skip {
  border: none;
  background: none;
  padding: 0.35rem 0.5rem;
  font: inherit;
  font-size: 0.82rem;
  color: var(--muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.onboarding-skip:hover { color: var(--ink-soft); }
.onboarding-skip[hidden] { display: none; }
.onboarding-next {
  margin-left: auto;
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
}

.danger-zone {
  margin-top: 1.1rem;
  padding: 1rem;
  border: 1px solid var(--bad);
  border-radius: var(--radius);
  background: var(--bad-soft);
}
.danger-zone h3 { margin: 0 0 0.4rem; font-size: 1rem; color: var(--bad); letter-spacing: -0.01em; }
.danger-zone .muted { color: var(--ink-soft); }
.danger-zone #settings-confirm {
  width: 100%;
  margin-top: 0.6rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  background: var(--surface);
  color: var(--ink);
}
.settings-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.8rem;
}
.settings-actions button {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.9rem;
  font: inherit;
  cursor: pointer;
  transition: background 0.14s var(--ease), border-color 0.14s var(--ease), transform 0.1s var(--ease);
}
.settings-actions button:hover { border-color: var(--muted); }
.settings-actions button:active { transform: scale(0.98); }
button.danger {
  background: var(--bad);
  border-color: var(--bad);
  color: #fff;
}
button.danger:hover:not(:disabled) { background: var(--bad); border-color: var(--bad); filter: brightness(0.95); }
button.danger:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------------------------------------------------------------------------
   Due-complete celebration
   --------------------------------------------------------------------------- */

.celebrate-confetti {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 2000;
}
.celebrate-confetti span {
  position: absolute;
  top: -12vh;
  border-radius: 2px;
  opacity: 0;
  animation-name: confetti-fall;
  animation-timing-function: cubic-bezier(0.3, 0.6, 0.5, 1);
  animation-fill-mode: forwards;
}
@keyframes confetti-fall {
  0% { transform: translate3d(0, -12vh, 0) rotate(0deg); opacity: 0; }
  10% { opacity: 1; }
  85% { opacity: 1; }
  100% { transform: translate3d(var(--drift, 0), 112vh, 0) rotate(var(--rot, 540deg)); opacity: 0; }
}

.celebrate-card {
  position: fixed;
  left: 50%;
  top: 24%;
  z-index: 2001;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  max-width: min(88vw, 400px);
  padding: 20px 30px 18px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--gold);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  text-align: center;
  cursor: pointer;
  animation: celebrate-pop-in 0.45s var(--ease) both;
}
.celebrate-card.leaving { animation: celebrate-pop-out 0.4s var(--ease) both; }
.celebrate-card .celebrate-emoji {
  font-size: 2.6rem;
  line-height: 1;
  animation: celebrate-emoji-bounce 0.9s var(--ease) 0.1s both;
}
.celebrate-title {
  font-size: 1.2rem;
  font-weight: 700;
}
.celebrate-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.celebrate-stat {
  padding: 4px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: 0.85rem;
  font-weight: 600;
}
.celebrate-stat--streak {
  position: relative;
  background: var(--gold-soft);
  border-color: var(--gold);
}
.celebrate-stat--level {
  background: #e7f0fd;
  border-color: #3d7de0;
}
/* The streak acts out its extension: it enters at yesterday's count (or not at
   all on day one), then ticks up to include today with a pop and a "+1". */
.celebrate-stat--streak.streak-empty { display: none; }
.celebrate-stat--streak.streak-tick,
.daily-goal-streak.streak-tick { animation: streak-tick-pop 0.55s var(--ease); }
@keyframes streak-tick-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.28); }
  100% { transform: scale(1); }
}
.streak-plus {
  position: absolute;
  top: -0.6rem;
  right: -0.45rem;
  font-size: 0.82rem;
  font-weight: 800;
  color: #d99a1f;
  pointer-events: none;
  animation: streak-plus-float 1.1s var(--ease) forwards;
}
@keyframes streak-plus-float {
  0% { opacity: 0; transform: translateY(6px); }
  30% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-16px); }
}
@keyframes celebrate-emoji-bounce {
  0% { transform: scale(0.3) rotate(-14deg); }
  45% { transform: scale(1.25) rotate(7deg); }
  70% { transform: scale(0.95) rotate(-2deg); }
  100% { transform: scale(1) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .celebrate-card,
  .celebrate-card.leaving,
  .celebrate-card .celebrate-emoji,
  .celebrate-stat--streak.streak-tick,
  .daily-goal-streak.streak-tick,
  .daily-goal-status.pct-tick,
  .daily-goal-bar.goal-bar-pulse span,
  .level-banner-status.pct-tick,
  .level-bar.level-bar-pulse span,
  .goal-plus,
  .streak-plus { animation: none; }
  .daily-goal-bar span,
  .level-bar span { transition: none; }
}
@keyframes celebrate-pop-in {
  0% { opacity: 0; transform: translateX(-50%) translateY(8px) scale(0.7); }
  60% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1.06); }
  100% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
@keyframes celebrate-pop-out {
  0% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) translateY(-6px) scale(0.9); }
}

.daily-goal.celebrate-pop,
.level-banner.celebrate-pop { animation: chip-celebrate 0.7s var(--ease); }
@keyframes chip-celebrate {
  0% { transform: scale(1); }
  30% { transform: scale(1.18); }
  55% { transform: scale(0.96); }
  100% { transform: scale(1); }
}

/* ---------------------------------------------------------------------------
   Utilities
   --------------------------------------------------------------------------- */

.muted { color: var(--muted); font-size: 0.85rem; line-height: 1.5; }

/* Nice slim scrollbars */
#messages, #side-pane { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
#messages::-webkit-scrollbar, #side-pane::-webkit-scrollbar { width: 8px; }
#messages::-webkit-scrollbar-thumb, #side-pane::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}

/* ---------------------------------------------------------------------------
   Mobile — single column; production items become a floating horizontal reel
   relocated above the composer by main.ts.
   --------------------------------------------------------------------------- */

@media (max-width: 800px) {
  .layout {
    grid-template-columns: 1fr;
    gap: 0;
    padding: 0;
    max-width: none;
    height: 100vh;
    height: 100dvh;
  }
  #chat-pane {
    height: 100vh;
    height: 100dvh;
    border-radius: 0;
    border-left: none;
    border-right: none;
    box-shadow: none;
  }
  .msg { max-width: 85%; }

  /* Stripped of desktop chrome — a flex wrapper with suggestions pulled above
     the heading too (they already precede the reel in the DOM) and the reel
     itself sitting just above the composer. */
  #side-pane {
    display: flex;
    flex-direction: column;
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 0;
    overflow: visible;
    box-shadow: none;
  }
  #side-pane > h2 {
    display: block;
    margin: 0;
    padding: 0.45rem 0.85rem 0.15rem;
    border-top: 1px solid var(--border);
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(10px);
    color: var(--ink-soft);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }
  #side-pane > p.muted { display: none; }
  #suggestions { order: -1; }
  #suggestions:not(:empty) { padding: 0.5rem 0.75rem 0; }

  /* Horizontal, swipeable, floating reel. */
  #production-list {
    display: flex;
    gap: 0.45rem;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    padding: 0.3rem 0.85rem 0.6rem;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(10px);
    box-shadow: 0 -8px 24px rgba(20, 24, 33, 0.12);
    scrollbar-width: none;
  }
  #production-list::-webkit-scrollbar { display: none; }
  #production-list .prod-card {
    flex: 0 0 auto;
    max-width: 75vw;
    padding: 0.4rem 0.6rem;
    margin-top: 0;
    font-size: 0.78rem;
    scroll-snap-align: start;
    white-space: nowrap;
  }
  #production-list.expanded .prod-card {
    flex: 0 0 78%;
    max-width: 14.5rem;
    white-space: normal;
  }
  /* Collapsed pills size to their term; the rare extra-long phrase is capped
     above and ellipsised here so it can't spill over the next pill. The full
     title wraps into view once the reel is expanded. */
  #production-list:not(.expanded) .prod-card .form-es {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  #production-list .prod-toggle::before { width: 0.45rem; height: 0.45rem; }
  #production-list .prod-card .kind {
    margin-bottom: 0.28rem;
    padding: 0.08rem 0.34rem;
    font-size: 0.56rem;
  }
  #production-list .prod-card .form-es { font-size: 0.86rem; }
  #production-list .prod-card .example {
    margin-top: 0.12rem;
    font-size: 0.72rem;
    line-height: 1.3;
  }
  #production-list .prod-card .reveal {
    margin-top: 0.4rem;
    padding: 0.24rem 0.45rem;
    font-size: 0.68rem;
  }
  #production-list > .muted { margin: 0; align-self: center; }
}

/* ---------------------------------------------------------------------------
   Dark theme
   --------------------------------------------------------------------------- */

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115;
    --bg-grad-1: #161922;
    --bg-grad-2: #0e1014;
    --surface: #1a1d24;
    --surface-2: #20242d;
    --surface-3: #2a2f3a;
    --ink: #eef0f4;
    --ink-soft: #c5cad3;
    --muted: #8b919c;
    --border: #2b303a;
    --border-strong: #3a414e;

    --accent-soft: #3a241c;
    --bubble-bot: #262b35;
    --bubble-bot-ink: #eef0f4;

    --good-soft: #16301f;
    --bad-soft: #3a1c19;
    --gold-soft: #322a12;

    color-scheme: dark;
  }
  .level-banner[data-tooltip]::after { background: #000; }
  #app-loading { background: rgba(15, 17, 21, 0.55); }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
}
