/* =========================================================
   Navbar
   ========================================================= */
.navbar {
  border-bottom: 1px solid var(--border-soft);
  background: rgba(16,15,24,0.9);
  backdrop-filter: blur(6px);
  position: sticky;
  top: 0;
  z-index: 50;
}
.navbar-inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
}
.navbar-brand {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--accent-gold);
  letter-spacing: 0.06em;
  white-space: nowrap;
}
.navbar-brand span.navbar-brand-sub { color: var(--text-dim); font-size: 0.75rem; font-family: var(--font-body); margin-left: 6px; letter-spacing: 0; }
.navbar-links { display: flex; gap: 4px; flex-wrap: wrap; }
.navbar-links a {
  color: var(--text-dim);
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  font-size: 0.92rem;
}
.navbar-links a:hover, .navbar-links a.active {
  color: var(--text);
  background: var(--bg-card);
  text-decoration: none;
}

.page-header { padding-top: 36px; padding-bottom: 20px; border-bottom: 1px solid var(--border-soft); margin-bottom: 28px; }
/* stage-detail.html's header — replaces the old plain <h1>{{標題}}</h1>.
   Sized between body text and the site's normal 1.9rem h1 (per the
   "不要一樣大" request), color stays plain --text rather than the gold
   accent so it doesn't compete with .chapter-title above it or
   .page-eyebrow. The visible heading is .stage-switcher-label (plain
   text — always shows the full "章節 · 序號", independent of whatever
   each <option>'s own text says); the actual <select> sits invisible
   on top of it (opacity:0, stretched via position:absolute) purely so
   tapping the heading still opens the native picker. This split exists
   because a native <select> can't show different text for its closed
   state vs. its open dropdown list — shortening the option labels (see
   stageDetail.js, avoids the native list wrapping mid-word on long
   entries like SP-1~4) would otherwise have shortened this heading too. */
.stage-switcher-wrap { position: relative; display: inline-block; }
.stage-switcher-label {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text);
  pointer-events: none;
}
.stage-switcher {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  border: none;
  padding: 0;
  cursor: pointer;
}
.stage-switcher:focus { outline: none; }
.stage-switcher-wrap:focus-within .stage-switcher-label { outline: 2px solid var(--accent-teal); outline-offset: 2px; }
.page-header p {
  color: var(--text-dim);
  margin: 0;
  font-size: 0.8rem;
  white-space: pre-wrap;
}
.page-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-teal);
  margin-bottom: 8px;
}

/* =========================================================
   Buttons
   ========================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text);
  padding: 0.6em 1.1em;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  font-weight: 500;
  transition: background 0.15s, border-color 0.15s, transform 0.1s;
}
.btn:hover { background: var(--bg-card-hover); border-color: var(--text-faint); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent-gold); border-color: var(--accent-gold); color: #1a1608; font-weight: 700; }
.btn-primary:hover { background: #d9b46c; border-color: #d9b46c; }
.btn-secondary { background: transparent; border-color: var(--accent-teal-dim); color: var(--accent-teal); }
.btn-secondary:hover { background: rgba(79,200,176,0.1); }
.btn-danger { background: transparent; border-color: var(--danger); color: var(--danger); }
.btn-danger:hover { background: rgba(226,96,79,0.12); }
.btn-sm { padding: 0.4em 0.8em; font-size: 0.8rem; }
.btn-block { width: 100%; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }

/* =========================================================
   Accordion (stage list)
   ========================================================= */
/* Each chapter's own box (2026-09-06) — border/background come from
   --chapter-box-border/--chapter-box-bg, set inline per-chapter by
   stagesList.js from 網站設定 → 章節外觀 (keyed by the exact chapter
   string, e.g. "忘卻遺跡 第 22 季") — a chapter with no configured
   style just falls back to the plain default box below. */
/* Scoped to .so-wrap specifically (2026-09-07) — .chapter-group is
   also reused on 我的隊伍總覽/隊伍筆記 as a plain title wrapper (their
   own visible box is .mt-chapter-box instead, one per row, not one per
   chapter), so this box styling must NOT leak there unscoped — it did
   before this fix, as an unintended side effect. */
.so-wrap > .chapter-group {
  margin-bottom: 24px;
  border: 1px solid var(--chapter-box-border, var(--border-soft));
  background: var(--chapter-box-bg, transparent);
  border-radius: var(--radius-md);
  padding: 20px;
}
.chapter-group { margin-bottom: 24px; }
.chapter-title {
  font-family: var(--font-display);
  color: var(--accent-gold);
  font-size: 1.1rem;
  margin-bottom: 10px;
  padding-left: 2px;
}

/* 過往關卡 (2026-09-06) — one shared box around the WHOLE section (not
   one box per chapter inside it, unlike the main chapters above), plus
   its own divider above the "過往關卡" heading. Button appearance is
   scoped by setting the exact same --so-btn-*/--so-divider-* custom
   properties stagesList.js's main .so-wrap already uses, but on THIS
   element instead — CSS variables resolve from the nearest ancestor
   that sets them, so every .so-btn/.so-row inside naturally picks up
   the past-section's own values without needing separate selectors or
   duplicated CSS rules. */
/* A plain div, not <hr> (2026-09-06) — <hr> carries inconsistent
   browser-default styling (inset border/groove effects vary by
   browser) that can silently fight with author CSS trying to recolor
   it; a div has none of that baggage. */
.stage-past-divider {
  height: var(--past-divider-width, 1px);
  background: var(--past-divider-color, var(--accent-gold));
  margin: 30px 0;
  max-width: 640px;
}
/* 系列篩選 chips (2026-09-07) — shared markup across 關卡攻略/我的隊伍
   總覽/隊伍筆記 (each page's own small renderSeriesFilter copy). */
