/* Campaign list page: align wrapper + text colors with dark island cards. */
#route-campaign:not(.hidden) {
  background: #121a2b;
  color: #e8ecf4;
  border: 1px solid #2a3a56;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  padding: 14px;
}

#route-campaign:not(.hidden) h2,
#route-campaign:not(.hidden) h3 {
  color: #f8fbff;
}

.campaign-elo { margin: 0 0 8px; color: #f2f7ff; font-weight: 600; }
.campaign-hint { color: #a9badc; font-size: 0.9rem; margin: 0 0 16px; max-width: 52rem; }

.campaign-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(340px, 1fr));
  gap: 14px;
}
@media (max-width: 1180px) {
  .campaign-grid {
    grid-template-columns: repeat(2, minmax(320px, 1fr));
  }
}
@media (max-width: 760px) {
  .campaign-grid {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 1024px) and (orientation: portrait) {
  #route-campaign:not(.hidden) {
    padding: 10px;
    overflow-x: hidden;
  }
  .campaign-grid {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .island-card {
    min-width: 0;
    padding: 10px;
  }
  .enemy-row {
    min-width: 0;
    gap: 8px;
  }
  .enemy-meta {
    min-width: 0;
  }
  .enemy-status {
    min-width: 0;
    font-size: 0.8rem;
    margin-left: auto;
  }
}
.island-card {
  border: 1px solid #304263;
  border-radius: 10px;
  padding: 12px;
  background: #172136;
}
.island-card h3 {
  margin: 0 0 10px;
  font-size: 1.05rem;
}
.island-enemies {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.enemy-row {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-start;
  border-top: 1px solid #2a3a56;
  padding: 8px 0;
}
.enemy-row:first-of-type { border-top: 0; }
.enemy-avatar {
  border-radius: 6px;
  object-fit: cover;
  flex-shrink: 0;
  background: #1a2233;
}
.enemy-avatar-trigger {
  padding: 0;
  border: 0;
  background: transparent;
  line-height: 0;
  cursor: pointer;
  border-radius: 6px;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.enemy-avatar-trigger:hover,
.enemy-avatar-trigger:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 0 0 2px rgba(232, 197, 71, 0.55);
  outline: none;
}
.enemy-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.enemy-name {
  font-weight: 600;
  color: #eef3ff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.enemy-stars { color: #e8c547; font-size: 0.85rem; letter-spacing: 1px; }
.enemy-status { color: #c6d4ef; font-size: 0.85rem; min-width: 4.5rem; text-align: right; }
.enemy-row button { flex-shrink: 0; }

.campaign-boss-card {
  margin-top: 20px;
  padding: 16px;
  border: 1px solid #5a4a2a;
  border-radius: 10px;
  background: #1a1820;
}
.campaign-boss-card h3 { margin: 0 0 12px; }
.boss-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.boss-avatar { border: 1px solid #8a7a4a; }

.campaign-match-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}
.campaign-match-header h2 { margin: 0; flex: 1; min-width: 200px; }
.btn-back-campaign,
.btn-undo-campaign {
  background: #2a3a56;
  border: none;
  border-radius: 8px;
  color: #e8ecf4;
  padding: 0.5rem 0.75rem;
  font: inherit;
  cursor: pointer;
}

/* Campaign match: fill <main> between navbar + footer — avoid 100dvh math that ignores main padding/footer
   and clashes with .route-section { min-height: 70vh } (causes useless scroll + clipped board). */
main.main:has(#route-campaign-match.campaign-match-layout:not(.hidden)) {
  display: flex;
  flex-direction: column;
  /* Campaign match should be fullscreen-like (no shell padding/max-width wrap). */
  padding: 0;
  max-width: none;
  margin: 0;
  min-height: 100dvh;
  min-height: min(100dvh, 100svh);
  /* Match shell: dark so no bright page background shows at edges. */
  background: #07090e;
  color: #c4d0e6;
}

#route-campaign-match.campaign-match-layout:not(.hidden) {
  /* Override `.route-section` chrome for a clean fullscreen match surface. */
  padding: 0;
  margin: 0;
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* Hide site shell only on campaign match like SoftwaresShare fullscreen pages. */
body:has(#route-campaign-match.campaign-match-layout:not(.hidden)) .navbar {
  display: none !important;
}

body:has(#route-campaign-match.campaign-match-layout:not(.hidden)) .footer {
  display: none !important;
}

/* Definite viewport height so flex children (board wrap) get a real max-height — fixes bottom clip.
   min(100dvh, 100svh): on iPad Safari portrait, dynamic vh can exceed the visible area vs the
   address bar; svh caps to the smaller viewport so flex allocation matches the real screen. */
#route-campaign-match.campaign-match-layout {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-height: 0 !important; /* override .route-section min-height: 70vh */
  height: 100dvh;
  max-height: 100dvh;
  height: min(100dvh, 100svh);
  max-height: min(100dvh, 100svh);
  /* Home indicator / safe area — flex math otherwise treats full dvh as usable and the board clips. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
  overflow: hidden;
  box-sizing: border-box;
  /* Game-style match surface: deep cool shadows + subtle warm rim (pairs with end-of-match modal). */
  background:
    radial-gradient(ellipse 100% 70% at 50% -10%, rgba(201, 162, 39, 0.07) 0%, transparent 42%),
    radial-gradient(ellipse 90% 55% at 50% 100%, rgba(30, 42, 64, 0.45) 0%, transparent 50%),
    linear-gradient(168deg, #0a0d14 0%, #101520 38%, #0b0e16 100%);
  color: #b9c5da;
}
#route-campaign-match.campaign-match-layout .campaign-match-main {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
}
#route-campaign-match.campaign-match-layout .campaign-match-header {
  flex-shrink: 0;
}
#route-campaign-match.campaign-match-layout .campaign-match-players {
  flex-shrink: 0;
  max-width: 100%;
}
#route-campaign-match.campaign-match-layout .campaign-match-actions {
  flex-shrink: 0;
}

