:root {
  --bg: #f4efe4;
  --bg-strong: #e6dcc7;
  --panel: rgba(251, 247, 239, 0.86);
  --panel-strong: rgba(244, 236, 219, 0.96);
  --line: rgba(65, 51, 31, 0.15);
  --ink: #1f1b16;
  --muted: #655742;
  --accent: #1f5c4a;
  --accent-strong: #123b30;
  --accent-soft: rgba(31, 92, 74, 0.12);
  --user-bubble: #e8efe9;
  --sage-bubble: #f7f1e4;
  --shadow: 0 18px 50px rgba(60, 48, 28, 0.12);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
  overflow-x: hidden;
}

body {
  color: var(--ink);
  font-family: "IBM Plex Sans", sans-serif;
  background:
    radial-gradient(circle at top left, rgba(194, 168, 104, 0.22), transparent 28%),
    radial-gradient(circle at bottom right, rgba(31, 92, 74, 0.16), transparent 30%),
    linear-gradient(180deg, #f6f1e8 0%, #eee3cf 100%);
  overflow: hidden;
}

button,
input,
textarea {
  font: inherit;
}

.page-shell {
  display: block;
  min-height: 100vh;
}

.main-view {
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  height: 100vh;
  overflow: hidden;
}

.eyebrow {
  margin: 0 0 8px;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 12px;
  font-weight: 600;
}

h1,
h2,
h3 {
  margin: 0;
  font-family: "Cormorant Garamond", serif;
  font-weight: 600;
}

h1 {
  font-size: 48px;
  line-height: 0.95;
  margin-bottom: 10px;
}

h2 {
  font-size: 30px;
}

h3 {
  font-size: 28px;
}

.helper-text,
.book-meta,
.message-meta {
  color: var(--muted);
}

.panel,
.composer-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 22px;
  box-shadow: var(--shadow);
}

.panel {
  padding: 16px;
  margin-bottom: 16px;
}

.panel-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 14px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 12px;
}

.field span {
  font-size: 13px;
  color: var(--muted);
}

input,
textarea {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(255, 252, 248, 0.92);
  padding: 12px 14px;
  color: var(--ink);
  resize: vertical;
}

input:focus,
textarea:focus {
  outline: 2px solid rgba(31, 92, 74, 0.18);
  border-color: rgba(31, 92, 74, 0.4);
}

.primary-btn,
.secondary-btn,
.ghost-btn {
  border: 0;
  border-radius: 999px;
  padding: 11px 16px;
  cursor: pointer;
  transition: transform 120ms ease, opacity 120ms ease, background 120ms ease;
}

.primary-btn:hover,
.secondary-btn:hover,
.ghost-btn:hover {
  transform: translateY(-1px);
}

.primary-btn {
  background: var(--accent);
  color: #f5f0e5;
}

.secondary-btn {
  background: var(--accent-soft);
  color: var(--accent-strong);
}

.ghost-btn {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink);
}

.book-form {
  display: grid;
  gap: 10px;
  margin-bottom: 12px;
}

.books-list {
  display: grid;
  gap: 10px;
}

.book-item,
.session-item {
  width: 100%;
  text-align: left;
  padding: 14px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: rgba(255, 250, 244, 0.72);
  cursor: pointer;
}

.book-item.active,
.session-item.active {
  background: var(--panel-strong);
  border-color: rgba(31, 92, 74, 0.35);
}

.book-title,
.session-title {
  margin: 0 0 4px;
  font-weight: 600;
}

.book-meta,
.session-meta {
  margin: 0;
  font-size: 12px;
}

.topbar {
  padding: 8px 4px 4px;
  display: flex;
  justify-content: space-between;
  gap: 20px;
  align-items: center;
}

/* Bookshelf button in topbar */
.bookshelf-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  font-size: 14px;
  font-weight: 500;
}

.bookshelf-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #f5f0e5;
  font-size: 12px;
  font-weight: 600;
}

/* Cumulative token cost pill — kept deliberately understated. Same muted
   palette as the per-turn footnote (.turn-cost-note) so the two read as
   "metering metadata", not as a CTA or a billing alert. */
.cost-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 1px;
  /* Visual peer of .user-menu-btn / .bookshelf-btn: same outer rect, same
     ink color, same weight. Reads as part of the actionable cluster — the
     full-ink text is what makes it feel "clickable" without any explicit
     icon or arrow. */
  padding: 10px 18px;
  border-radius: 999px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease;
}
.cost-pill:hover {
  background: rgba(31, 92, 74, 0.04);
  border-color: rgba(31, 92, 74, 0.3);
}
.cost-pill:active {
  transform: translateY(1px);
}

.cost-pill-label {
  white-space: nowrap;
}

/* ── Slot-machine digit reels ───────────────────────────────────────────
   Each digit position is its own .digit-reel containing a vertical strip
   of 0–9 repeated 3 times. To display digit X we translateY to the middle
   copy of X (canonical position = -(X + 10) × 1em). To animate to digit Y
   we transition the strip past one extra full cycle to land on Y in a
   lower copy, producing the "wheel-coming-to-rest" feel. After the
   transition completes JS snaps back to Y's canonical position so the
   next animation starts from a fresh baseline. */
.cost-pill-value {
  display: inline-flex;
  align-items: baseline;
}
.cost-pill-value .static-char {
  display: inline-block;
}
.digit-reel {
  display: inline-block;
  height: 1em;
  width: 0.62em;
  line-height: 1em;
  overflow: hidden;
  vertical-align: baseline;
  text-align: center;
}
.digit-strip {
  display: flex;
  flex-direction: column;
  will-change: transform;
}
.digit-strip > span {
  display: block;
  height: 1em;
  line-height: 1em;
}
.digit-strip.is-spinning {
  /* Duration must match REEL_SPIN_DURATION_MS in app.js — the JS uses
     setTimeout(... duration + delay + 60) to snap-back / record state. */
  transition: transform 1200ms cubic-bezier(0.18, 0.85, 0.25, 1);
}