.series-filter { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.series-filter-btn {
  padding: 6px 16px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: none;
  color: var(--text-dim);
  font-size: 0.85rem;
  cursor: pointer;
}
.series-filter-btn.active { background: var(--accent-gold); border-color: var(--accent-gold); color: #201e2f; font-weight: 700; }

.stage-past-heading {
  /* Matches the page's own <h1>（"關卡攻略"）— color/weight/family,
     NOT .chapter-title's gold (2026-09-06 correction: this heading
     names the SECTION, it isn't a chapter name itself, so it shouldn't
     read as one). */
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--text);
  font-size: 1.5rem;
  margin-bottom: 16px;
}
.stage-past-section {
  max-width: 640px;
  border: 1px solid var(--past-box-border, var(--border-soft));
  background: var(--past-box-bg, transparent);
  border-radius: var(--radius-md);
  padding: 20px;
}
.stage-past-section .chapter-group {
  /* No per-chapter box inside the past section — the outer
     .stage-past-section above is the only box here — so this is
     un-done back to a plain spacer, and the title gets its own
     (smaller, dimmer, admin-editable) treatment instead of
     .chapter-title's normal size/color. */
  margin-bottom: 24px;
  border: none;
  background: none;
  padding: 0;
}
.stage-past-section .chapter-group:last-child { margin-bottom: 0; }
.stage-past-section .chapter-title {
  color: var(--past-chapter-title-color, var(--text-dim));
  font-size: var(--past-chapter-title-size, 0.9rem);
}

/* 關卡攻略 stage-order buttons — replaces the old per-stage accordion.
   Every visual property here has a var(--so-xxx, fallback) — the
   fallback is what renders if site-settings.json's stageButtonStyle is
   ever missing/incomplete; the actual value normally comes from
   stagesList.js reading that setting and writing these custom
   properties onto .so-wrap. Deliberately max-width-capped (not 100% of
   the page container) so it stays a similar visual size on a wide
   desktop screen instead of stretching into a mostly-empty wide block —
   same reasoning as the round table's own max-width. */
.so-wrap { max-width: 640px; }
.so-grid { margin-bottom: 28px; }
.so-row {
  display: flex;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 12px;
  /* Divider drawn as a background strip (not border-bottom) — a CSS
     border can't render a gradient reliably, but a background-image
     gradient can, which is what lets 分隔線 support a two-color
     gradient the same way the button background does. Solid color is
     just this same gradient with both stops set to the same color. */
  background-image: linear-gradient(to right, var(--so-divider-start, var(--accent-gold)), var(--so-divider-end, var(--accent-gold)));
  background-repeat: no-repeat;
  background-position: bottom;
  background-size: 100% var(--so-divider-width, 1px);
}
.so-row:last-child { background-image: none; margin-bottom: 0; padding-bottom: 0; }
.so-btn {
  flex: 1; /* every button in a row equally shares that row's width,
              whether the row has 2 buttons or 4 — never wraps, never
              gets visually squeezed unevenly */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--so-btn-pad-y, 12px) var(--so-btn-pad-x, 6px);
  border-style: solid;
  border-width: var(--so-btn-border-width, 1px);
  border-color: var(--so-btn-border-color, var(--border-soft));
  border-radius: var(--so-btn-radius, 8px);
  background: var(--so-btn-bg, var(--bg-card));
  color: var(--so-btn-text, var(--text));
  font-family: var(--so-btn-font, var(--font-display));
  font-size: var(--so-btn-font-size, 0.95rem);
  font-weight: 700;
  text-decoration: none;
  transition: transform 0.15s, box-shadow 0.15s;
}
.so-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(0,0,0,0.35);
}

.accordion { border: 1px solid var(--border-soft); border-radius: var(--radius-md); overflow: hidden; }
.accordion-item + .accordion-item { border-top: 1px solid var(--border-soft); }
.accordion-header {
  width: 100%;
  display: flex; align-items: center; gap: 14px;
  background: var(--bg-elevated);
  border: none; color: var(--text);
  padding: 16px 18px;
  text-align: left;
  font-size: 1rem;
}
.accordion-header:hover { background: var(--bg-card); }
.accordion-order {
  font-family: var(--font-mono);
  color: var(--accent-teal);
  font-size: 0.85rem;
  min-width: 46px;
}
.accordion-stage-title { flex: 1; font-weight: 600; }
.accordion-caret { color: var(--text-faint); transition: transform 0.18s; }
.accordion-item.open .accordion-caret { transform: rotate(90deg); }
.accordion-panel {
  background: var(--bg-card);
  padding: 0 18px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.22s ease, padding 0.22s ease;
}
.accordion-item.open .accordion-panel { padding: 16px 18px 20px; max-height: 900px; }
.accordion-block { margin-bottom: 14px; }
.accordion-block-label { font-size: 0.78rem; color: var(--text-faint); letter-spacing: 0.05em; margin-bottom: 6px; }
.tag-row { display: flex; gap: 8px; flex-wrap: wrap; }
.mini-team-row { display: flex; gap: 10px; flex-wrap: wrap; }
.guide-preview { color: var(--text-dim); font-size: 0.9rem; }
.guide-preview p { margin: 0; }

/* =========================================================
   Section divider (stage detail page)
   ========================================================= */
.section { margin-bottom: 36px; }
.section-title {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 16px;
}
.section-title h2 { margin: 0; }
.section-title::after { content: ""; flex: 1; height: 1px; background: var(--border-soft); }
/* stage-detail.html「我的隊伍」區塊，說明「我的隊伍總覽」頁只會列出
   第1組——固定顯示在標題/按鈕列下方，不分桌機/手機都一樣位置，避免
   跟按鈕擠在同一列時窄螢幕被截斷。 */
