/* ============ Variables ============
   --card-h is the only "input" — everything else is derived. clamp() floors at
   90 (legibility floor on small screens), uses (viewport_h - chrome) / 6 for
   normal sizing, and caps at 132 on big screens so the board stays compact. */
:root {
  --bg: #0e131c;
  --bg-felt-1: #1b2540;
  --bg-felt-2: #0b1226;
  --bg-card: #2a3650;
  --bg-card-2: #1f2a40;
  --line: #2e3a55;
  --text: #e8eaf0;
  --text-dim: #8990a5;
  --accent: #4a7dd1;
  --accent-soft: rgba(74, 125, 209, 0.22);
  --gold: #c9a86a;
  --warn: #d14a4a;
  --good: #5cb16a;
  --shadow-card: 0 2px 6px rgba(0, 0, 0, 0.5);
  --shadow-lift: 0 18px 30px rgba(0, 0, 0, 0.65), 0 6px 12px rgba(0, 0, 0, 0.4);
  --shadow-pile: 0 4px 14px rgba(0, 0, 0, 0.6);
  --radius: 8px;
  --bar-h: 50px;

  --card-h: clamp(90px, calc((100vh - 156px) / 6), 132px);
  --card-w: calc(var(--card-h) * 96 / 132);
  /* Hand cards are 50% bigger than battlefield/pile cards — the hand peek
     is the only thing you can read at a glance, so it needs the room. */
  --hand-card-h: calc(var(--card-h) * 1.5);
  --hand-card-w: calc(var(--card-w) * 1.5);
  --hand-peek: calc(var(--hand-card-h) * 34 / 132);
  --opp-card-w: calc(var(--card-h) * 60 / 132);
  --opp-card-h: calc(var(--card-h) * 84 / 132);
  --stack-w: calc(var(--card-h) * 86 / 132);
  --stack-h: calc(var(--card-h) * 120 / 132);
  --sidebar-w: clamp(160px, calc(var(--card-h) * 220 / 132), 220px);
  --label-font: clamp(9px, calc(var(--card-h) * 11 / 132), 11px);
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font: 14px/1.4 system-ui, -apple-system, sans-serif;
  background: #050810;
  color: var(--text);
  height: 100vh;
  overflow: hidden;
  user-select: none;
  cursor: default;
}
body.dragging * { cursor: grabbing !important; }

button, input {
  font: inherit;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 12px;
  cursor: pointer;
}
button:hover { background: var(--accent-soft); border-color: var(--accent); }
button.primary { background: var(--accent-soft); border-color: var(--accent); }
input { cursor: text; background: rgba(0, 0, 0, 0.3); }

/* ============ Top bar ============ */

.topbar {
  position: fixed; top: 0; left: 0; right: 0;
  height: var(--bar-h);
  background: linear-gradient(to bottom, #131825, #0a0e18);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 16px;
  z-index: 200;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}
.topbar-left, .topbar-right { display: flex; align-items: center; gap: 8px; }
.brand {
  font-weight: 700; letter-spacing: 1.5px;
  color: var(--gold);
  margin-right: 16px;
  font-size: 13px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.conn-status {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 3px 9px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.4);
  color: var(--text-dim);
  margin-left: 6px;
}
.conn-status.online { color: var(--good); background: rgba(92, 177, 106, 0.15); }
.conn-status.connecting { color: var(--gold); background: rgba(201, 168, 106, 0.15); }

#roomInput { width: 280px; font-family: ui-monospace, "SF Mono", monospace; font-size: 12px; }

/* ============ Board (the 3D stage) ============ */

.board {
  position: fixed;
  top: var(--bar-h);
  bottom: 0;
  left: 0; right: 0;
  perspective: 1800px;
  perspective-origin: 50% 70%;
  overflow: hidden;
  background:
    radial-gradient(ellipse 60% 50% at 50% 50%, rgba(74, 125, 209, 0.10), transparent 70%),
    radial-gradient(circle at 50% 110%, rgba(201, 168, 106, 0.06) 0%, transparent 50%),
    repeating-linear-gradient(45deg,  rgba(255, 255, 255, 0.015) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(-45deg, rgba(0, 0, 0, 0.04)        0 1px, transparent 1px 3px),
    linear-gradient(180deg, var(--bg-felt-1), var(--bg-felt-2));
}
.board::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at center, transparent 45%, rgba(0, 0, 0, 0.45) 100%),
    linear-gradient(to bottom, rgba(0, 0, 0, 0.25), transparent 12%, transparent 88%, rgba(0, 0, 0, 0.4));
  z-index: 50;
}