/* Buttons + player labels: dark “game HUD” (scoped to in-match route only). */
#route-campaign-match.campaign-match-layout .btn-back-campaign,
#route-campaign-match.campaign-match-layout .btn-undo-campaign {
  background: linear-gradient(180deg, #2a3448 0%, #1a2233 100%);
  border: 1px solid #3d4d66;
  color: #e8ecf4;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
#route-campaign-match.campaign-match-layout .btn-back-campaign:hover,
#route-campaign-match.campaign-match-layout .btn-undo-campaign:hover {
  filter: brightness(1.08);
  border-color: #a88c28;
}
#route-campaign-match.campaign-match-layout .campaign-match-player-name {
  color: #a8b8d4;
}
#route-campaign-match.campaign-match-layout .campaign-match-user-name,
#route-campaign-match.campaign-match-layout .campaign-match-user-elo {
  color: #a8b8d4;
}

.campaign-match-players {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  max-width: min(100%, 520px);
  margin: 0 auto 12px;
  padding: 0 4px;
}
.campaign-match-player {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 0;
  text-align: center;
}
.campaign-match-player--you { align-items: flex-start; }
.campaign-match-player--enemy {
  align-items: flex-start;
}
/* Let the name block use the full player column width; align-self stretch avoids min-content (one char per line with wrap). */
.campaign-match-player--enemy .campaign-match-enemy-stack {
  align-self: flex-start;
}
.campaign-match-player--enemy .campaign-match-enemy-identity {
  align-self: stretch;
  width: 100%;
  box-sizing: border-box;
}
.campaign-match-avatar-wrap {
  position: relative;
  width: 64px;
  height: 64px;
  border-radius: 10px;
  overflow: hidden;
  background: #1a2233;
  border: 2px solid #304263;
  flex-shrink: 0;
}
.campaign-match-player--enemy .campaign-match-avatar-wrap {
  border-color: #4a5568;
}
.campaign-match-avatar {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}
.campaign-match-thinking {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 14, 24, 0.55);
  pointer-events: none;
}
.campaign-match-thinking.hidden {
  display: none;
}
.campaign-match-thinking img {
  width: min(70%, 48px);
  height: auto;
  max-height: 78%;
  object-fit: contain;
}
.campaign-match-player-name {
  font-size: 0.8rem;
  color: #9aaccc;
  max-width: min(100%, 240px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.campaign-match-player--you .campaign-match-player-name {
  text-align: left;
}

/* “PePong (1650)”: nickname line (ellipsis if long), ELO on the line below */
.campaign-match-user-identity {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: min(100%, 240px);
  min-width: 0;
  width: 100%;
  gap: 2px;
  line-height: 1.25;
  text-align: left;
}
.campaign-match-user-name {
  font-size: 0.8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 100%;
  min-width: 0;
}
.campaign-match-user-elo {
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

.campaign-match-enemy-stack {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 64px;
}
.campaign-match-enemy-identity {
  max-width: min(100%, 240px);
  text-align: left;
  line-height: 1.45;
}
/* Name on first line, stars always on the line below (campaign match). */
.campaign-match-enemy-id-inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}
.campaign-match-player--enemy .campaign-match-enemy-name.campaign-match-player-name {
  display: block;
  width: 100%;
  text-align: left;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: break-word;
  word-break: normal;
}
.campaign-match-enemy-stars {
  display: block;
  flex-shrink: 0;
  color: #e8c547;
  font-size: 0.95rem;
  line-height: 1.2;
  letter-spacing: 0.06em;
  white-space: nowrap;
  text-shadow: 0 0 10px rgba(232, 197, 71, 0.45);
  user-select: none;
}
.campaign-enemy-bubble {
  position: absolute;
  /* Show speech bubble to the right of the enemy avatar. */
  top: 50%;
  left: 100%;
  bottom: auto;
  right: auto;
  transform: translateY(-50%);
  margin-left: 10px;
  display: block;
  box-sizing: border-box;
  /* Avoid shrink-to-fit one character wide (vertical “stripe”); allow 1–2 lines of text */
  min-width: 9rem;
  max-width: min(260px, 78vw);
  padding: 10px 12px;
  background: #2a3348;
  border: 1px solid #4a5a78;
  border-radius: 10px;
  font-size: 0.82rem;
  line-height: 1.45;
  color: #e8ecf4;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  z-index: 6;
  text-align: left;
  white-space: normal;
  overflow-wrap: break-word;
  word-break: normal;
}
.campaign-enemy-bubble.hidden {
  display: none;
}
.campaign-enemy-bubble::after {
  content: "";
  position: absolute;
  top: 50%;
  left: -14px;
  transform: translateY(-50%);
  border: 7px solid transparent;
  border-right-color: #2a3348;
}

.campaign-board-wrap {
  max-width: min(100%, 520px);
  margin: 0 auto 16px;
}

/* Out of flex flow so the board can use full column height (bigger square ⇒ less empty side space). */
#route-campaign-match.campaign-match-layout .campaign-match-back {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  /* Same max width as #campaign-xiangqiboard — centered row; two buttons = 50/50 */
  width: min(calc(100cqmin * 0.85), calc(100% - 3.25rem));
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  justify-content: center;
  gap: 8px;
  padding: 8px 12px 12px;
  box-sizing: border-box;
  flex-shrink: 0;
  pointer-events: none;
}
#route-campaign-match.campaign-match-layout .campaign-match-back .btn-back-campaign,
#route-campaign-match.campaign-match-layout .campaign-match-back .btn-undo-campaign {
  pointer-events: auto;
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
}
@supports not (width: 1cqmin) {
  #route-campaign-match.campaign-match-layout .campaign-match-back {
    width: min(calc(92vmin * 0.85), calc(100% - 3.25rem));
  }
}