.section-title-note { color: var(--accent-teal); font-size: 0.8rem; margin: -8px 0 16px; }
.guide-content {
  background: var(--bg-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 18px 20px;
  font-size: 0.9rem;
  line-height: 1.6;
  max-width: 640px;
}
.guide-content :last-child { margin-bottom: 0; }
/* Same fix as .ultimate-skill-desc p — Quill wraps each line in its own
   <p> on Enter; without this, the browser's own default paragraph
   margin (not this site's design) creates a much looser line spacing
   here than what the admin's Quill editor itself shows while typing. */
.guide-content p { margin: 0; }
/* Quill (the admin's rich-text editor) writes <span class="ql-size-large">
   for the one-step-up size option, and inline color styles for the
   swatch picker (those need no extra CSS — they're plain style="color:").
   The admin page has Quill's own stylesheet loaded so this class already
   renders there; the public site doesn't load Quill at all, so this rule
   is what makes the same markup look right here too. Deliberately modest
   (not Quill's default 1.5em) per the "限制一個程度不能放太大" request. */
.guide-content .ql-size-large { font-size: 1.3em; }

/* Round table (回合表) — a real <table>, separate from .guide-content's
   rich text. Fixed max-width (not full-container-width) so it doesn't
   stretch into a mostly-empty wide block on a big desktop screen —
   stays roughly the same visual size whether you're on mobile or a wide
   monitor, just left-aligned rather than centered so it lines up with
   the guide text above/below it. table-layout is deliberately left at
   its default (auto), not "fixed" — that's what lets column 1 hug
   whatever its longest line actually is while column 2 takes the rest;
   no JS measurement involved. */
.rt-table {
  max-width: 640px;
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  background: var(--bg-card);
}
.rt-table td {
  border: 1px solid var(--border-soft);
  padding: 8px 12px;
  color: var(--text);
  font-size: 0.9rem;
  line-height: 1.6;
  vertical-align: top;
}
.rt-table td:first-child {
  white-space: nowrap;
  font-weight: 600;
  color: var(--text-dim);
}
.rt-table td:last-child {
  width: 100%; /* takes whatever's left after column 1's content-hugged width */
  overflow-wrap: break-word;
}

/* Divider inserted from the rich-text editor's toolbar (攻略內容 /
   必殺技敘述 — both share this). A "ticket perforation" dash-dot line:
   drawn with an SVG background (round-capped dashes + a round dot, on a
   repeating tile) rather than CSS border-style:dashed, which only draws
   uniform rectangular dashes and looks flat/plain by comparison.
   Narrower than the content column and left-aligned (not centered) to
   read as a break in the text rather than a decorative full-width rule. */
hr.ql-divider {
  border: none;
  width: 60%;
  max-width: 320px;
  height: 4px;
  margin: 1.1em 0;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2222%22%20height%3D%224%22%3E%3Cline%20x1%3D%220%22%20y1%3D%222%22%20x2%3D%2222%22%20y2%3D%222%22%20stroke%3D%22%23a29cbf%22%20stroke-width%3D%221.3%22%20stroke-linecap%3D%22round%22%20stroke-dasharray%3D%226%2C8%2C0%2C8%22/%3E%3C/svg%3E");
  background-repeat: repeat-x;
  background-position: left center;
}

/* =========================================================
   Tag pill (icon + label, read-only display)
   ========================================================= */
.tag-pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5px 12px 5px 6px;
  font-size: 0.82rem;
  color: var(--text-dim);
}
.tag-pill img { width: 18px; height: 18px; }

/* =========================================================
   Filter + grid two-column layout — shared by 卡片資料庫 (cards.html)
   and the「選擇卡片」picker modal (CardPicker.js), so both look and
   behave the same: 300px filter sidebar on the left, results on the
   right, stacking to one column on narrow viewports.
   ========================================================= */
.filter-grid-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; }
.filter-grid-layout .fg-sidebar {
  background: var(--bg-elevated);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  position: sticky;
  top: 0;
  max-height: 70vh;
  overflow-y: auto;
  overflow-x: hidden;
}
/* Holds the actual padding that used to live on .fg-sidebar itself.
   .filter-panel-header is a *sibling* of this, not a child — sitting
   directly in .fg-sidebar with no padding to fight against — so its
   sticky/opaque background reaches the sidebar's real top edge with no
   gap for scrolled-past content to peek through. (A negative-margin
   trick to cancel .fg-sidebar's old padding was tried first and didn't
   reliably work — sticky positioning + negative margins is a known
   rough combination — so this restructures around the problem instead
   of fighting it.) */
.fg-sidebar-body { padding: 16px; }
.filter-grid-layout .result-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; color: var(--text-dim); font-size: 0.85rem; }
/* Page context (卡片資料庫) has a sticky navbar above it that the modal
   context (CardPicker) doesn't — offset the sidebar down by the navbar's
   own height so it doesn't stick underneath/behind it while scrolling. */
.filter-grid-layout.fg-page .fg-sidebar { top: 78px; }
@media (max-width: 700px) {
  .filter-grid-layout { grid-template-columns: 1fr; }
  .filter-grid-layout .fg-sidebar { position: static; max-height: none; }
}

/* =========================================================
   Card info modal — small class/element icons + ultimate skill block
   ========================================================= */
.card-info-meta { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.card-info-icon {
  width: 30px;
  height: 30px;
  object-fit: contain;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.5));
  -webkit-user-drag: none;
}
.card-info-cd { font-size: 0.9rem; color: var(--text-dim); margin-left: 4px; }

/* Card info modal — no text selection anywhere in it (title, meta,
   必殺技 description, tags); right-click is separately blocked in
   CardInfoModal.js. Doesn't stop someone determined via devtools, just
   removes the easy "select all, copy" / "right-click → 另存圖片" path. */
.card-info-modal-body {
  user-select: none;
  -webkit-user-select: none;
}
/* The label sits like a tab on top of the description box: rounded top
   corners, square bottom corners, no gap to the box below it. The desc
   box mirrors this — square top-left (so the two shapes fuse into one
   continuous outline where they meet) and the label's own radius on
   its other three corners for visual balance. */
.ultimate-skill-label {
  display: inline-block;
  background: #373737;
  color: var(--text-dim);
  font-weight: 700;
  font-size: 0.85rem;
  padding: 6px 14px;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.ultimate-skill-desc {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 0 var(--radius-sm) var(--radius-sm) var(--radius-sm);
  padding: 12px 14px;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: break-word;
  /* Some 必殺技敘述 entries are legacy plain text with literal \n line
     breaks (not Quill's usual per-line <p>) — a browser's default
     white-space handling collapses a bare \n to a single space, which
     is why 後台 (Quill, which does preserve raw \n visually) and 前台
     used to disagree on the same text (2026-09-07). pre-line preserves
     \n as a real line break while still letting normal spaces/wrapping
     behave as usual — it doesn't do anything to content that's already
     properly <p>-wrapped, so this is safe for every other card too. */
  white-space: pre-line;
  /* Belt-and-suspenders: normal block-level <p>/<br> tags already break
     correctly without this, but if a paste ever lands as a literal "\n"
     character inside a text node (rather than as its own tag), the
     default white-space:normal silently collapses it into a space —
     this makes any literal newline character visible too, not just tag
     -based ones. */
  white-space: pre-wrap;
}
.ultimate-skill-desc p { margin: 0; }
.ultimate-skill-desc .ql-size-large { font-size: 1.3em; }

/* =========================================================
   Card grid & card button (used by CardGrid / CardButton)
   ========================================================= */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 12px;
}
/* Denser variant for the 選擇卡片 picker modal, which has less width to
   work with than the full 卡片資料庫 page — smaller minimum tile size so
   more cards fit per row (~5 on desktop, ~3 on narrow phones via the
   media query below), without changing the main card database page. */