/* When the value crosses a digit-count boundary (e.g. 9.99 → 10.05), the
   brand-new leading reel(s) "emerge" — slide down from above the pill,
   widen from 0 to natural width, and fade in. Existing reels continue to
   spin normally via the slot-machine logic. After the keyframe finishes
   JS removes this class so the reel falls back to its natural layout. */
.digit-reel.is-emerging {
  animation: digit-emerge 480ms cubic-bezier(0.2, 0.7, 0.3, 1);
}
@keyframes digit-emerge {
  0% {
    width: 0;
    opacity: 0;
    transform: translateY(-0.6em);
  }
  60% {
    width: 0.62em;
    opacity: 1;
  }
  100% {
    width: 0.62em;
    opacity: 1;
    transform: translateY(0);
  }
}

/* Per-turn cost line — rendered OUTSIDE the message bubble as a footnote.
   Visually decoupled from the "本轮提到的书" block: it's billing metadata, not content.
   Negative margin-top pulls it up into the .messages grid gap so it sits tight
   (~6px) below the bubble it belongs to, while still leaving the natural gap
   to the next message below. */
.turn-cost-note {
  margin: -8px 4px 0;
  padding-left: 4px;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  opacity: 0.55;
  letter-spacing: 0.02em;
}

.topbar h2 {
  font-size: 22px;
  line-height: 1.05;
}

/* Focus book label (used in overlay header) */
.focus-book-label {
  font-family: "Cormorant Garamond", serif;
  font-size: 22px;
  font-weight: 600;
  color: var(--accent-strong);
  line-height: 1.2;
}

/* Session switch fade animation */
.messages {
  opacity: 1;
  transition: opacity 150ms ease;
}

.messages.fade-out {
  opacity: 0;
}

.chat-section {
  display: flex;
  min-height: 0;
  flex: 1;
  overflow: hidden;
}

.chat-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 18px;
  min-height: 0;
  flex: 1;
  overflow: hidden;
}

.chat-main {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 18px;
  min-height: 0;
  overflow: hidden;
}

.messages {
  display: grid;
  gap: 14px;
  align-content: start;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 6px;
  padding-left: 6px;
  padding-bottom: 6px;
  scroll-behavior: auto;
}

.book-rail {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
  overflow-y: auto;
  padding-right: 4px;
}

.book-card-grid {
  display: grid;
  gap: 14px;
}

.reading-focus {
  display: grid;
  gap: 16px;
}

.reading-focus-head,
.focus-card-back {
  border: 1px solid var(--line);
  border-radius: 26px;
  background: var(--panel);
  box-shadow: var(--shadow);
}

.reading-focus-head {
  padding: 18px;
  display: flex;
  justify-content: space-between;
  gap: 18px;
  align-items: flex-start;
}

.reading-focus-book {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
  min-width: 0;
}

.reading-focus-cover {
  width: 88px;
  aspect-ratio: 0.72 / 1;
  border-radius: 16px;
  background:
    linear-gradient(180deg, rgba(31, 92, 74, 0.18), rgba(31, 92, 74, 0.05)),
    linear-gradient(135deg, #d8c9a9, #f6efe0);
  border: 1px solid rgba(65, 51, 31, 0.12);
  display: grid;
  place-items: center;
  color: var(--accent-strong);
  font-family: "Cormorant Garamond", serif;
  font-size: 30px;
  font-weight: 600;
  overflow: hidden;
}

.reading-focus-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.reading-focus-label {
  margin: 0 0 8px;
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.reading-focus-title {
  margin: 0;
  font-size: 34px;
  line-height: 1.05;
}

.reading-focus-meta {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.65;
}

.reading-focus-stats-bar {
  border: 1px solid var(--line);
  border-radius: 26px;
  background: var(--panel);
  box-shadow: var(--shadow);
  padding: 18px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.reading-focus-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.reading-focus-stat-count {
  font-size: 36px;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-strong);
  font-family: "Cormorant Garamond", serif;
}

.reading-focus-stat-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--muted);
  text-align: center;
}

.reading-focus-cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.focus-card-back {
  min-height: 220px;
  padding: 18px;
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at top left, rgba(255, 255, 255, 0.26), transparent 28%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.24), rgba(65, 51, 31, 0.04)),
    var(--panel);
}

.focus-card-back::before {
  content: "";
  position: absolute;
  inset: 16px;
  border-radius: 20px;
  border: 1px dashed rgba(65, 51, 31, 0.16);
}

.focus-card-back::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(135deg, transparent 0 38%, rgba(255, 255, 255, 0.12) 50%, transparent 62% 100%);
}

.focus-card-back span {
  position: relative;
  z-index: 1;
  color: rgba(101, 87, 66, 0.86);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.08em;
}

/* --- Insights Tree --- */

.insights-tree {
  display: grid;
  gap: 0;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 26px;
  background: var(--panel);
  box-shadow: var(--shadow);
}

.insights-tree-title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.insights-tree-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-strong);
}

.branch-undo-btn {
  background: none;
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 11px;
  cursor: pointer;
  padding: 2px 8px;
  border-radius: 4px;
  line-height: 1.2;
  transition: background 0.15s, color 0.15s;
}

.branch-undo-btn:hover {
  background: rgba(0, 0, 0, 0.06);
  color: var(--ink);
  border-color: var(--muted);
}

.branch-undo-btn.hidden {
  display: none;
}

.insights-empty {
  color: var(--muted);
  font-size: 14px;
  text-align: center;
  padding: 24px 16px;
}