/* Very narrow: stack Campaign + Undo (each full width of the bar). */
@media (max-width: 400px) {
  #route-campaign-match.campaign-match-layout .campaign-match-back .btn-back-campaign,
  #route-campaign-match.campaign-match-layout .campaign-match-back .btn-undo-campaign {
    flex: 1 1 100%;
  }
}

/* Mobile-only Undo placement (shown in portrait; hidden on desktop). */
#route-campaign-match.campaign-match-layout .campaign-match-undo-mobile {
  display: none;
}

/* Board area grows to use remaining height; square is limited by the smaller axis (critical in landscape). */
#route-campaign-match.campaign-match-layout .campaign-board-wrap {
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  max-width: none;
  margin: 0;
  /* Reserve space for absolutely positioned back button */
  padding-bottom: 3.25rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  container-type: size;
  container-name: campaign-board;
  overflow: hidden;
}
/* Size square from the board *container* (not raw 100dvh), so flex siblings (avatars, back btn) cannot clip it. */
#route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
  /* Resize board to half (square) using the viewport-based min axis. */
  /* Let the widget determine exact square height from width (prevents internal border clipping). */
  width: min(calc(100cqmin * 0.85), calc(100% - 3.25rem));
  max-width: 100%;
  height: auto;
  min-width: 0;
  min-height: 0;
  margin: 0 auto;
  box-sizing: border-box;
}
/* Fallback when container queries are unavailable: vmin tracks the shorter screen side. */
@supports not (width: 1cqmin) {
  #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
    width: min(calc(92vmin * 0.85), calc(100% - 3.25rem));
    max-width: 100%;
    height: auto;
  }
}