.stage {
  position: absolute;
  top: 8px;                                    /* topbar already separates this from the chrome — no extra cushion needed */
  bottom: 60px;                                /* enough room for the hand-peek to clear the row */
  left: 14px; right: 14px;
  transform-style: preserve-3d;
  transform: rotateX(16deg);
  transform-origin: 50% 100%;
  display: grid;
  grid-template-rows: 1fr 1fr;
  gap: 38px;
}

.player-row {
  display: grid;
  gap: 14px;
  min-height: 0;
  transform-style: preserve-3d;
  grid-template-columns: var(--sidebar-w) 1fr var(--sidebar-w);
  align-items: stretch;        /* sidebars fill battlefield height */
}
.player-row.opponent { transform: translateZ(-30px); }
.player-row.you      { transform: translateZ(20px); }

/* Sidebars are full-height; their CONTENTS hug the inner edge. Opp's life
   strip + pile grid both push DOWN inside their full-height container (so the
   life total and piles sit at the bottom of opp's mat, against the center).
   Yours push UP. */
.player-row.opponent > .player-strip { justify-content: flex-end; }
.player-row.you      > .player-strip { justify-content: flex-start; }
.player-row.opponent > .stack-zones  { align-content: end; }
.player-row.you      > .stack-zones  { align-content: start; }

.player-strip {
  display: flex; flex-direction: column;
  gap: 10px;
  padding: 12px;
  background:
    linear-gradient(to bottom, rgba(255, 255, 255, 0.02), rgba(0, 0, 0, 0.18)),
    rgba(0, 0, 0, 0.30);
  border-radius: var(--radius);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    0 4px 12px rgba(0, 0, 0, 0.5);
  justify-content: center;
}
.player-strip .player-name {
  font-weight: 600; font-size: 13px;
  display: flex; justify-content: space-between; align-items: center;
  letter-spacing: 0.3px;
}
.hand-count-label { font-size: 11px; color: var(--text-dim); }

.life-display {
  font-size: 28px; font-weight: 700;
  display: flex; align-items: center; justify-content: space-between;
  background:
    radial-gradient(circle at 50% 30%, rgba(255, 255, 255, 0.06), transparent 70%),
    rgba(0, 0, 0, 0.45);
  padding: 8px 14px; border-radius: 8px;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    inset 0 -1px 0 rgba(0, 0, 0, 0.4);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.life-display .lf-btn {
  font-size: 16px; color: var(--text-dim);
  padding: 2px 8px; border-radius: 4px; cursor: pointer;
  transition: all 0.15s;
}
.life-display .lf-btn:hover { background: var(--accent-soft); color: var(--text); }
.life-display .heart { color: var(--warn); margin-left: 6px; font-size: 18px; }

/* Compact counter rows (poison + energy): half-height variants of
   life-display. flex-shrink:0 so they can't be crushed by the strip's
   other children. The value span has pointer-events:none so click events
   always originate from an lf-btn (matching life-display's click model).
   Poison sits below life on your side and above life on opp side; energy
   sits one step further out (beneath poison / above poison). */
.poison-display, .energy-display {
  flex-shrink: 0;
  font-size: 14px; font-weight: 700;
  display: flex; align-items: center; justify-content: space-between;
  background:
    radial-gradient(circle at 50% 30%, rgba(255, 255, 255, 0.04), transparent 70%),
    rgba(0, 0, 0, 0.40);
  padding: 3px 10px; border-radius: 6px;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    inset 0 -1px 0 rgba(0, 0, 0, 0.4);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.poison-display .pd-val, .energy-display .pd-val { pointer-events: none; display: inline-flex; align-items: center; }
.poison-display .lf-btn, .energy-display .lf-btn {
  font-size: 12px; color: var(--text-dim);
  padding: 1px 6px; border-radius: 4px; cursor: pointer;
  transition: all 0.15s;
}
.poison-display .lf-btn:hover, .energy-display .lf-btn:hover {
  background: var(--accent-soft); color: var(--text);
}
/* Poison: phi (Φ), the actual MTG poison symbol, in green. */
.poison-display .poison { color: var(--good); margin-left: 5px; font-size: 14px; line-height: 1; }
/* Energy: gold pentagon outline with a lightning bolt inside, like MTG's
   energy symbol. Inline SVG so it scales/themes via currentColor. */
.energy-display .energy-icon {
  width: 16px; height: 17px; margin-left: 5px;
  color: var(--gold);
  vertical-align: middle;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.6));
}
.energy-display .energy-icon polygon {
  fill: rgba(0, 0, 0, 0.55);
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
}
.energy-display .energy-icon path { fill: currentColor; }