/* --- Tree structure with CSS lines --- */

.insights-branch {
  position: relative;
  padding: 8px 0 8px 24px;
}

/* 竖线：连接兄弟分支 */
.insights-branch::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 0;
  bottom: 0;
  border-left: 1.5px solid rgba(65, 51, 31, 0.28);
}

/* 横线：从竖线连到分支名 */
.insights-branch::after {
  content: "";
  position: absolute;
  left: 8px;
  top: 18px;
  width: 12px;
  border-top: 1.5px solid rgba(65, 51, 31, 0.28);
}

/* 第一个顶层分支：竖线从横线位置开始向下（不画上方多余的竖线） */
.insights-tree > .insights-branch:first-child::before {
  top: 18px;
}

/* 最后一个兄弟：竖线截断到横线位置 */
.insights-branch:last-child::before {
  bottom: calc(100% - 18px);
}

/* 分支头部行：名称 + 按钮 */
.insights-branch-header {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

/* 分支名称（可编辑区域） */
.insights-branch-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--accent-strong);
  letter-spacing: 0.04em;
  cursor: pointer;
  line-height: 1.5;
}

.insights-branch-name:hover {
  text-decoration: underline;
}

.insights-branch-name[contenteditable="true"] {
  outline: 2px solid var(--accent);
  border-radius: 4px;
  padding: 2px 4px;
  text-decoration: none;
  font-weight: inherit;
  color: inherit;
}

/* 子分支容器 */
.insights-branch-children {
  margin-top: 2px;
}

/* 子分支：继承树线，同色减重 */
.insights-branch-children .insights-branch-name {
  font-weight: 400;
  color: var(--accent-strong);
}

/* 添加分支按钮 */
.branch-add-btn {
  background: none;
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 11px;
  cursor: pointer;
  padding: 1px 5px;
  border-radius: 4px;
  line-height: 1.2;
  opacity: 0;
  transition: opacity 0.15s;
  flex-shrink: 0;
}

.insights-branch-header:hover .branch-add-btn {
  opacity: 1;
}

.branch-add-btn:hover {
  background: rgba(0, 0, 0, 0.06);
  color: var(--ink);
  border-color: var(--muted);
}

/* 空占位 placeholder */
.insights-branch-name:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
  font-weight: 400;
}

/* 删除按钮 */
.branch-delete-btn {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 14px;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 50%;
  line-height: 1;
  opacity: 0;
  transition: opacity 0.15s;
  margin-left: auto;
  flex-shrink: 0;
}

.insights-branch-header:hover .branch-delete-btn {
  opacity: 1;
}

.branch-delete-btn:hover {
  background: rgba(0, 0, 0, 0.06);
  color: var(--ink);
}

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

.book-card {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: var(--panel);
  box-shadow: var(--shadow);
  padding: 16px;
  transition: opacity 280ms ease, transform 280ms ease;
}

/* Streaming entrance: a new card appears mid-conversation. Cards start
   slightly offset and fade in once .shown is added on the next frame. */
.book-card.entering {
  opacity: 0;
  transform: translateY(6px);
}
.book-card.entering.shown {
  opacity: 1;
  transform: translateY(0);
}

.book-cover {
  width: 88px;
  aspect-ratio: 0.72 / 1;
  border-radius: 14px;
  background:
    linear-gradient(180deg, rgba(31, 92, 74, 0.18), rgba(31, 92, 74, 0.05)),
    linear-gradient(135deg, #d8c9a9, #f6efe0);
  border: 1px solid rgba(65, 51, 31, 0.12);
  display: grid;
  place-items: center;
  color: var(--accent-strong);
  font-family: "Cormorant Garamond", serif;
  font-size: 30px;
  font-weight: 600;
  overflow: hidden;
}

.book-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity 320ms ease;
}