/* Landscape: avatar column left of the board (phones, tablets, desktop). Use orientation only —
   phone landscape is usually <1024px wide; the old min-width:1024px gate left those on the
   stacked-above-board layout. Portrait tablets (e.g. iPad 1024px wide) stay portrait orientation,
   so they never match (orientation: landscape) and won’t pick up this row layout. */
@media (orientation: landscape) {
  #route-campaign-match.campaign-match-layout {
    flex-direction: row;
    align-items: stretch;
  }

  #route-campaign-match.campaign-match-layout .campaign-match-players {
    flex-shrink: 0;
    width: fit-content;
    max-width: 240px;
    min-width: 132px;
    margin: 0;
    padding: 16px 12px;
    height: auto;
    flex: 0 0 auto;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-start;
    text-align: left;
    gap: 18px;
    border-right: 1px solid #1e2638;
    background: linear-gradient(180deg, rgba(14, 17, 26, 0.97) 0%, rgba(10, 12, 18, 0.98) 100%);
    box-shadow: inset -1px 0 0 rgba(201, 162, 39, 0.06);
  }

  /* Enemy at top of left panel, user at bottom (DOM order is user then enemy). */
  #route-campaign-match.campaign-match-layout .campaign-match-player--enemy {
    order: -1;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--you {
    order: 1;
  }

  #route-campaign-match.campaign-match-layout .campaign-match-main {
    flex: 1 1 0;
    margin: 0;
    min-width: 0;
    min-height: 0;
    /* Put back button next to the board (not below it) to avoid clipping. */
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    height: 100%;
    align-self: stretch;
  }

  #route-campaign-match.campaign-match-layout .campaign-board-wrap {
    margin: 0;
    max-width: none;
    flex: 1 1 0;
    min-height: 0;
    overflow: hidden;
    align-items: center;
    justify-content: center;
    height: 100%;
    min-width: 0;
    padding-bottom: 3.25rem;
  }

  #route-campaign-match.campaign-match-layout .campaign-match-back {
    margin-left: auto;
    margin-right: auto;
    padding: 8px 12px 12px;
    flex-shrink: 0;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 3;
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    justify-content: center;
    gap: 8px;
    pointer-events: none;
  }

  /* Same as portrait: square is limited by board-wrap (flex area), not by 100dvh math. */
  #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
    width: min(calc(100cqmin * 0.85), calc(100% - 3.25rem));
    max-width: 100%;
    min-width: 0;
    min-height: 0;
    margin: 0 auto;
    box-sizing: border-box;
    height: auto;
  }

  @supports not (width: 1cqmin) {
    #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
      width: min(calc(92vmin * 0.85), calc(100% - 3.25rem));
      max-width: 100%;
      height: auto;
    }
  }

  #route-campaign-match.campaign-match-layout .campaign-match-player {
    align-items: flex-start;
    text-align: left;
  }
}