/* Mana pool: 5 MTG-colored bubbles in a pentagon arrangement plus a
   colorless center. Bubbles are absolutely positioned inside a fixed-
   height pad — translate(-50%, -50%) so the (left, top) values name
   each bubble's CENTER. The opp pool is rotated 180° to mirror the
   across-the-table view; each bubble counter-rotates so the numbers
   stay readable. */
/* Mana pool: 2-column × 3-row grid of [- N pip +] counters, sharing
   the visual style of poison/energy. HTML order determines fill:
     your side:  W U / B R / G C
     opp side:   C G / R B / U W   (reversed both axes for table-mirror)
   On opp side the +/- positions are also swapped per row (HTML order),
   so the player-near button is + just like life/poison/energy. */
.mana-pool {
  flex-shrink: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
.mana-counter {
  font-size: 13px; font-weight: 700;
  display: flex; align-items: center; justify-content: space-between;
  background:
    radial-gradient(circle at 50% 30%, rgba(255, 255, 255, 0.04), transparent 70%),
    rgba(0, 0, 0, 0.40);
  padding: 2px 6px; border-radius: 6px;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    inset 0 -1px 0 rgba(0, 0, 0, 0.4);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.mana-counter .pd-val { pointer-events: none; display: inline-flex; align-items: center; }
.mana-counter .lf-btn {
  font-size: 12px; color: var(--text-dim);
  padding: 1px 4px; border-radius: 4px; cursor: pointer;
  transition: all 0.15s;
}
.mana-counter .lf-btn:hover { background: var(--accent-soft); color: var(--text); }
/* Colored pip — small filled circle indicating which mana color. */
.mana-pip {
  display: inline-block;
  width: 11px; height: 11px;
  margin-left: 4px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 1px rgba(0, 0, 0, 0.4);
}
.mana-pip[data-color="W"] { background: #f0e6c8; }
.mana-pip[data-color="U"] { background: #1f5fa0; }
.mana-pip[data-color="B"] { background: #2a2a2a; }
.mana-pip[data-color="R"] { background: #c63a3a; }
.mana-pip[data-color="G"] { background: #3a8c5a; }
.mana-pip[data-color="C"] { background: #6f6f6f; }

.battlefield {
  flex: 1;
  position: relative;       /* containing block for absolute card positions */
  padding: 12px;
  background:
    linear-gradient(to bottom, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.30)),
    repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.012) 0 1px, transparent 1px 5px);
  border-radius: var(--radius);
  border: 1px solid rgba(74, 125, 209, 0.18);
  overflow: hidden;
  min-height: 0;
  box-shadow:
    inset 0 0 30px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.03);
  transform-style: preserve-3d;
}
/* Battlefield = always 2 rows, vertically centered, with a half-card gap
   between them. cell_x is a half-card horizontal step so adjacent cards
   overlap 50%. cell_y is 0 or 1.
   Layout math: total row block = 2 cards + 0.5-card gap = 2.5 cards.
     Row 0 top = 50% - 1.25*cardH
     Row 1 top = 50% + 0.25*cardH
   → top = 50% + (cell_y * 1.5 - 1.25) * cardH                          */
.battlefield .card {
  position: absolute;
  left: calc(var(--cell-x, 0) * var(--card-w) / 2);
  top:  calc(50% + (var(--cell-y, 0) * 1.5 - 1.25) * var(--card-h));
}
/* Opp's battlefield is rendered across the table from their POV — both
   axes are mirrored. cell_y is flipped so opp's "back row" (cell_y=1,
   closer to opp) sits at the top of opp's battlefield on my screen.
   cell_x is mirrored by anchoring from the RIGHT edge — opp's leftmost
   column (cell_x=0) lands at the right edge of opp's battlefield on my
   screen, matching the across-the-table flip. */
#oppBattlefield .card {
  left: auto;
  right: calc(var(--cell-x, 0) * var(--card-w) / 2);
  top: calc(50% + ((1 - var(--cell-y, 0)) * 1.5 - 1.25) * var(--card-h));
}

.stack-zones {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-content: start;
  gap: 14px;
  padding: 14px;
  background:
    linear-gradient(to bottom, rgba(255, 255, 255, 0.02), rgba(0, 0, 0, 0.20)),
    rgba(0, 0, 0, 0.30);
  border-radius: var(--radius);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    0 4px 12px rgba(0, 0, 0, 0.5);
  transform-style: preserve-3d;
  align-items: start;
  justify-items: center;
}

/* ============ Card piles ============ */

.pile {
  position: relative;
  width: var(--stack-w);
  height: var(--stack-h);
  cursor: pointer;
  transform-style: preserve-3d;
  transition: transform 0.2s;
}
.pile:hover { transform: translateZ(8px); }

.pile .pile-label {
  position: absolute;
  bottom: 4px;
  left: 0; right: 0;
  text-align: center;
  font-size: var(--label-font);
  color: var(--text-dim);
  letter-spacing: 0.6px;
  text-transform: uppercase;
  font-weight: 600;
  /* The pile uses transform-style: preserve-3d, so z-index is largely
     ignored — whichever child has the highest translateZ paints in
     front. JS sets --pile-top-z to match the topmost card's translateZ
     for this pile's current size, and the label sits just 2px in front of
     it. That way the label hugs an empty placeholder at Z=2 and rides up
     to Z=10 on a fully-stacked pile, never floating off into space. */
  z-index: 20;
  transform: translateZ(calc(var(--pile-top-z, 0px) + 2px));
  background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0));
  padding: 8px 2px 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.pile .stack-card {
  position: absolute;
  width: 100%; height: 100%;
  border-radius: 6px;
  background-color: #1a1428;
  background-image: url("https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg");
  background-size: cover;
  background-position: center;
  /* Dark, near-black border so the small offsets behind the top card don't
     read as a purple halo — the MTG back image itself has a dark frame. */
  border: 1px solid rgba(0, 0, 0, 0.6);
  box-shadow: var(--shadow-pile);
  transform-origin: center;
}
.pile .stack-card:nth-child(2) { transform: translateZ(0) translate(-1.5px, -1.5px); }
.pile .stack-card:nth-child(3) { transform: translateZ(2px); }
.pile .stack-card:nth-child(4) { transform: translateZ(4px) translate(1px, 1px); }
.pile .stack-card:nth-child(5) { transform: translateZ(6px) translate(2px, 2px); }
.pile .stack-card:nth-child(6) { transform: translateZ(8px) translate(3px, 3px); }

.pile.empty .stack-card {
  background-image: none;
  background-color: transparent;
  background: transparent;
  border: 1px dashed rgba(255, 255, 255, 0.15);
  box-shadow: none;
}

/* shows-top piles render the actual top card as a face-up .pile-top in
   place of the topmost stack-card (back). JS sets the transform to match
   the offset/Z of the position it occupies, so the stack reads as
   (count-1) face-down cards + 1 face-up card on top, not as a separate
   floating card above the whole pile.
   No accent border / box-shadow — those produced a "blue glow" around the
   commander/exile/graveyard zones. */
.pile .pile-top {
  position: absolute;
  inset: 0;
  border-radius: 6px;
  background-size: cover;
  background-position: center;
  background-color: var(--bg-card-2);
  pointer-events: none;
  z-index: 10;
}

/* While dragging a card off the top of this pile, hide the visible top —
   for plain piles that's the topmost stack-card, for shows-top piles that's
   the pile-top face-up card. Immediate feedback that the card was picked up.
   Restored when the drag ends. */
.pile.pile-source > .stack-card-top,
.pile.pile-source > .pile-top {
  visibility: hidden;
}

/* Shuffle animation — clean 45° rotation around each card's center, then
   back. Odd-indexed back-cards rotate right, even rotate left, so the
   pile reads as cards being mixed, not rocking in unison. The topmost
   card (.stack-card-top on plain piles, .pile-top on shows-top piles)
   intentionally stays put so the pile has a stable visual anchor. */
.pile.shuffling > .stack-card:not(.stack-card-top) {
  animation: shuffle-rotate-right 0.55s ease-in-out;
  transform-origin: 50% 50%;
}
.pile.shuffling > .stack-card:not(.stack-card-top):nth-child(2n) {
  animation-name: shuffle-rotate-left;
}
@keyframes shuffle-rotate-right {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(30deg); }
}
@keyframes shuffle-rotate-left {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(-30deg); }
}