.book-card-content {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.book-card-title {
  margin: 0 0 8px;
  font-weight: 600;
  font-size: 22px;
  line-height: 1.15;
}

.shadow-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  border: 1px solid var(--muted, #999);
  border-radius: 4px;
  color: var(--muted, #999);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  vertical-align: middle;
}

.book-card-reason {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
  /* Expand to fill the space between meta and actions so that the action
     buttons stay pinned at the card bottom during typewriter animation. */
  flex-grow: 1;
}

.book-card-meta {
  margin: 0 0 8px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
}

.book-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
  transition: opacity 500ms ease;
}

/* Hide action buttons while the card's typewriter is still running.
   Buttons stay in DOM (so min-height measurement includes them) but are
   invisible + non-interactive until the typing finishes. */
.book-card.typing .book-card-actions {
  opacity: 0;
  pointer-events: none;
}

.mini-btn {
  border: 0;
  border-radius: 999px;
  padding: 8px 12px;
  cursor: pointer;
  background: var(--accent-soft);
  color: var(--accent-strong);
}

.mini-btn.primary {
  background: var(--accent);
  color: #f5f0e5;
}

.book-empty {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
  padding: 6px 2px;
}

.message {
  max-width: 90%;
  min-width: 0;
  padding: 14px 16px;
  border-radius: 22px;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}

.message.user {
  margin-left: auto;
  max-width: 75%;
  background: var(--user-bubble);
}

.message.assistant {
  background: var(--sage-bubble);
}

.message.system {
  background: rgba(255, 255, 255, 0.7);
}

.message-meta {
  margin-bottom: 8px;
  font-size: 12px;
}

.message-body {
  line-height: 1.7;
  overflow-wrap: break-word;
  word-break: break-word;
  min-width: 0;
}

.message-body p { margin: 0 0 0.75em; }
.message-body p:last-child { margin-bottom: 0; }

.message-body ul,
.message-body ol { margin: 0 0 0.75em; padding-left: 1.5em; }
.message-body li { margin-bottom: 0.25em; }

.message-body h1,
.message-body h2,
.message-body h3 {
  margin: 0.75em 0 0.4em;
  font-weight: 600;
  line-height: 1.3;
}

.message-body code {
  font-family: "IBM Plex Mono", "Menlo", monospace;
  font-size: 0.88em;
  background: rgba(0, 0, 0, 0.05);
  border-radius: 4px;
  padding: 0.15em 0.35em;
}

.message-body pre {
  background: rgba(0, 0, 0, 0.05);
  border-radius: 8px;
  padding: 12px 14px;
  overflow-x: auto;
  margin: 0 0 0.75em;
  white-space: pre;
}

.message-body pre code {
  background: none;
  padding: 0;
  font-size: 0.85em;
}

.message-body blockquote {
  margin: 0 0 0.75em;
  padding-left: 1em;
  border-left: 3px solid var(--line);
  color: var(--muted);
}

.message-body strong { font-weight: 600; }
.message-body em { font-style: italic; }

.message-body table {
  border-collapse: collapse;
  width: 100%;
  margin: 0 0 0.75em;
  font-size: 0.9em;
  display: block;
  overflow-x: auto;
}
.message-body th,
.message-body td {
  border: 1px solid var(--line);
  padding: 0.4em 0.75em;
  text-align: left;
  vertical-align: top;
}
.message-body thead tr {
  background: var(--bg-strong);
  font-weight: 600;
}

.message-action {
  text-align: right;
  font-size: 12px;
  color: var(--muted);
  padding: 2px 0 6px;
  margin: 0;
}

/* Tree suggestions in meta area */
.meta-tree-ops {
  margin-bottom: 6px;
}

/* --- Mini tree in meta notifications --- */

.mini-tree-header {
  font-size: 13px;
  margin-bottom: 2px;
  color: var(--ink);
}

.mini-tree {
  margin-top: 4px;
  margin-left: 8px;
  font-size: 12.5px;
}

.mini-tree .insights-branch {
  padding: 3px 0 3px 20px;
}

.mini-tree .insights-branch::before {
  left: 7px;
}

.mini-tree .insights-branch::after {
  left: 7px;
  top: 13px;
  width: 9px;
}

.mini-tree > .insights-branch:first-child::before {
  top: 13px;
}

.mini-tree .insights-branch:last-child::before {
  bottom: calc(100% - 13px);
}

.mini-tree .insights-branch-name {
  font-size: 12.5px;
  line-height: 1.45;
  cursor: default;
}

.mini-tree .insights-branch-name:hover {
  text-decoration: none;
}

.mini-tree .insights-branch-children {
  margin-top: 0;
}

/* New branch: bold accent text, no background */
.mini-tree-new {
  font-weight: 600 !important;
  color: var(--accent-strong) !important;
}

/* Context node (parent): dimmed */
.mini-tree-context {
  color: var(--muted) !important;
  font-weight: 400 !important;
}

/* Renamed branch */
.mini-tree-renamed {
  color: var(--ink) !important;
  font-style: italic;
}

/* Deleted branch */
.mini-tree-deleted {
  color: var(--muted) !important;
  text-decoration: line-through;
}

/* --- Suggestion items in mini tree --- */

.mini-tree .suggest-item-row {
  line-height: 1.6;
}

.mini-tree .suggest-item-name {
  font-weight: 600;
  color: var(--accent-strong);
  font-size: 12.5px;
  outline: none;
}

.mini-tree .suggest-item-tail {
  white-space: nowrap;
}

.mini-tree .suggest-item-name.editing {
  background: white;
  border-radius: 3px;
  padding: 1px 4px;
  box-shadow: 0 0 0 1px var(--accent);
}

.mini-tree .suggest-item-dismissed {
  font-weight: 400 !important;
  color: var(--muted) !important;
  text-decoration: line-through;
}

.suggest-item-actions {
  display: inline-flex;
  gap: 2px;
  vertical-align: baseline;
  margin-left: 6px;
}

.suggest-btn {
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 10px;
  cursor: pointer;
  border: 1px solid rgba(65, 51, 31, 0.15);
  background: white;
  color: var(--ink);
}

.suggest-btn.accept {
  color: var(--accent);
  border-color: var(--accent);
}

.suggest-btn.dismiss {
  color: var(--muted);
}

.suggest-status {
  font-size: 11px;
  color: var(--accent);
  white-space: nowrap;
}

.suggest-status.dismissed {
  color: var(--muted);
}

.branch-dismissed {
  opacity: 0.45;
}

.meta-suggestions {
  margin-top: 4px;
}

.rebuild-suggestion .rebuild-parent-label {
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 4px;
  padding-left: 4px;
}

.rebuild-tree-preview {
  padding: 8px 0;
}

.rebuild-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
  align-items: center;
}

.meta-quick-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.quick-action-card {
  padding: 2px 8px;
  border-radius: 3px;
  font-size: 11px;
  cursor: pointer;
  border: 1px solid var(--accent);
  background: white;
  color: var(--accent);
  transition: background 0.15s;
}

.quick-action-card:hover:not(.triggered) {
  background: var(--accent-soft);
}

.quick-action-card.triggered {
  cursor: default;
  pointer-events: none;
  border-color: rgba(65, 51, 31, 0.15);
  color: var(--muted);
}

.quick-action-label {
  font-size: inherit;
  color: inherit;
}

.quick-action-status {
  font-size: 10px;
  color: var(--muted);
  margin-left: 2px;
}

.tree-suggestions-header {
  font-size: 12px;
  color: var(--accent);
  margin-bottom: 4px;
  font-weight: 500;
}