/* Small phone landscape: Campaign + Undo moved under avatars in the left column (see placeCampaignMatchActionBar). */
@media (orientation: landscape) and (max-width: 934px) {
  #route-campaign-match.campaign-match-layout .campaign-match-players {
    justify-content: flex-start;
    gap: 10px;
    align-items: stretch;
    min-height: 0;
    overflow-y: auto;
  }

  /* Enemy -1, you 1, actions last — default order:0 on back sat between enemy and user. */
  #route-campaign-match.campaign-match-layout .campaign-match-players .campaign-match-back {
    order: 2;
    position: static;
    inset: auto;
    left: auto;
    right: auto;
    bottom: auto;
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 8px 10px 10px;
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: flex-start;
    align-items: stretch;
    gap: 8px;
    pointer-events: auto;
    flex-shrink: 0;
    box-sizing: border-box;
  }

  #route-campaign-match.campaign-match-layout .campaign-match-players .campaign-match-back .btn-back-campaign,
  #route-campaign-match.campaign-match-layout .campaign-match-players .campaign-match-back .btn-undo-campaign {
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
    text-align: center;
  }

  /* Let board/pieces extend to edges of the host; default overflow:hidden on main/wrap was clipping the bottom rank. */
  #route-campaign-match.campaign-match-layout .campaign-match-main {
    justify-content: center;
    min-height: 0;
    overflow: visible;
  }

  #route-campaign-match.campaign-match-layout .campaign-match-main .campaign-board-wrap {
    padding-bottom: 0;
    flex: 1 1 auto;
    min-height: 0;
    max-height: min(100%, 100svh, 100dvh);
    height: 100%;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0;
    overflow: visible;
  }

  /*
   * JS sets inline width/maxWidth and steps down until offsetHeight fits the wrap; host height
   * stays auto so the widget can size grid + notation. overflow:visible avoids clipping the bottom rank.
   */
  #route-campaign-match.campaign-match-layout .campaign-match-main #campaign-xiangqiboard {
    width: min(100cqw, 100cqh, 100vmin);
    max-width: min(100%, 100vmin, 100svh);
    max-height: none;
    height: auto;
    aspect-ratio: unset;
    margin: auto;
    overflow: visible;
    box-sizing: border-box;
    flex: 0 0 auto;
    align-self: center;
    min-height: 0;
  }

  @supports not (width: 1cqw) {
    #route-campaign-match.campaign-match-layout .campaign-match-main #campaign-xiangqiboard {
      width: min(100vmin, 100%);
      max-height: none;
      aspect-ratio: unset;
    }
  }
}

@media (max-height: 480px) {
  #route-campaign-match.campaign-match-layout .campaign-match-players {
    margin-bottom: 6px;
    gap: 8px;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-avatar-wrap {
    width: 52px;
    height: 52px;
  }
}

/* Portrait / square (width ≤ height): avatars + names in a full-width top bar; board + actions below.
   Each player column flexes to half the row so justify-content can pin enemy left and you right. */