/* Drop-zone highlights were intentionally removed — the user found the
   dashed accent outlines distracting. JS still toggles .drop-active for any
   future re-enable, but no visual effect is attached. */

/* ============ Cards ============ */

.card {
  width: var(--card-w);
  height: var(--card-h);
  border-radius: 7px;
  cursor: grab;
  flex-shrink: 0;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1),
              box-shadow 0.18s,
              filter 0.18s;
}
.card:active { cursor: grabbing; }

/* Card-inner is the rotation surface for tap (rotate Z) and flip (rotate Y).
   Both front and back are children of card-inner — each face is positioned
   absolutely and backface-hidden, so rotating card-inner reveals one or the
   other. */
.card .card-inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform 0.32s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.card.tapped .card-inner { transform: rotate(90deg); }
.card.flipped .card-inner { transform: rotateY(180deg); }
.card.tapped.flipped .card-inner { transform: rotate(90deg) rotateY(180deg); }

.card .card-face {
  position: absolute;
  inset: 0;
  border-radius: 7px;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  overflow: hidden;
  box-shadow: var(--shadow-card),
              inset 0 1px 0 rgba(255, 255, 255, 0.10),
              inset 0 -1px 0 rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.10);
}
.card .card-face.front {
  background:
    radial-gradient(ellipse at 50% 10%, rgba(255, 255, 255, 0.10), transparent 70%),
    linear-gradient(135deg, var(--bg-card), var(--bg-card-2));
  display: flex; flex-direction: column;
  padding: 6px;
  font-size: 11px;
}
.card .card-face.back {
  transform: rotateY(180deg);
  background-image: url("https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg");
  background-size: cover;
  background-position: center;
  /* MTG back image has its own dark frame — use the same neutral border
     the stack-cards now use so face-down cards don't read as purple. */
  border-color: rgba(0, 0, 0, 0.6);
}