.tree-suggestion-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  margin-bottom: 4px;
  border-radius: 6px;
  background: var(--accent-soft);
  font-size: 13px;
  transition: opacity 0.2s;
}

.tree-suggestion-card.dismissed {
  opacity: 0.45;
}

.tree-suggestion-card.accepted {
  background: rgba(31, 92, 74, 0.08);
}

.tree-suggestion-content {
  flex: 1;
  min-width: 0;
}

.tree-suggestion-name {
  color: var(--ink);
  outline: none;
}

.tree-suggestion-name.editing {
  background: white;
  border-radius: 3px;
  padding: 1px 4px;
  box-shadow: 0 0 0 1px var(--accent);
}

.tree-suggestion-parent {
  color: var(--muted);
  font-size: 11px;
}

.tree-suggestion-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
  align-items: center;
}

.tree-suggestion-btn {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 4px;
  transition: background 0.15s;
}

.tree-suggestion-btn.accept {
  color: var(--accent);
}

.tree-suggestion-btn.accept:hover {
  background: rgba(31, 92, 74, 0.15);
}

.tree-suggestion-btn.edit {
  color: var(--muted);
}

.tree-suggestion-btn.edit:hover {
  background: rgba(65, 51, 31, 0.1);
}

.tree-suggestion-btn.dismiss {
  color: var(--muted);
}

.tree-suggestion-btn.dismiss:hover {
  background: rgba(65, 51, 31, 0.1);
}

.tree-suggestion-status {
  font-size: 11px;
  color: var(--accent);
}

.tree-suggestion-status.dismissed {
  color: var(--muted);
}

/* --- Suggestion groups: grouped by parent --- */

.suggest-group {
  margin-bottom: 6px;
}

.suggest-group:last-child {
  margin-bottom: 0;
}

/* 上级分支标签 */
.suggest-group-parent {
  font-size: 11px;
  color: var(--muted);
  line-height: 1.5;
  margin-bottom: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 建议条目 */
.suggest-group-item {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 8px;
  margin-bottom: 2px;
  border-radius: 5px;
  background: var(--accent-soft);
  font-size: 13px;
  flex-wrap: wrap;
}

.suggest-group-item.dismissed {
  opacity: 0.45;
}

.suggest-group-item.accepted {
  background: rgba(31, 92, 74, 0.08);
}

/* 建议名称 */
.suggest-group-item-name {
  color: var(--ink);
  outline: none;
  flex: 1;
  min-width: 0;
}

.suggest-group-item-name.editing {
  background: white;
  border-radius: 3px;
  padding: 1px 4px;
  box-shadow: 0 0 0 1px var(--accent);
}

/* 操作按钮 */
.suggest-group-actions {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
  align-items: center;
}

.message-books {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid rgba(65, 51, 31, 0.1);
  color: rgba(101, 87, 66, 0.86);
  font-size: 12px;
  line-height: 1.7;
  white-space: pre-wrap;
}

.composer-card {
  padding: 10px 14px;
  background: rgba(249, 244, 234, 0.96);
}

.composer-top {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  align-items: baseline;
  margin-bottom: 0;
  min-height: 0;
}


.composer-form {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px 12px;
  align-items: end;
}

#messageInput {
  grid-column: 1 / -1;
  min-height: 52px;
}

.composer-settings {
  display: flex;
  align-items: center;
  grid-column: 1;
}

.settings-select {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 10px;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
  outline: none;
  width: fit-content;
  text-align: left;
}

.settings-select:focus {
  border-color: var(--accent);
}

.composer-actions {
  display: flex;
  justify-content: flex-end;
  grid-column: 2;
}

/* ── Thinking Zone ────────────────────────────────────────── */

.thinking-zone {
  margin-bottom: 8px;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  font-size: 12px;
  color: var(--muted);
}

.thinking-summary {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  cursor: pointer;
  user-select: none;
}

.thinking-summary:hover {
  background: var(--bg-raised, rgba(0,0,0,0.03));
}

.thinking-icon {
  flex-shrink: 0;
}

.thinking-latest {
  flex: 1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-style: italic;
}

.thinking-toggle {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 11px;
  padding: 0;
  flex-shrink: 0;
  transition: transform 0.2s;
}

.thinking-body {
  display: none;
  padding: 8px 10px;
  border-top: 1px solid var(--line);
  max-height: 300px;
  overflow-y: auto;
}

.thinking-zone[data-expanded="true"] .thinking-body {
  display: block;
}

.thinking-zone[data-expanded="true"] .thinking-toggle {
  transform: rotate(180deg);
}

.thinking-text {
  white-space: pre-wrap;
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 8px;
  line-height: 1.5;
}

.thinking-tools {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.inline-working-indicator {
  /* visibility instead of display so the 24px reservation (14px height +
     10px top margin) stays constant during streaming. With display:none,
     toggling between thinking events (spinner on) and delta events (spinner
     off) every ~80ms caused the bubble height to oscillate, which looked
     like vertical "flashing". The element is fully removed via
     removeInlineSpinner when the stream actually completes — that's the
     only time the bubble should compact. */
  visibility: hidden;
  width: 14px;
  height: 14px;
  margin-top: 10px;
  border: 2px solid rgba(59, 130, 246, 0.18);
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: sage-spin 0.75s linear infinite;
}

.inline-working-indicator.active {
  visibility: visible;
}

@keyframes sage-spin {
  to { transform: rotate(360deg); }
}

.tool-step {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 0;
}

.tool-step-icon {
  font-size: 13px;
  flex-shrink: 0;
}

.tool-step-query {
  flex: 1;
  font-size: 11px;
}

.tool-step-status {
  font-size: 11px;
  color: var(--muted);
}

.hidden {
  display: none !important;
}

@media (max-width: 980px) {
  .main-view {
    padding: 18px;
    height: auto;
    overflow: visible;
  }

  .topbar,
  .composer-top {
    flex-direction: column;
    align-items: flex-start;
  }

  .stats-bar {
    width: 100%;
  }

  .message {
    max-width: 100%;
  }

  .chat-layout {
    grid-template-columns: 1fr;
  }

  .reading-focus-head,
  .reading-focus-book {
    grid-template-columns: 1fr;
  }

  .reading-focus-head {
    flex-direction: column;
  }

  .reading-focus-cards {
    grid-template-columns: 1fr;
  }

  body {
    overflow: auto;
  }

  .chat-section {
    overflow: visible;
  }

  .messages {
    overflow: visible;
    max-height: none;
    padding-right: 0;
  }
}

/* ── Overlay system ── */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}