@media (max-aspect-ratio: 1/1) {
  #route-campaign-match.campaign-match-layout .campaign-match-players {
    width: 100%;
    max-width: none;
    margin: 10px 0 10px;
    padding: 10px 10px;
    box-sizing: border-box;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 10px;
    background: rgba(12, 15, 22, 0.72);
    border: 1px solid rgba(40, 50, 68, 0.55);
    border-radius: 10px;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    flex: 1 1 0;
    min-width: 0;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--enemy {
    order: -1;
    justify-content: flex-start;
  }
  /* DOM is avatar then identity; row-reverse + flex-start (row-reverse main-start = right) puts avatar on the edge. */
  #route-campaign-match.campaign-match-layout .campaign-match-player--you {
    order: 1;
    flex-direction: row-reverse;
    justify-content: flex-start;
  }
  #route-campaign-match.campaign-match-player-name {
    max-width: 100%;
    text-align: left;
  }
  /* Don’t grow: flex-grow was stretching the column left; width:100% on the name then
     pinned the nickname to the far left of that gap (looks “centered” in the bar). */
  #route-campaign-match.campaign-match-layout .campaign-match-user-identity {
    flex: 0 1 auto;
    min-width: 0;
    width: auto;
    max-width: min(100%, 240px);
    align-items: flex-end;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--you .campaign-match-user-name {
    width: auto;
    max-width: min(100%, 240px);
    min-width: 0;
    text-align: right;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--you .campaign-match-user-elo {
    text-align: right;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--enemy .campaign-match-enemy-identity {
    text-align: left;
    /* Row layout: take remaining width next to avatar so the name isn’t min-content (one char per line). */
    flex: 1 1 auto;
    min-width: 0;
    width: auto;
    max-width: none;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--enemy .campaign-match-enemy-id-inner {
    align-items: flex-start;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-player--enemy .campaign-match-enemy-name.campaign-match-player-name {
    text-align: left;
  }

  #route-campaign-match.campaign-match-layout .campaign-match-main {
    justify-content: flex-start;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-back {
    position: static;
    order: 0;
    pointer-events: auto;
    /* Match board width: largest square in the board area (no 0.85 shrink — fills width when height allows). */
    width: min(100cqw, 100cqh);
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
    z-index: auto;
    padding: 8px 10px 12px;
    box-sizing: border-box;
    justify-content: center;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 8px;
  }
  #route-campaign-match.campaign-match-layout .campaign-match-back .btn-back-campaign,
  #route-campaign-match.campaign-match-layout .campaign-match-back .btn-undo-campaign {
    flex: 1 1 0;
    min-width: 0;
    width: auto;
    max-width: none;
    box-sizing: border-box;
    text-align: center;
  }
  #route-campaign-match.campaign-match-layout .campaign-board-wrap {
    padding-bottom: 0;
    padding-left: 0;
    padding-right: 0;
    justify-content: flex-start;
    margin-bottom: 0;
  }
  /* Largest square that fits; cap height so the widget cannot exceed the flex area (fixes short
     viewports e.g. Surface Duo where overflow:hidden on main would clip the bottom). */
  #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
    width: min(100cqw, 100cqh);
    max-width: 100%;
    max-height: min(100cqw, 100cqh);
    aspect-ratio: 1 / 1;
  }
  @supports not (width: 1cqw) {
    #route-campaign-match.campaign-match-layout .campaign-match-back {
      width: min(92vmin, 100%);
    }
    #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
      width: min(92vmin, 100%);
    }
  }
}

/* Desktop non-emulation square-ish range (reported 645x914 .. 914x914):
   keep the same stacked layout but reduce board/control scale so the board
   never gets clipped by the bottom edge.
   Require min-width so phone portrait (e.g. 390x844) does not match — those
   viewports also satisfy max-width/max-height 914 and would wrongly override
   the full-width portrait board rules with 0.86 scaling (side gaps). */
@media (min-width: 520px) and (max-width: 914px) and (max-height: 914px) {
  #route-campaign-match.campaign-match-layout .campaign-match-back,
  #route-campaign-match.campaign-match-layout .campaign-match-undo-mobile {
    width: min(calc(100cqw * 0.86), calc(100cqh * 0.86), 100%);
  }

  #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
    width: min(calc(100cqw * 0.86), calc(100cqh * 0.86), 100%);
    max-width: 100%;
    max-height: min(calc(100cqw * 0.86), calc(100cqh * 0.86));
    aspect-ratio: 1 / 1;
  }

  @supports not (width: 1cqw) {
    #route-campaign-match.campaign-match-layout .campaign-match-back,
    #route-campaign-match.campaign-match-layout .campaign-match-undo-mobile,
    #route-campaign-match.campaign-match-layout #campaign-xiangqiboard {
      width: min(80vmin, 100%);
      max-height: 80vmin;
    }
  }
}