.card .card-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: 7px;
  pointer-events: none;
}

.card .card-name {
  font-weight: 600; font-size: 11px; line-height: 1.2;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
  position: relative; z-index: 2;
  background: linear-gradient(to bottom, rgba(0,0,0,0.75), rgba(0,0,0,0));
  padding: 2px 4px; margin: -6px -6px 0; border-radius: 7px 7px 0 0;
}
.card .card-pt {
  font-size: 13px; font-weight: 700;
  align-self: flex-end; margin-top: auto;
  background: rgba(0, 0, 0, 0.75);
  padding: 1px 6px; border-radius: 4px;
  position: relative; z-index: 2;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.card .counter-badge {
  position: absolute;
  bottom: 4px; left: 4px;
  background: linear-gradient(to bottom, #5a8de0, #3a65b8);
  color: white;
  font-size: 10px; font-weight: 700;
  padding: 2px 7px; border-radius: 10px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5),
              inset 0 1px 0 rgba(255, 255, 255, 0.2);
  z-index: 3;
}

/* Card back: only meaningful as a pile/opp-hand visualization. Player cards
   rely on .card-face.back rotation to avoid the right-half-of-flip showing
   a cardback prematurely. */
.opp-hand-row .card.card-back .card-face.front {
  background-image: url("https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg");
  background-size: cover;
  background-position: center;
}

/* Battlefield hover lift. The battlefield has transform-style: preserve-3d,
   so z-index gets ignored in 3D compositing — siblings stack by translateZ.
   Lifting the hovered card via translateZ guarantees it draws in front of
   overlapping neighbors instead of z-fighting at the same depth. */
.battlefield .card {
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1),
              filter 0.18s;
}
.battlefield .card:hover {
  filter: brightness(1.08);
  transform: translateZ(40px) translateY(-4px);
}

/* Dragging-source dim: ONLY applied when the cursor has left the hand area.
   JS toggles .dragging-out-of-hand so an in-hand reorder leaves the source at
   full opacity (it stays in the fan and slides around). */