.overlay.hidden {
  display: none;
}

.overlay-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(31, 27, 22, 0.32);
}

.overlay-view {
  display: none;
  position: relative;
}

.overlay-view.active {
  display: flex;
}

/* ── Shelf panel (bookshelf view) ── */

.shelf-panel {
  width: calc(100vw - 80px);
  height: calc(100vh - 80px);
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 26px;
  box-shadow: var(--shadow);
  padding: 28px 32px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  overflow: hidden;
}

.shelf-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.shelf-header h2 {
  font-size: 22px;
  margin: 0;
}

.shelf-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  min-height: 0;
  overflow: hidden;
  flex: 1;
}

.shelf-column {
  overflow-y: auto;
  min-height: 0;
  padding-right: 4px;
}

.shelf-column-title {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 12px;
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.bookshelf-count {
  font-size: 14px;
  color: var(--muted);
  font-weight: 400;
}

.bookshelf-list {
  display: grid;
  gap: 12px;
}

.bookshelf-empty {
  color: var(--muted);
  font-size: 14px;
  padding: 8px 0;
}

/* ── Focus panel (reading view) ── */

.focus-panel {
  width: calc(100vw - 80px);
  height: calc(100vh - 80px);
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 26px;
  box-shadow: var(--shadow);
  display: grid;
  grid-template-rows: auto 1fr;
  overflow: hidden;
}

.focus-header {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 24px;
  border-bottom: 1px solid var(--line);
}

.focus-header .focus-book-label {
  flex: 1;
}

.focus-header .ghost-btn:last-child {
  margin-left: auto;
}

.focus-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 18px;
  min-height: 0;
  overflow: hidden;
  padding: 18px;
}

.focus-chat-main {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  min-height: 0;
  overflow: hidden;
}

.focus-chat-main .messages {
  overflow-y: auto;
  padding-right: 6px;
}

.focus-right-rail {
  overflow-y: auto;
  padding-right: 4px;
}

/* ── Auth Screen ── */
.auth-screen {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg, #faf9f7);
  z-index: 1000;
}
/* When shown on top of guest content, use a semi-transparent backdrop */
.auth-screen.modal {
  background: rgba(250, 249, 247, 0.92);
  backdrop-filter: blur(4px);
}
.auth-card {
  position: relative;
  width: 100%;
  max-width: 360px;
  padding: 40px 32px;
  background: #fff;
  border: 1px solid #e5e2dc;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.06);
}
.auth-modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  font-size: 1rem;
  color: #888;
  line-height: 1;
  padding: 4px 8px;
}
.auth-title {
  font-family: 'Cormorant Garamond', serif;
  font-size: 1.8rem;
  font-weight: 500;
  text-align: center;
  margin: 0 0 24px;
  color: #1a1816;
}
.auth-tabs {
  display: flex;
  gap: 0;
  margin-bottom: 20px;
  border-bottom: 1px solid #e5e2dc;
}
.auth-tab {
  flex: 1;
  padding: 8px 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 0.9rem;
  color: #888;
  font-family: inherit;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
.auth-tab.active {
  color: #1a1816;
  border-bottom-color: #1a1816;
}
.auth-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.auth-input {
  padding: 10px 12px;
  border: 1px solid #d9d6d0;
  border-radius: 4px;
  font-size: 0.95rem;
  font-family: inherit;
  background: #faf9f7;
  outline: none;
  transition: border-color 0.15s;
}
.auth-input:focus {
  border-color: #888;
}
.auth-error {
  color: #c0392b;
  font-size: 0.85rem;
  margin: 0;
}
.auth-submit {
  margin-top: 4px;
  width: 100%;
}
.topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

.user-menu {
  position: relative;
}

.user-menu-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 18px;
  font-size: 14px;
  font-weight: 500;
}
.user-menu-btn::after {
  content: "▾";
  font-size: 12px;
  opacity: 0.5;
}

.user-dropdown {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  background: #fff;
  border: 1px solid #e5e2dc;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.10);
  min-width: 110px;
  z-index: 200;
  overflow: hidden;
}

.user-dropdown-item {
  display: block;
  width: 100%;
  padding: 10px 16px;
  text-align: left;
  background: none;
  border: none;
  font-size: 0.88rem;
  color: #c0392b;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  box-sizing: border-box;
}
.user-dropdown-item:hover {
  background: #fdf5f5;
}

/* ── Donate modal ────────────────────────────────────────────────────────
   Triggered by clicking the topbar #costPill. Honor-system donation flow:
   user picks a tier (or custom amount), scans a QR code in WeChat/Alipay,
   then clicks "我已经支持过了" to log a pending donation. */