.campaign-board-wrap .campaign-error { color: #ff9b9b; }
.campaign-match-actions { display: flex; gap: 10px; flex-wrap: wrap; }

/* End-of-match modal (SoftwaresShare `campaign-congrats` pattern) */
.campaign-match-end-modal {
  display: none;
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.85);
  z-index: 1200;
  justify-content: center;
  align-items: center;
  padding: 2rem;
}
.campaign-match-end-modal.active {
  display: flex;
}
.campaign-match-end-inner {
  max-width: 420px;
  background: linear-gradient(180deg, #4a4035 0%, #3a3028 100%);
  border: 3px solid #c9a227;
  border-radius: 16px;
  padding: 2rem;
  text-align: center;
  white-space: pre-line;
  line-height: 1.6;
  color: #e8e4dc;
}
.campaign-match-end-inner h2 {
  color: #c9a227;
  margin: 0 0 1rem;
  font-size: 1.35rem;
}
.campaign-match-end-inner p {
  margin: 0;
}
.campaign-match-end-inner button {
  margin-top: 1rem;
  padding: 0.5rem 1.5rem;
  font-weight: bold;
  cursor: pointer;
  background: #c9a227;
  color: #1a1510;
  border: none;
  border-radius: 8px;
}
.campaign-match-end-inner button:hover {
  filter: brightness(1.08);
}
.campaign-match-quit-actions {
  margin-top: 1rem;
  display: flex;
  gap: 0.75rem;
  flex-wrap: nowrap;
  width: 100%;
}
.campaign-match-quit-actions button {
  margin-top: 0;
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
  white-space: nowrap;
  padding-inline: 0.75rem;
}

.campaign-enemy-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1250;
  background: rgba(0, 0, 0, 0.72);
  padding: 16px;
  align-items: center;
  justify-content: center;
}
.campaign-enemy-modal.active {
  display: flex;
}
.campaign-enemy-modal-inner {
  width: min(94vw, 620px);
  max-height: min(92vh, 820px);
  overflow: auto;
  background: linear-gradient(180deg, #20283c 0%, #161d2f 100%);
  border: 1px solid #40506e;
  border-radius: 14px;
  padding: 18px;
  box-shadow: 0 16px 42px rgba(0, 0, 0, 0.45);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  position: relative;
}
.campaign-enemy-modal-close {
  position: absolute;
  right: 10px;
  top: 8px;
  border: 0;
  background: transparent;
  color: #cfd8e9;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.campaign-enemy-modal-avatar {
  width: min(74vw, 420px);
  height: min(74vw, 420px);
  border-radius: 12px;
  object-fit: cover;
  border: 2px solid #6f7b97;
  background: #1a2233;
}
.campaign-enemy-modal-title {
  margin: 4px 0 0;
  font-size: 1.15rem;
}
.campaign-enemy-modal-stars {
  margin: 0;
  color: #f0cc4c;
  letter-spacing: 1.5px;
  font-size: 1rem;
}
.campaign-enemy-modal-status {
  margin: 0;
  color: #9aaccc;
  font-size: 0.92rem;
}
.campaign-enemy-modal-description {
  margin: 4px 0 0;
  color: #d6deed;
  font-size: 0.92rem;
  line-height: 1.55;
}
.campaign-enemy-modal-actions {
  margin-top: 10px;
  width: min(100%, 360px);
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 10px;
}
.campaign-enemy-modal-play,
.campaign-enemy-modal-cancel {
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
}

@media (max-width: 540px) {
  .campaign-enemy-modal-inner {
    width: 100%;
    max-height: 92vh;
    padding: 16px;
  }
  .campaign-enemy-modal-avatar {
    width: min(80vw, 320px);
    height: min(80vw, 320px);
  }
}

/* Selected square (click-to-move): basic brown until 1★ reward; blue aura after */
#campaign-xiangqiboard [data-square].campaign-sq-selected.campaign-sq-selected--basic {
  background-color: rgba(120, 70, 35, 0.45) !important;
  box-shadow: inset 0 0 0 2px rgba(255, 200, 120, 0.75);
  border-radius: 4px;
  z-index: 2;
}
#campaign-xiangqiboard [data-square].campaign-sq-selected.campaign-sq-selected--glow {
  position: relative;
  overflow: visible;
  border-radius: 4px;
  z-index: 2;
}
#campaign-xiangqiboard [data-square].campaign-sq-selected.campaign-sq-selected--glow::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: 100%;
  margin-left: -50%;
  margin-top: -50%;
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(circle at center, rgba(15, 55, 255, 0.92) 0%, rgba(25, 70, 255, 0.7) 35%, rgba(30, 80, 255, 0.4) 55%, transparent 75%);
  box-shadow: inset 0 0 12px 4px rgba(15, 55, 255, 0.9), 0 0 10px 3px rgba(15, 55, 255, 0.7);
}
#campaign-xiangqiboard [data-square].campaign-sq-selected.campaign-sq-selected--glow > * {
  position: relative;
  z-index: 1;
}