.card-grid.card-grid--compact {
  grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 10px;
}
@media (max-width: 480px) {
  .card-grid.card-grid--compact {
    grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
    gap: 6px;
  }
}
.card-btn {
  position: relative;
  background: var(--bg-card);
  border: 2px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 0;
  /* No overflow:hidden — CardFace clips its own artwork/gradient via
     .card-face-clip; leaving this visible lets the element badge
     protrude past the card edge as designed. */
  text-align: left;
  transition: border-color 0.15s, transform 0.1s;
  aspect-ratio: 200 / 260;
  container-type: inline-size;
}
.card-btn:hover { border-color: var(--text-faint); transform: translateY(-2px); }
.card-btn.selected { border-color: var(--accent-teal); box-shadow: 0 0 0 2px rgba(79,200,176,0.35); }
.card-btn.disabled { cursor: default; }
.card-btn.disabled:hover { border-color: var(--border-soft); transform: none; }
.card-btn-disabled-overlay {
  position: absolute;
  /* Matches .card-face-badge-tl's own overflow amount (top:-4cqw;
     left:-4cqw; in card-face.css) so the dimming actually reaches the
     element badge that's deliberately poking past the card's own edge —
     inset:0 alone left that badge lit up outside the dimmed area. */
  top: -4cqw;
  left: -4cqw;
  right: 0;
  bottom: 0;
  background: rgba(25,23,38,0.7);
  border-radius: inherit;
  pointer-events: none;
  z-index: 3;
}
/* Hover-only "view card info" button — same footprint as the 定位 badge
   (34cqw), bottom-right, styled to match the badge family (circular,
   drop-shadow) but with its own icon since there's no uploaded asset for
   this one. Hidden until hover/focus so it doesn't compete with the
   artwork at rest. */
.card-info-btn {
  position: absolute;
  right: -2cqw;
  bottom: -2cqw;
  width: 34cqw;
  height: 34cqw;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(20,19,31,0.92);
  border: 1.5px solid var(--accent-teal-dim);
  color: var(--accent-teal);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s, transform 0.15s;
  z-index: 4;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6));
  cursor: pointer;
}
.card-info-btn svg { width: 62%; height: 62%; }
.card-btn:hover .card-info-btn,
.card-info-btn:focus-visible {
  opacity: 1;
  pointer-events: auto;
}
.card-info-btn:hover { transform: scale(1.08); background: rgba(20,19,31,1); }
.card-empty-slot {
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elevated);
  border: 2px dashed var(--border);
  border-radius: var(--radius-md);
  aspect-ratio: 200 / 260;
  /* Without this, a <button> just sizes to its own "+" content (small,
     roughly square) and only THEN applies aspect-ratio to that small
     width — same underlying issue as elsewhere on this site, a form
     element not filling its container on its own. The filled slot's
     card button gets width:100% set inline (see TeamEditor.js); this
     is the empty-slot equivalent, so both end up the same size/shape
     inside their shared 88px-wide .team-slot-btn column. */
  width: 100%;
  box-sizing: border-box;
  color: var(--text-faint);
  font-size: 2rem;
}
.card-empty-slot:hover { border-color: var(--accent-teal-dim); color: var(--accent-teal); }
.empty-state {
  text-align: center;
  padding: 50px 20px;
  color: var(--text-dim);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}
.empty-state h3 { color: var(--text); }

/* =========================================================
   Filter panel — badge / icon / avatar / range / icon-multi
   ========================================================= */
.filter-panel { display: flex; flex-direction: column; gap: 18px; }
.filter-group-label {
  font-size: 0.82rem;
  color: var(--text-faint);
  letter-spacing: 0.06em;
  margin-bottom: 8px;
}
.filter-options { display: flex; gap: 10px; flex-wrap: wrap; }
.tag-group-block + .tag-group-block { margin-top: 18px; }
.tag-group-label {
  font-size: 0.82rem;
  color: var(--accent-teal);
  letter-spacing: 0.06em;
  margin-bottom: 8px;
  font-weight: 600;
}

.opt-badge {
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-dim);
  padding: 8px 16px;
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-family: var(--font-mono);
  font-size: 0.85rem;
}
.opt-badge.active { color: #14131f; border-color: transparent; }

/* Icon-style options (定位/屬性): no wrapping circle/background — the
   uploaded icon artwork (which already has its own frame/rim baked in)
   fills the whole clickable area directly, enlarged, so there's no
   double-ring effect from stacking a CSS circle behind it. */
.opt-icon {
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.15s, transform 0.15s;
}
.opt-icon img {
  width: 100%; height: 100%; object-fit: contain;
  /* Belt-and-suspenders alongside <img draggable="false"> in
     FilterPanel.js — some browsers still allow a drag gesture to pick
     up the image unless this is set too, which was getting in the way
     of just clicking to select. */
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}
.opt-icon.active { opacity: 1; transform: scale(1.08); }
/* 屬性 icons only (定位 stays 56px) — per explicit request the element
   icons read as slightly too large relative to 定位's. */
.filter-group-elementId .opt-icon { width: 44px; height: 44px; }

.opt-avatar {
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.15s, transform 0.15s;
}
.opt-avatar img {
  width: 100%; height: 100%; object-fit: contain;
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}
.opt-avatar.active { opacity: 1; transform: scale(1.08); }

/* 卡片管理側欄的角色篩選 (2026-09-09) — 跟篩選面板同一顆 .opt-avatar，
   只是邊欄比篩選面板窄，縮小一點、間距也收緊一點才排得下。 */
.card-character-filter-grid {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-soft);
}
.card-character-filter-grid .opt-avatar { width: 40px; height: 40px; }