.card.dragging-out-of-hand { opacity: 0.3; }

/* Set on any of my visible cards while the peer is holding it. The card's
   slot stays (preserving layout) but the card itself goes invisible —
   matches the "I picked it up" affordance the peer sees locally. */
.card.peer-dragging { visibility: hidden; }

/* ============ Hand (bottom, always visible) ============ */

.hand-area {
  position: fixed;
  bottom: 0;
  left: 0; right: 0;
  height: calc(var(--hand-card-h) + 12px);
  display: flex; justify-content: center; align-items: flex-end;
  z-index: 100;
  pointer-events: none;
  perspective: 1200px;
  perspective-origin: 50% 100%;
  transform: translateY(calc(var(--hand-card-h) - var(--hand-peek)));
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1);
  padding-bottom: 8px;
}
.hand-area::before {
  content: '';
  position: absolute; inset: -8px 0 0 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.15) 50%, transparent);
  pointer-events: none;
  z-index: -1;
}
/* Hand area is up ONLY when the cursor is over it. During a drag the cursor
   takes care of itself — if it returns to the hand, :hover re-engages and the
   hand pops back up automatically. */
.hand-area:hover { transform: translateY(0); }
/* Make hand-area always pickable while dragging (empty-hand drop target). */
body.dragging .hand-area { pointer-events: auto; }

.hand-area .hand-row {
  pointer-events: auto;
  display: flex;
  align-items: flex-end;
  transform-style: preserve-3d;
}

/* Hand fan: per-card rotation (--hand-rot) AND a parabolic vertical drop
   (--hand-y), both set by JS. The translateY is what gives the fan an
   actual arc — outer cards sit lower than the center, so the row reads as
   a curve rather than a row of rotated rectangles. */
.hand-row .card {
  width: var(--hand-card-w);
  height: var(--hand-card-h);
  margin-left: var(--hand-margin, calc(-1 * var(--hand-card-w) * 34 / 96));
  transform-origin: 50% 100%;
  transform: translateY(var(--hand-y, 0px)) rotate(var(--hand-rot, 0deg));
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1),
              margin 0.18s, box-shadow 0.18s;
}
.hand-row .card:first-child { margin-left: 0; }

/* While an external drag's cursor is inside the hand area, a "phantom" card
   joins the fan in place of the drag ghost. It renders the actual dragged
   card (or its back if face-down) and gets the same scale(1.2) as an
   in-hand drag source — so the user sees the card as if it had been picked
   up from the hand. */
.hand-row .card[data-phantom] { pointer-events: none; }

/* Hover: scale 20% in place (no upward translate — the user explicitly
   rejected the straight-up lift). */
.hand-row .card:hover {
  transform: translateY(var(--hand-y, 0px)) rotate(var(--hand-rot, 0deg)) scale(1.2);
  z-index: 40;
}
/* During ANY drag, suppress hover effects on every hand card. Otherwise the
   neighbor under the cursor scales up while the dragged card is still in its
   old slot — looks like the dragged card is on top of the neighbor when in
   reality the swap just hasn't happened yet. */
body.dragging .hand-row .card:hover {
  transform: translateY(var(--hand-y, 0px)) rotate(var(--hand-rot, 0deg));
  z-index: auto;
}
/* EXCEPT the dragged card itself, which stays at 20% scale while the user
   is rearranging in the hand. Once the cursor leaves the hand area, the
   ghost takes over and the source fades — no need to keep it scaled then. */
body.dragging .hand-row .card.dragging:not(.dragging-out-of-hand) {
  transform: translateY(var(--hand-y, 0px)) rotate(var(--hand-rot, 0deg)) scale(1.2);
  z-index: 40;
}

/* Opponent hand at top — card-back arc that curves DOWN, same as the
   player's fan. The board's tilt isn't strict top-down, so a "physical
   mirror" reads wrong; the user wants the same visual arc shape on both
   sides. Pivot at top of card since opp's hand hangs from above on
   my screen. */