.donate-modal {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.donate-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(31, 27, 22, 0.45);
  backdrop-filter: blur(2px);
}
.donate-modal-card {
  position: relative;
  width: 100%;
  /* 720px chosen so the longest breakdown line —
     "输出回答：outputTokens 1.69 万 + cacheWriteTokens 43.29 万 — 合 1.95 美元"
     fits on a single line. Below this it wraps; above it the modal feels
     too imposing for the amount of content. */
  max-width: 720px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background: #f5f0e5;
  border-radius: 16px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.18);
  padding: 32px 32px 24px;
  font-family: inherit;
}
.donate-modal-close {
  position: absolute;
  top: 14px;
  right: 16px;
  background: transparent;
  border: 0;
  font-size: 18px;
  color: var(--muted);
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 8px;
  line-height: 1;
}
.donate-modal-close:hover {
  background: rgba(0, 0, 0, 0.04);
  color: var(--ink);
}
.donate-modal-title {
  font-family: "Cormorant Garamond", serif;
  font-size: 28px;
  font-weight: 600;
  color: var(--accent-strong);
  margin: 0 0 12px;
  text-align: center;
}
.donate-modal-subtitle {
  font-size: 14px;
  line-height: 1.7;
  color: var(--muted);
  margin: 0 0 24px;
  text-align: center;
}

/* ── Option B: 3-block layout ─────────────────────────────────────────
   ① Cost transparency (explain content)  — sage tint
   ② Math derivation (5x optimization + formula) — slightly lighter sage
   ③ Action zone (QR + memo + input) — warm beige tint

   Same family for ①②, contrast for ③. Header (title + 已分担 stat)
   sits OUTSIDE all three cards. */
.donate-cost-card {
  background: rgba(31, 92, 74, 0.05);
  border: 1px solid rgba(31, 92, 74, 0.14);
  border-radius: 12px;
  padding: 18px 22px;
  margin-bottom: 12px;
}

/* Math + action zones are no longer boxed — they sit "open" on the modal
   background and a single dashed line separates them. The visual rhythm
   becomes: [boxed cost facts] → [open math derivation] - - - [open
   payment action]. The dashed line says "same flow, different step". */
.donate-math-card {
  background: none;
  border: none;
  padding: 20px 8px 18px;
  margin-bottom: 0;
}
.donate-math-card .donate-rate-block {
  margin-bottom: 0;
}

.donate-action-card {
  background: none;
  border: none;
  padding: 0 8px 6px;
  margin: 0 0 8px;
}
/* Dashed divider implemented as a ::before so its width is independent
   of the action card itself — same horizontal extent as the formula
   above (~70% of modal width), not edge-to-edge. Centered via margin
   auto. */
.donate-action-card::before {
  content: "";
  display: block;
  width: 70%;
  margin: 0 auto 20px;
  border-top: 1px dashed rgba(31, 27, 22, 0.22);
}
.donate-action-card .donate-channels {
  margin-bottom: 0;
}

/* Educational explanation block. Card styling moved to wrappers; this
   just controls its inner spacing. */
.donate-explain {
  margin: 0;
}
.donate-explain-block {
  font-size: 13px;
  line-height: 1.75;
  color: var(--ink);
  margin: 0 0 12px;
}
.donate-explain-block:last-of-type {
  margin-bottom: 0;
}
.donate-explain-block strong {
  color: var(--accent-strong);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* Breakdown lines — classic "- xxx" bullet style. Each line stays compact
   and the eye scans top-to-bottom along the hyphens. */
.donate-explain-list {
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--ink);
  margin: 6px 0 4px;
  padding: 0;
  list-style: none;
}
.donate-explain-list li {
  position: relative;
  padding-left: 14px;
}
.donate-explain-list li::before {
  content: "-";
  position: absolute;
  left: 0;
  color: var(--muted);
}
.donate-explain-list strong {
  color: var(--accent-strong);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Rate block — lives outside the explain card, above the channels/QR.
   Larger font than the explain text so it reads as the main "you pay
   this much" anchor for the donation flow. */
.donate-rate-block {
  margin: 0 0 24px;
  text-align: center;
}
.donate-rate-text {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ink);
  margin: 0 0 14px;
}
/* "优惠" and "5 倍" are the hero words of this sentence — sized noticeably
   larger and in a warmer/brighter green so they stand out against the
   surrounding 14.5px ink text without breaking the sage aesthetic. */
.donate-rate-text strong {
  font-size: 22px;
  font-weight: 700;
  color: #1f7a5a;
  margin: 0 2px;
  letter-spacing: 0.01em;
  vertical-align: -1px;
}

/* The formula: vertical fraction (USD / 5) followed by × rate = RMB.
   Sized noticeably larger than the surrounding text so the math reads
   as the visual centerpiece of the modal. */