.opt-range {
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-dim);
  padding: 8px 18px;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-weight: 600;
}
.opt-range.active { background: var(--accent-teal); border-color: var(--accent-teal); color: #0d1f1c; }
/* Fixed 2-column grid for the "normal" tag rows — short labels sit two
   to a row; anything actually measured as too long (see
   widenOverflowingTags in TagPicker.js) gets .opt-tag-wide and spans
   both columns instead of wrapping or getting squeezed. This exact
   rule (fixed 2 columns) is confirmed working on the public site —
   don't change it here; add a *new* class instead (see .tag-flow-admin
   below) if a different context needs different column behavior. */
.tag-flow {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
/* Admin-only variant (see fieldSelectors.js's flowClass option) — plain
   flex-wrap, each button's own width is set inline by TagPicker.js
   (computeFixedTagWidth), straight from that label's character count.
   No grid, no column-sharing, no live measurement: a given label is
   always the same width regardless of what's next to it, and rows just
   wrap whenever the next button doesn't fit. Public site never uses
   this class, so it can't be affected by it. */
.tag-flow-admin {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.tag-flow .opt-tag {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
.tag-flow-admin .opt-tag {
  flex: 0 0 auto;
}
.tag-flow .opt-tag span, .tag-flow-admin .opt-tag span {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.tag-flow .opt-tag-wide { grid-column: 1 / -1; }
.opt-tag {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  border-radius: 999px;
  padding: 7px 14px 7px 9px;
  color: var(--text-dim);
  font-size: 0.8rem;
  white-space: nowrap;
  opacity: 0.75;
  transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.opt-tag img { width: 20px; height: 20px; flex-shrink: 0; }
.opt-tag.active { opacity: 1; border-color: var(--accent-teal); background: rgba(79,200,176,0.12); color: var(--text); }

.filter-panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 16px 10px 16px;
  border-bottom: 1px solid var(--border-soft);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-elevated);
}
.filter-count { font-size: 0.82rem; color: var(--text-faint); }

/* =========================================================
   Modal
   ========================================================= */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(8,7,13,0.72);
  display: flex; align-items: center; justify-content: center;
  z-index: 200;
  padding: 20px;
}
.modal-box {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 640px;
  max-height: 88vh;
  display: flex; flex-direction: column;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
.modal-box.modal-wide { max-width: 920px; }
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px;
  border-bottom: 1px solid var(--border-soft);
}
.modal-header h2 { margin: 0; font-size: 1.1rem; }
.modal-close { background: none; border: none; color: var(--text-faint); font-size: 1.3rem; line-height: 1; padding: 4px 8px; border-radius: 6px; }
.modal-close:hover { color: var(--text); background: var(--bg-card); }
.modal-body { padding: 20px 22px; overflow-y: auto; }
/* 匯入備份 dialog's (例如…) explanatory asides — see backupModals.js.
   Kept visually distinct (dimmer, smaller) from the actual instruction
   sentence above it so it reads as "just an example", not part of the
   core rule. white-space:pre-line preserves the \n line breaks already
   baked into that string in backupModals.js. */
.backup-modal-note { color: var(--text-faint); font-size: 0.85rem; white-space: pre-line; margin-top: -6px; }
/* 【加入檔案】【覆蓋檔案】標題 — 金色（沿用網站既有的 --accent-gold），
   並把預設 h3 的 0.5em 下邊距收窄，讓標題跟緊接在下面的說明句貼近。 */
.backup-modal-title { color: var(--accent-gold); margin-bottom: 0.15em; }
.modal-footer { padding: 16px 22px; border-top: 1px solid var(--border-soft); display: flex; justify-content: flex-end; gap: 10px; }

/* =========================================================
   Forms
   ========================================================= */
.form-field { margin-bottom: 16px; }
/* Marks a field as the start of a visually distinct sub-section (e.g.
   關卡按鈕外觀 after a run of plain text-description fields) — a
   clear rule + extra breathing room above it, rather than just another
   16px-spaced field blending into the ones before it. */
.form-field-section-break {
  margin-top: 32px;
  padding-top: 24px;
  border-top: 2px solid var(--border-soft);
}
.form-field-section-break > label {
  font-size: 1.05rem;
  color: var(--text);
  font-weight: 700;
  margin-bottom: 4px;
}
.form-field label { display: block; font-size: 0.85rem; color: var(--text-dim); margin-bottom: 6px; }
.form-field input[type="text"], .form-field textarea { width: 100%; }
.form-field textarea { resize: vertical; min-height: 70px; white-space: pre-wrap; }
.form-hint { font-size: 0.76rem; color: var(--text-faint); margin-top: 4px; }

/* Lightweight "select text, apply color" editor for a team's 備註 —
   a plain contenteditable + a few color-swatch buttons, deliberately not
   pulling in the full Quill library (that's an admin-only dependency;
   this field lives on the public site). */
.note-editor-toolbar { display: flex; gap: 6px; margin-bottom: 6px; }
.note-color-swatch {
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 2px solid var(--border-soft);
  padding: 0;
  cursor: pointer;
}
.note-color-swatch:hover { border-color: var(--text-dim); }
.note-color-reset {
  background: var(--bg-elevated);
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.note-editor {
  min-height: 70px;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 0.9rem;
  line-height: 1.6;
  white-space: pre-wrap;
  outline: none;
}
.note-editor:focus { border-color: var(--accent-teal); }
.note-editor:empty::before { content: attr(data-placeholder); color: var(--text-faint); }
/* Links inserted via the 🔗 button in this editor — same styling
   whether you're looking at it in the admin's editing view (.note-editor)
   or the public page it ends up rendered into (.page-header p, where
   我的隊伍/關卡攻略/卡片資料庫's description text lives). */
.note-editor a, .page-header p a {
  color: var(--accent-teal);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.note-editor a:hover, .page-header p a:hover { color: var(--accent-gold); }

/* =========================================================
   我的隊伍總覽 — chapter header + a row of order-number tabs
   (mirrors 關卡攻略's own button grid), one team preview panel below
   whichever tab in that chapter is currently active. Colors here are
   deliberately hand-picked (not the site's normal --accent-gold/
   --border-soft tokens) per an explicit design request for this one
   page — #c9a45c for the chapter title/panel border (same as
   --accent-gold, just spelled out since this section intentionally
   doesn't reference the variable), #887242 for the active/enabled tab
   elements, #5c5c5c for stages with no saved team.
   ========================================================= */
.mt-tab-wrap {
  /* Single source of truth for the vertical spacing above/below the
     tab row — symmetric top and bottom (8px each) so the gap from the
     box's top border down to the tabs matches the gap from the tabs
     down to whatever comes next (the .mt-tab-panel divider, or the
     box's own bottom border when nothing's expanded). .mt-tab-row NO
     LONGER has its own top/bottom padding (see below) — mixing two
     separately-tuned padding values here is exactly what produced the
     top/bottom mismatch this replaced.
   */
  padding: 8px 14px;
}
/* Tabs and the "查看攻略" link now share one line: this wraps both,
   with justify-content:space-between pushing the link to the box's
   far-right edge while the (fixed-width, content-sized) tab grid sits
   at the left — not two separate stacked rows. */
.mt-tab-toprow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
/* Horizontally-scrollable strip around the tab grid (see
   myTeamsOverview.js) — min-width:0 is required for a flex child to
   be allowed to shrink below its content's natural size at all,
   otherwise this would just push the box wider (or get clipped by
   .mt-chapter-box's overflow:hidden) instead of scrolling. The link
   (flex-shrink:0, see .mt-tab-link) always keeps its own full space,
   only this strip gives up room / scrolls when a row's tabs don't fit
   the box's actual width — e.g. 104px columns on a narrow phone. */
.mt-tab-scroll { min-width: 0; overflow-x: auto; }
.mt-tab-row {
  display: grid;
  /* grid-template-columns is set inline per-row by myTeamsOverview.js
     (see gridTemplate there) — every column is the same fixed width,
     so a tab's size never depends on its own label length. No gap
     between columns any more — cells sit directly edge-to-edge, and
     the '｜' divider is drawn as a border on the cell itself (see
     .mt-tab-cell.sep-left) instead of eating its own gap/track, so an
     active cell's background can reach fully flush against whichever
     neighbor it's next to. The grid itself is only as wide as this
     row's own tabs need — it stays left-aligned, and when it's wider
     than the box actually is, .mt-tab-scroll (its parent) is what
     handles that instead of it being squeezed or clipped. Vertical
     spacing lives entirely on .mt-tab-wrap now, not here. */
  align-items: stretch;
  column-gap: 0;
}
.mt-tab-cell {
  /* This div is the actual grid item now (not the button/span inside
     it) — divs reliably stretch to their grid column's full width by
     default, which a <button> does not always do. min-width:0 blocks
     the "item won't shrink below its content" default that could
     otherwise re-expand the column past the fixed px width.
     display:flex here (stretch is flex's default align-items) forces
     the button/span inside to fill this cell's FULL height, not just
     its own natural content height — otherwise the text could sit
     flush at the top with the cell's stretched extra height left
     empty underneath, instead of the text landing dead-center with
     equal space above and below within the gold background. */
  display: flex;
  min-width: 0;
  box-sizing: border-box;
}
/* The '｜' divider between two tabs, as a 1px border instead of a text
   glyph — a glyph carries its own font whitespace that can't be
   squeezed to 0, a border can. Only present when NEITHER side of this
   edge is the active tab (see myTeamsOverview.js) — once one is, the
   active cell's own background fills that edge instead. */
.mt-tab-cell.sep-left { border-left: 1px solid #887242; }
.mt-tab-cell.active {
  background: #887242;
  /* Square corners, not rounded — per explicit reference image, the
     active highlight reads as a flat continuous bar, not a chip. */
  border-radius: 0;
}
.mt-tab {
  background: none;
  border: none;
  width: 100%;
  box-sizing: border-box;
  /* Padding is intentionally NOT symmetric — tighter top/bottom than
     left/right, per explicit request (the active highlight should
     read as a wide, short bar, not a box with equal padding on every
     side). */
  padding: 3px 2px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  cursor: pointer;
  color: #887242;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mt-tab-cell.active .mt-tab { color: #201e2f; }
.mt-tab-disabled {
  color: #5c5c5c;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  cursor: default;
  width: 100%;
  box-sizing: border-box;
  padding: 3px 2px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mt-tab-link {
  color: var(--accent-teal);
  font-size: 0.85rem;
  text-decoration: none;
  flex-shrink: 0;
  white-space: nowrap;
}
.mt-tab-link:hover { text-decoration: underline; }
/* Whole-chapter box — tab rows AND whatever team is expanded below
   them both live inside this one border, always visible whether or
   not anything's currently expanded (not a border that only appears
   once you open something). */
.mt-chapter-box {
  max-width: 640px;
  width: 100%;
  /* Falls back to the original hardcoded look when a chapter has no
     章節外觀 entry (2026-09-07) — same --chapter-box-border/-bg vars
     stagesList.js's own .chapter-group reads, set inline per-chapter by
     myTeamsOverview.js/teamNotes.js the same way. */
  border: 1px solid var(--chapter-box-border, #c9a45c);
  border-radius: var(--radius-md);
  margin-bottom: 20px;
  overflow: hidden;
  background: var(--chapter-box-bg, var(--bg-card));
}
.mt-tab-panel {
  padding: 14px 16px;
  /* This divider is what actually marks "tabs end, expanded team
     content begins" now that the outer box itself is always drawn —
     .mt-tab-panel only exists in the DOM while something's open, so a
     plain border-top here is exactly the "點開後才出現分隔線" effect. */
  border-top: 1px solid #c9a45c;
}
/* renderTeamCard() normally draws its own .team-card box (background/
   border/padding) — redundant here since .mt-chapter-box already draws
   that same box, so it's flattened to just the member row. */
.mt-tab-panel .team-card { padding: 0; margin-bottom: 0; border: none; background: none; }
/* Step-1 preview button (see myTeamsOverview.js) — just an unstyled
   button wrapping the avatar-only team card, full width, so clicking
   anywhere on the card row toggles the detail below it open/closed. */
.mt-team-preview-btn {
  display: block;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  text-align: left;
}
.mt-team-detail { margin-top: 12px; padding-top: 12px; border-top: 1px solid #c9a45c; }
/* Scaled down further than the member avatars' own default size (see
   .team-card-members .mini-avatar-stage in this file) — same technique
   (render at full 200×260 reference size, then shrink as one unit via
   transform:scale so CardFace's badges/gradient/border keep correct
   proportions), just a smaller scale factor for this specific context
   per the "隊伍整體等比縮小顯示" request. Bumped from 0.28 to 0.35
   (2026-09-05, "整排比例放大一點") — 200×0.35/260×0.35 = 70/91. */
.mt-tab-panel .team-card-members .mini-avatar,
.mt-tab-panel .team-card-members .mini-avatar-empty { width: 70px; height: 91px; }
.mt-tab-panel .team-card-members .mini-avatar-stage { transform: scale(0.35); }

/* 隊伍筆記 (teamNotes.js) — the enemy row at the top of a 過往關卡's
   panel. Reuses renderEnemyChip (same component as 關卡攻略's own 敵人
   list) but shrunk to roughly match the team card's own mini-avatar
   size just below it (70×91px, see above), not 關卡攻略's full 88px
   square — otherwise the enemy row would visually dominate over the
   team it's paired with. Only icon+name per the "敵人圖示＋名稱" spec;
   the chip's own note text (enemy.note — a different field from the
   team's own 筆記/note) is hidden here, not deleted from the data. */
.tn-enemy-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.tn-enemy-row .enemy-chip { width: 64px; gap: 4px; }
.tn-enemy-row .enemy-portrait,
.tn-enemy-row .enemy-portrait-clip,
.tn-enemy-row .enemy-portrait-empty { width: 64px; height: 64px; }
/* Badge is a fixed 38px normally (see enemy-portrait.css) — scaled
   down to match the smaller 64px portrait here (same ~43% ratio),
   otherwise it reads oversized relative to its own portrait. */
.tn-enemy-row .enemy-portrait-badge { width: 28px; height: 28px; top: -6px; left: -6px; }
.tn-enemy-row .enemy-chip-name { font-size: 0.75rem; text-align: center; }
.tn-enemy-row .enemy-chip-note { display: none; }
/* Separates the enemy row from the team card below it — without this
   the two blocks read as one continuous list, which is confusing
   since they're different kinds of things (敵人 vs 你的隊伍). */
/* Matches .mt-team-detail's own divider exactly (same gold #c9a45c) —
   was using --border-soft before, which reads as basically invisible
   against the dark background, very different from the visible gold
   line further down. This is a standalone empty divider (no content
   below it to pad away from, unlike .mt-team-detail), so symmetric
   margin instead of margin+padding. */
.tn-enemy-divider { margin: 12px 0; border-top: 1px solid #c9a45c; }

.form-row { display: flex; gap: 14px; }
.form-row .form-field { flex: 1; }

/* =========================================================
   Team card
   ========================================================= */
.team-card {
  background: var(--bg-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  margin-bottom: 12px;
  max-width: 640px;
}
/* 推薦隊伍 (official teams, stage-detail.html) — a muted/translucent
   gold tint over the same base card, so it reads as "this one is the
   recommended pick" at a glance without it being a jarring solid color
   block; same gold as .chapter-title/--accent-gold, just heavily
   softened via low-opacity rgba rather than a flat fill. */
.team-card-official {
  background: rgba(201, 164, 92, 0.12);
  border-color: rgba(201, 164, 92, 0.4);
}
.team-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
/* 推薦隊伍's "觀看影片" link (2026-09-06) — same look as .mt-tab-link's
   "查看攻略" (accent-teal, no underline until hover), sits in the same
   header row as the team name via .team-card-head's own
   justify-content:space-between. */
.team-card-video-link {
  color: var(--accent-teal);
  font-size: 0.85rem;
  text-decoration: none;
  flex-shrink: 0;
  white-space: nowrap;
}
.team-card-video-link:hover { text-decoration: underline; }
.team-card-name { font-weight: 700; font-size: 1.02rem; }
.team-card-note { font-size: 0.82rem; color: var(--text-dim); margin-bottom: 10px; white-space: pre-wrap; }
.team-card-members { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.team-card-members .mini-avatar, .team-card-members .mini-avatar-empty {
  width: 80px; height: 104px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border-soft);
  background: var(--bg-elevated);
  position: relative;
  flex-shrink: 0;
}
/* .mini-avatar-stage is rendered at the same 200x260 reference size as
   every other card (.card-btn/.card-preview-box) so CardFace's cqw-based
   badges/gradient/rarity text keep identical proportions, then scaled
   down as one unit — this is what keeps "唯一差異只有等比縮小或放大"
   true against 卡片資料庫 / 卡片管理's full-size card.
   Scale is 0.4 (not 0.36) specifically because 200×0.4 / 260×0.4 land on
   whole pixels (80 / 104), avoiding fractional-pixel rounding. */
.team-card-members .mini-avatar-stage {
  position: relative;
  transform: scale(0.4);
  transform-origin: top left;
}
/* The rarity border normally drawn inside .card-face (via CSS mask) loses
   crisp edges on its right/bottom sides once rendered at full size and
   then shrunk 0.4× by the transform above — masks + transform:scale are
   an unreliable combination for a clean edge at this size. Hidden here;
   .mini-avatar-border below redraws the same border directly at the
   avatar's real, unscaled 80×104 size instead. */
.team-card-members .mini-avatar-stage .card-face-border { display: none; }
.mini-avatar-border {
  position: absolute;
  inset: 0;
  border-radius: 8px; /* matches .mini-avatar's own border-radius */
  padding: 2px;
  pointer-events: none;
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  z-index: 2;
}
.team-card-members .mini-avatar-empty { display: flex; align-items: center; justify-content: center; color: var(--text-faint); font-size: 0.9rem; }
.team-card-footer { display: flex; justify-content: flex-end; gap: 8px; }
.team-card-stage-link { font-size: 0.82rem; color: var(--text-faint); margin-bottom: 8px; }

/* Below ~480px the 80px avatars + gaps no longer fit 5-across inside the
   card's own padding, which is what was breaking out of the frame on
   phones. Shrinking the avatars (rather than wrapping to a second row)
   keeps the "one row = one team" reading intact — wrapping worked but
   read as awkward/unintuitive per feedback. 48px is small but chosen so
   5 avatars + gaps fit down to ~360px-wide phones; flex-wrap:wrap above
   stays on as a last-resort fallback only for anything narrower than
   that (rare at this point), so it never actually breaks the frame. */
@media (max-width: 480px) {
  .team-card-members .mini-avatar, .team-card-members .mini-avatar-empty {
    width: 48px;
    height: 62.4px;
  }
  .team-card-members .mini-avatar-stage {
    transform: scale(0.24); /* 200×0.24 / 260×0.24 = 48 / 62.4 */
  }
}

.team-slots { display: flex; gap: 12px; margin: 6px 0 4px; flex-wrap: wrap; }
.team-slot-btn { width: 88px; }
.team-slot-move-row { display: flex; gap: 4px; margin-top: 4px; }
.team-slot-move-row .btn { flex: 1; padding: 4px 0; font-size: 0.9rem; line-height: 1; }
.team-slot-move-row .btn:disabled { opacity: 0.3; cursor: default; }
.team-slot-label { text-align: center; font-size: 0.7rem; color: var(--text-faint); margin-top: 4px; }

/* =========================================================
   Toast
   ========================================================= */
.toast-stack { position: fixed; bottom: 20px; right: 20px; z-index: 500; display: flex; flex-direction: column; gap: 8px; }
.toast {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-teal);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  font-size: 0.86rem;
  box-shadow: var(--shadow-card);
  animation: toast-in 0.2s ease;
  max-width: 320px;
}
.toast.toast-error { border-left-color: var(--danger); }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* =========================================================
   Footer
   ========================================================= */
.site-footer { border-top: 1px solid var(--border-soft); margin-top: 60px; padding: 26px 0; color: var(--text-faint); font-size: 0.82rem; }

/* =========================================================
   首頁圖片框 (2026-09-07, 後台「首頁外觀」)
   ========================================================= */
.home-card {
  position: relative;
  display: block;
  overflow: hidden;
  /* 首頁圖片框固定直角 (2026-09-07) — 外框漸層本身就沒辦法保持圓角
     (CSS border-image 的限制)，如果只有邊框變直角、圖片裁切還是圓角，
     兩者對不起來會很奇怪，乾脆整組固定用直角，不看有沒有開漸層。 */
  border-radius: 0;
  text-decoration: none;
}
/* 跟 card-face.css 的 .card-face-clip/.card-face-img 同一套定位邏輯
   (2026-09-07) — mountCoverImage() 用 JS 算縮放/位移，需要這個
   position:absolute + inset:0 的容器當基準，才能吃到後台裁切器存的
   imageZoom/imageOffsetX/imageOffsetY。 */
.home-card-clip { position: absolute; inset: 0; overflow: hidden; }
.home-card-clip .card-face-img { position: absolute; object-fit: cover; -webkit-user-drag: none; }
.home-card-overlay {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 16px; display: flex; flex-direction: column; gap: 2px;
  background: linear-gradient(to top, rgba(0,0,0,0.75), transparent);
}
.home-card-overlay strong { color: #fff; font-size: 1.05rem; }
.home-card-overlay span { color: rgba(255,255,255,0.85); font-size: 0.85rem; }

/* 版主的隊伍展示頁 (2026-09-09) */
.mv-section-title { font-family: var(--font-display); color: var(--accent-gold); margin: 28px 0 14px; }

/* 全站靜態星空背景 (2026-09-11，見 Navbar.js 的 mountStaticStarfield)。
   首頁自己另有一份更豐富的版本，class 名稱不同 (#home-starfield)，
   不會被這份蓋到。 */
.site-starfield { position: fixed; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }

/* =========================================================
   連續選取隊伍 (CardPicker.js 的 openTeamPicker, 2026-09-09)
   ========================================================= */
.tp-slot-row {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
  /* Sticks to the top of .modal-body's own scroll area (not the whole
     page) while the filter panel below scrolls underneath it — same
     idea as the modal's header staying put, just one level down. The
     negative margin + matching padding trick makes the sticky bar
     bleed full-width flush against modal-body's own 20px/22px padding
     (otherwise it'd float with visible gaps at both sides once stuck,
     letting scrolled content show around it instead of behind it).
     Bottom padding is bigger than the others specifically to cover the
     "移到第__位" dropdown's -22px overhang (see .tp-slot-select below)
     with solid background, so that overhang doesn't visually sit on
     top of whatever scrolls up underneath it. */
  position: sticky;
  top: -20px;
  z-index: 2;
  margin: -20px -22px 0;
  padding: 20px 22px 32px;
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border-soft);
}
.tp-slot {
  /* Grid controls the actual width now (always exactly 1/5 of the row,
     never wraps) — this used to be a fixed 64px, which is what caused
     the 5th slot to wrap onto its own row on narrow phone screens
     (2026-09-12 修正). min-width:0 lets it actually shrink that small;
     without it a flex/grid item's default min-width is its content's
     natural size, which is wider than that on some screens. */
  width: auto;
  min-width: 0;
  position: relative;
}
.tp-slot-empty {
  aspect-ratio: 200 / 260;
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elevated);
}
/* 已選的格子改成直接放 renderCardButton() 出來的 .card-btn，跟卡片本體
   共用同一顆元件 (稀有度邊框/徽章/SSR字樣/imageZoom 裁切定位都跟著來)，
   這裡只需要把它縮到格子寬度 (2026-09-09 修正：原本是自己刻的 <img>，
   裁切位置跟卡片本身對不上)。 */
.tp-slot .card-btn { width: 100%; cursor: pointer; }
.tp-slot-num { color: var(--text-faint); font-size: 1.1rem; font-family: var(--font-mono); }
.tp-slot-select {
  position: absolute; left: 50%; bottom: -22px;
  transform: translateX(-50%);
  /* 比格子本身寬一點，並關掉瀏覽器原生下拉箭頭，「第 N 位」四個字才不會被擠斷
     (2026-09-09 修正)。 */
  width: 80px;
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding: 2px 4px;
  font-size: 0.72rem; text-align: center; text-align-last: center;
  background: var(--bg-elevated); color: var(--text-dim);
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
}