.opp-hand-area {
  position: fixed;
  top: var(--bar-h);
  left: 0; right: 0;
  display: flex; justify-content: center;
  z-index: 100;
  pointer-events: none;
  perspective: 1200px;
  perspective-origin: 50% 0%;
}
.opp-hand-row {
  display: flex;
  align-items: flex-start;
  transform-style: preserve-3d;
}
.opp-hand-row .card {
  width: var(--opp-card-w);
  height: var(--opp-card-h);
  margin-left: var(--opp-hand-margin, calc(-1 * var(--opp-card-w) * 24 / 60));
  transform-origin: 50% 0%;
  transform: translateY(var(--hand-y, 0px)) rotate(var(--hand-rot, 0deg));
  cursor: default;
  /* Re-enable pointer events (parent .opp-hand-area has pointer-events:
     none so its background doesn't intercept clicks). Children can opt
     back in — needed so hovering opp's hand cards triggers the zoom
     preview's mouseover. */
  pointer-events: auto;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1), margin 0.18s;
}
.opp-hand-row .card:first-child { margin-left: 0; }

/* When the peer broadcasts that they're hovering one of their own hand
   cards, that card-back grows 1.2× with the BOTTOM anchored — same as
   the player's fan hover (which scales upward from the bottom pivot).
   For opp the base transform-origin is 50% 0% (top), so scale(1.2)
   would grow downward; the extra translateY(-0.2 * opp-card-h) shifts
   the scaled card back up so its bottom stays where it was. Net effect:
   the card scales upward, mirroring the player. */
.opp-hand-row .card.opp-hovered {
  transform: translateY(calc(var(--hand-y, 0px) - var(--opp-card-h) * 0.2))
             rotate(var(--hand-rot, 0deg))
             scale(1.2);
  z-index: 10;
}

/* ============ Drag ghost ============ */

.drag-ghost {
  position: fixed;
  top: 0; left: 0;
  width: var(--card-w);
  height: var(--card-h);
  pointer-events: none;
  z-index: 600;
  opacity: 0;
  border-radius: 7px;
  background:
    radial-gradient(ellipse at 50% 10%, rgba(255, 255, 255, 0.10), transparent 70%),
    linear-gradient(135deg, var(--bg-card), var(--bg-card-2));
  overflow: hidden;
}
.drag-ghost.visible { opacity: 1; }
.drag-ghost img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Dragging a face-down card (e.g. off the library) shows the back, not the
   front. JS toggles .show-back; CSS swaps the background and hides any img. */
.drag-ghost.show-back {
  background-image: url("https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg");
  background-size: cover;
  background-position: center;
}
.drag-ghost.show-back img { display: none; }

/* ============ Peer cursors ============ */

.peer-cursors {
  /* Fixed-position overlay covering the viewport. Cursors are placed at the
     correctly-projected point using inverse/forward projection through the
     stage's rotateX + board's perspective. */
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 550;
}
.peer-cursor {
  position: absolute;
  top: 0; left: 0;
  width: 0; height: 0;
  transition: transform 0.04s linear;
}
.peer-cursor-arrow {
  position: absolute;
  top: 0; left: 0;
  width: 0; height: 0;
  border-left: 16px solid var(--accent);
  border-top: 16px solid var(--accent);
  border-right: 16px solid transparent;
  border-bottom: 16px solid transparent;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,0.8));
}
.peer-cursor .peer-label {
  position: absolute;
  top: 20px; left: 16px;
  background: var(--accent);
  color: white;
  font-size: 11px; font-weight: 600;
  padding: 3px 9px;
  border-radius: 0 6px 6px 6px;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0,0,0,0.6);
}

/* Card ghost rendered ON opp's cursor while they're dragging. Centered
   via translate(-50%, -50%) so it follows the cursor point, not a corner.
   Default background is the MTG back; JS overrides via inline style when
   the dragged card is visible to me. No accent border / shadow-lift —
   matches the same "no tacky glow" pass on the local drag ghost. */
.peer-drag-ghost {
  position: absolute;
  top: 0; left: 0;
  width: var(--card-w);
  height: var(--card-h);
  border-radius: 7px;
  background-image: url("https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg");
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 0.1s;
  pointer-events: none;
  transform: translate(-50%, -50%);
}
.peer-drag-ghost.visible { opacity: 0.9; }

/* ============ Card preview ============ */