.donate-rate-formula {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 17px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.donate-rate-formula .frac {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.15;
  /* The fraction line should sit vertically centered next to ×/=/values,
     so its baseline drops to roughly half the fraction's own height. */
  vertical-align: middle;
}
.donate-rate-formula .frac-num {
  padding: 0 8px 3px;
  border-bottom: 1.5px solid currentColor;
  font-weight: 600;
}
.donate-rate-formula .frac-denom {
  padding-top: 3px;
  font-size: 22px;
  font-weight: 700;
  /* Same vibrant sage as the "优惠" / "5 倍" callout above — the two 5s
     belong to the same idea, so they're visually linked. */
  color: #1f7a5a;
  line-height: 1;
}
.donate-rate-formula .frac-op {
  font-size: 18px;
  font-weight: 400;
  color: var(--muted);
}
.donate-rate-formula .frac-text {
  font-weight: 500;
}
/* Base structural styles for .frac-result must live on the class itself
   (no context prefix) so the absolutely-positioned hand-circle SVG inside
   has a relatively-positioned container regardless of which section uses
   .frac-result (formula vs "本次建议" line). */
.frac-result {
  position: relative;
  display: inline-block;
  padding: 4px 14px;
}
.donate-rate-formula .frac-result {
  color: var(--accent-strong);
  font-weight: 700;
  font-size: 18px;
}

/* Top-level summary below the title — shows the user's cumulative
   contribution as a "thank-you stat". Only visible when totalClaimed > 0. */
.donate-claimed-summary {
  margin: 0 0 14px;
  text-align: center;
  font-size: 14px;
  color: var(--muted);
}
.donate-claimed-summary strong {
  color: var(--accent-strong);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Subtraction term inside the formula — appended after the gross result
   to make the math explicit: "your fair share MINUS what you've already
   given". Same size/style as the surrounding "×" and "汇率" elements so
   it reads as part of the equation. Only shown when totalClaimed > 0. */
.donate-rate-formula .frac-subtraction {
  margin-left: 6px;
  font-size: 17px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  font-weight: 500;
}
/* Context-free so the text sits on top of the absolutely-positioned
   hand-circle SVG regardless of which section uses .frac-result. */
.frac-result-text {
  position: relative;
  z-index: 1;
}
/* Teacher's-marker hand-drawn circle: SVG ellipse stretched to fit the
   surrounding text, in classic red-pen color, with one "overshoot tail"
   on the path so it reads as a quick hand gesture rather than a perfect
   geometric ring. Draws itself in over ~700ms on each modal open via
   stroke-dashoffset animation. */
/* Selector is intentionally context-free — the hand-circle is used both
   in the formula result and in the "本次建议" line. Previously this was
   nested under .donate-rate-formula, which caused the SVG inside
   .donate-rate-suggest to fall back to default block layout (no absolute
   positioning) and balloon to fill its container. */
.frac-result-circle {
  position: absolute;
  top: -6px;
  bottom: -6px;
  left: -10px;
  right: -10px;
  width: calc(100% + 20px);
  height: calc(100% + 12px);
  pointer-events: none;
  /* Slight rotation makes it look less "snapped to a baseline" — like the
     teacher tilted their wrist while drawing. */
  transform: rotate(-1.5deg);
}
.frac-result-circle path {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  animation: hand-draw-circle 720ms cubic-bezier(0.4, 0.6, 0.6, 1) 280ms forwards;
}
@keyframes hand-draw-circle {
  to { stroke-dashoffset: 0; }
}

/* Channel tabs + QR — sits right below the custom-amount toggle without a
   visible divider so the whole "pick amount → pay" flow reads as one unit. */
.donate-channels {
  margin-bottom: 22px;
  padding-top: 4px;
}
.donate-qr {
  position: relative;
  width: 180px;
  height: 180px;
  margin: 0 auto;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  overflow: hidden;
}
.donate-qr img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* When the QR image fails to load (404 placeholder phase), the <img> is
   empty and the placeholder behind it shows through. */
.donate-qr img[src=""],
.donate-qr img:not([src]) {
  display: none;
}
.donate-qr-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: repeating-linear-gradient(
    45deg,
    rgba(101, 87, 66, 0.06),
    rgba(101, 87, 66, 0.06) 10px,
    rgba(101, 87, 66, 0.02) 10px,
    rgba(101, 87, 66, 0.02) 20px
  );
  pointer-events: none;
}
.donate-qr-placeholder-text {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  line-height: 1.5;
}
/* Hide placeholder when the img has actually loaded */
.donate-qr.is-loaded .donate-qr-placeholder {
  display: none;
}

.donate-memo-hint {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.7;
  text-align: center;
  margin: 6px 0 0;
}
.donate-memo-hint strong {
  color: var(--ink);
  font-weight: 500;
}

/* "无需分担" note — single muted line shown ABOVE the QR when the user
   has over-donated. QR stays fully visible (in case they want to give
   more anyway); this line just labels the state.  Same font-size /
   color / line-height as .donate-memo-hint to keep the visual language
   coherent, only the margin position differs (margin-bottom instead of
   margin-top since this sits above the QR, not below). */
.donate-no-need-note {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.7;
  text-align: center;
  margin: 0 0 6px;
}

/* "刚刚分担了 ¥X 人民币 [提交]" — replaces the previous big claim button.
   Lets the user record the actual amount they paid, not just the system's
   suggestion. Inline single-row layout, centered. */
.donate-just-shared {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 18px 0 0;
  font-size: 14px;
  color: var(--ink);
}
.donate-just-shared-label {
  white-space: nowrap;
}
.donate-just-shared-symbol {
  color: var(--ink);
  font-weight: 500;
}
.donate-just-shared-input {
  width: 92px;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  text-align: center;
  font-variant-numeric: tabular-nums;
  background: #fff;
  /* Suppress the spinner buttons on number inputs — they look noisy here. */
  appearance: textfield;
  -moz-appearance: textfield;
}
.donate-just-shared-input::-webkit-outer-spin-button,
.donate-just-shared-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.donate-just-shared-input:focus {
  outline: 0;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(31, 92, 74, 0.12);
}
.donate-just-shared-submit {
  margin-left: 4px;
  padding: 7px 18px;
  background: var(--accent);
  color: #f5f0e5;
  border: 0;
  border-radius: 999px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 150ms ease, transform 80ms ease;
}
.donate-just-shared-submit:hover {
  background: var(--accent-strong);
}
.donate-just-shared-submit:active {
  transform: translateY(1px);
}
.donate-just-shared-submit:disabled {
  background: var(--line);
  color: var(--muted);
  cursor: not-allowed;
  transform: none;
}

/* One-shot success toast triggered after the claim is recorded */
.donate-toast {
  position: fixed;
  top: 76px;
  right: 24px;
  z-index: 600;
  padding: 12px 18px;
  background: var(--accent);
  color: #f5f0e5;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  box-shadow: 0 8px 24px rgba(31, 92, 74, 0.3);
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 200ms ease, transform 200ms ease;
}
.donate-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}