.card-preview {
  position: fixed;
  top: calc(var(--bar-h) + 14px);
  right: 14px;
  width: 220px;
  height: calc(220px * 132 / 96);   /* MTG card aspect */
  z-index: 700;
  border-radius: 10px;
  overflow: hidden;
  background: linear-gradient(135deg, var(--bg-card), var(--bg-card-2));
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.7), 0 0 0 1px var(--line);
  display: none;
}
.card-preview.visible { display: block; }
.card-preview img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  user-select: none;
  -webkit-user-drag: none;
}
.card-preview .popout-btn {
  position: absolute;
  bottom: 6px; right: 6px;
  width: 26px; height: 26px;
  background: rgba(0, 0, 0, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--text);
  font-size: 13px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
  z-index: 2;
}
.card-preview .popout-btn:hover { background: var(--accent); border-color: var(--accent); }

/* Bottom-left drag handle. Stripe pattern signals "draggable corner";
   JS resizes the panel proportionally while preserving the card aspect. */
.card-preview .resize-handle {
  position: absolute;
  bottom: 0; left: 0;
  width: 16px; height: 16px;
  cursor: nesw-resize;
  z-index: 2;
  background:
    linear-gradient(45deg, transparent 35%, rgba(255, 255, 255, 0.5) 35%, rgba(255, 255, 255, 0.5) 45%, transparent 45%,
                          transparent 55%, rgba(255, 255, 255, 0.5) 55%, rgba(255, 255, 255, 0.5) 65%, transparent 65%);
}
.card-preview .resize-handle:hover {
  background:
    linear-gradient(45deg, transparent 35%, var(--accent) 35%, var(--accent) 45%, transparent 45%,
                          transparent 55%, var(--accent) 55%, var(--accent) 65%, transparent 65%);
}

/* ============ Context menu ============ */

.ctx-menu {
  position: fixed;
  background: linear-gradient(to bottom, #1c2236, #11151f);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow:
    0 20px 40px rgba(0, 0, 0, 0.7),
    0 4px 10px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  min-width: 200px;
  padding: 5px;
  z-index: 500;
  font-size: 13px;
  display: none;
}
.ctx-menu.visible { display: block; }
.ctx-menu .item {
  padding: 8px 14px;
  cursor: pointer;
  border-radius: 5px;
  display: flex; justify-content: space-between; align-items: center;
  transition: all 0.1s;
}
.ctx-menu .item:hover {
  background: linear-gradient(to right, var(--accent-soft), transparent);
  color: var(--accent);
}
.ctx-menu .sep {
  height: 1px;
  background: linear-gradient(to right, transparent, var(--line), transparent);
  margin: 5px 0;
}
.ctx-menu .item .hk {
  font-size: 11px; color: var(--text-dim);
  font-family: ui-monospace, monospace;
  background: rgba(0, 0, 0, 0.3);
  padding: 1px 5px; border-radius: 3px;
}
/* Group for right-side hints so multiple badges (scroll + hotkey) sit
   together while justify-content: space-between still aligns them with
   the label. */
.ctx-menu .item .hk-group {
  display: flex;
  gap: 6px;
  align-items: center;
}
.ctx-menu .item .scroll-hint {
  font-size: 11px; color: var(--text-dim);
  font-family: ui-monospace, monospace;
  background: rgba(0, 0, 0, 0.3);
  padding: 1px 5px; border-radius: 3px;
  line-height: 1;
}

/* ============ Modal ============ */

.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.75);
  backdrop-filter: blur(6px);
  z-index: 400;
  display: none;
  align-items: center; justify-content: center;
}
.modal-backdrop.open { display: flex; }
.modal {
  background: linear-gradient(to bottom, #181f2f, #0e131e);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 24px;
  width: 90%; max-width: 520px;
  max-height: 80vh; overflow: auto;
  box-shadow:
    0 30px 60px rgba(0, 0, 0, 0.6),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.modal h2 { margin: 0 0 12px; font-size: 18px; color: var(--gold); letter-spacing: 0.5px; }
.modal p { color: var(--text-dim); font-size: 13px; }
.modal .row { display: flex; gap: 8px; margin-top: 12px; }
.modal textarea {
  width: 100%; min-height: 220px;
  background: rgba(0, 0, 0, 0.4); color: var(--text);
  border: 1px solid var(--line); border-radius: 6px;
  padding: 10px; font: 13px/1.4 ui-monospace, monospace;
  resize: vertical;
}
.deck-status { font-size: 12px; color: var(--text-dim); }
.deck-status.error { color: var(--warn); }
.deck-status.success { color: var(--good); }
