/* Neon Arena — arcomage view: the original's battlefield. Left to right:
   my three resource tiles, my tower and wall, the sky the played card crosses,
   the foe's wall and tower, the foe's three tiles — and my hand along the
   bottom. Loaded after game.css; reuses its board-frame/fighter/splash
   primitives.

   This is a port of the layout at https://arcomage.github.io/ (ArcoMage HD,
   MIT — the same remaster bot/arcade_arcomage.py ports its rules from), read
   as GEOMETRY and redrawn in our palette: their 3D-rendered towers and painted
   card faces become our stone gradients and our own card art. What is kept is
   the arrangement, because in arcomage the arrangement IS the game — you read
   the match by looking at which tower is taller across the field, and no
   summary bar answers that.

   It needs about 700px of board column and the scene gives 468 at a 1280px
   window, which is where the folded two-panel version came from. The width is
   bought by `hoistSideColumn()` (ui/board.js), which moves the in-scene move
   log and chat into the console's own `#ctx` column: measured 468 -> 826 at
   1280 and 670 -> 1028 at 1728. */

/* The match is one screenful. `.view-game` (game.css) is the frame — a
   `minmax(0,1fr) 340px` grid whose height is the SCENE's, written by
   `fitMatchColumn()` into `--match-h`, with the opponent bar pinned to the top
   of the board column, the board taking what is left and the player's own bar
   and the action row at the bottom. This file used to override that with a
   `display:flex` root and an inner `.tc-main` at `align-items:start`, which
   is how the column came out 1099px tall inside a 660px scene at 1280×800 —
   the player's own clock clipped by the fold and the buttons below it. */

.view-tc {
  overflow: visible;
  --tc-cw: 72px;
  --tc-ch: 100px;
  --tc-brick: var(--gold);
  --tc-gem: var(--cyan);
  --tc-recruit: var(--ok);
  /* The rail is the original's resource column: three tiles down the outer
     edge of each side. 104px is the narrowest that still fits a two-digit
     stock beside its name at 10px. */
  --tc-rail-w: 104px;
  /* The height of the hand band, and therefore the size of a card. The
     original spends the bottom THIRD of the screen on the hand; the scene
     rations our height through `--board-cap`, so the band is stated per size
     class against `@container scene` and the card is derived from it. */
  --tc-hand-h: 118px;
  /* Whose tower is whose, at a glance and from across the room. The original
     gives the two players a red and a blue roof; ours are the arena's own
     accent pair, and the SELF side always takes the cool one so the answer to
     "which one is mine" never depends on the seat. */
  --tc-mine: var(--cool);
  --tc-theirs: var(--danger);
  --tc-stone-1: #6e7684;
  --tc-stone-2: #363d49;
}

/* === battlefield === */
/* `auto`, not `hidden`: at the compact size class two towers side by side and
   a hand of six do not fit any height the scene can offer, and a battlefield
   you can scroll beats one whose bottom row is simply gone.
   `flex-start` is the other half of that sentence and it was missing. This box
   IS a `.board-frame`, which centres its child, and a centred flex item that
   is TALLER than its scroller overflows both ends — but only the bottom end is
   reachable, because scrollTop cannot go negative. Measured live at 1280x800
   mid-game: field 366.11 in a 348 stage, 9.08px above and 9.09px below, scroll
   range [0, 9] — the opponent's hand row was clipped and no amount of
   scrolling brought it back. With the shell's offline banner up it was 47px.
   `.tc-field` is `min-height:100%`, so it never sits shorter than this box and
   top-aligning is a no-op whenever the battlefield already fits. */
.tc-stage { padding: 0; overflow: auto; align-items: flex-start; }
/* The original's single line, restored. Four columns — my rail, my keep, the
   foe's keep, the foe's rail — with the sky spanning the two keeps above them
   and the hand spanning everything below. The keeps get `1fr` each and the
   rails a fixed width, so the empty middle of the field grows with the window
   exactly as it does in the original.
   `min-height: 100%` and not `height`: an `auto` row in a definite-height grid
   gets SQUEEZED when the rows do not fit, and the hand came out 22px tall with
   its cards spilling up over the towers. Growing past the stage and scrolling
   is the honest failure mode — `.tc-stage` scrolls from its top edge. */
.tc-field {
  position: relative;
  display: grid;
  grid-template-columns: var(--tc-rail-w) minmax(0, 1fr) minmax(0, 1fr) var(--tc-rail-w);
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas:
    "meres mekeep foekeep foeres"
    "hand  hand   hand    hand";
  gap: 4px 8px;
  width: 100%;
  min-height: 100%;
  padding: 6px 8px 8px;
  border: 1.5px solid var(--stroke);
  border-radius: var(--r-md);
  background:
    radial-gradient(120% 70% at 50% 0%, rgba(241, 193, 82, 0.07), transparent 55%),
    radial-gradient(110% 70% at 50% 100%, rgba(53, 199, 131, 0.06), transparent 55%),
    linear-gradient(160deg, rgba(17, 24, 33, 0.72), rgba(11, 15, 20, 0.85));
  user-select: none;
  -webkit-user-select: none;
}
/* The original's landscape sits behind the whole duel rather than under a
   panel, so it is allowed to be seen — 0.14 was tuned for a background nobody
   looked at. */
.tc-field::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: url("../../assets/arcomage/board_background.webp") center / cover no-repeat;
  opacity: 0.45;
  pointer-events: none;
}
.tc-field > * { position: relative; }
/* Each side hands the grid two pieces. `display: contents` is what lets one
   painter own a seat while its rail and its keep land in areas at opposite
   ends of the field. */
.tc-side-wrap { display: contents; }
.tc-me-wrap > .tc-rail { grid-area: meres; }
.tc-me-wrap > .tc-keep { grid-area: mekeep; }
.tc-foe-wrap > .tc-rail { grid-area: foeres; }
.tc-foe-wrap > .tc-keep { grid-area: foekeep; }
.tc-my-hand { grid-area: hand; min-height: 0; }
.tc-sel-info { grid-area: hand; align-self: start; }
/* The ground both keeps stand on — the original's waterline. Decoration only,
   and it must never eat a click meant for a card. */
.tc-ground {
  grid-row: 1;
  grid-column: 1 / -1;
  align-self: end;
  height: 30%;
  min-height: 22px;
  border-radius: 0 0 var(--r-md) var(--r-md);
  background: linear-gradient(180deg, transparent, rgba(53, 199, 131, 0.1) 45%, rgba(8, 16, 14, 0.55));
  box-shadow: inset 0 1px 0 rgba(124, 201, 255, 0.14);
  pointer-events: none;
  z-index: 0;
}
.tc-field.tc-draw-glow {
  border-color: transparent;
  background:
    radial-gradient(120% 70% at 50% 0%, rgba(241, 193, 82, 0.07), transparent 55%) padding-box,
    linear-gradient(160deg, rgba(17, 24, 33, 0.9), rgba(11, 15, 20, 0.94)) padding-box,
    var(--draw-rainbow) border-box;
  background-size: 100% 100%, 100% 100%, 240% 100%;
  animation: drawRainbowBorder 2.8s linear infinite;
  box-shadow: 0 0 26px rgba(124, 201, 255, 0.22);
}
/* Turn cue: the mover's half of the field breathes in their colour. Sits
   between the landscape (::before) and the z-1 rails/keeps. */
.tc-turn-glow {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 44%;
  pointer-events: none;
  z-index: 0;
  animation: tcTurnSweep 2.6s ease-in-out infinite;
}
.tc-turn-glow-me {
  left: 0;
  border-radius: var(--r-md) 0 0 var(--r-md);
  background: linear-gradient(90deg, rgba(124, 201, 255, 0.22), rgba(124, 201, 255, 0.07) 40%, transparent 76%);
}
.tc-turn-glow-foe {
  right: 0;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: linear-gradient(270deg, rgba(255, 93, 108, 0.22), rgba(255, 93, 108, 0.07) 40%, transparent 76%);
}
@keyframes tcTurnSweep {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

/* === the resource rail === */
/* The original's tile: a big art square with the PRODUCER in its top corner,
   and a coloured strip under it carrying the STOCK and the resource's name.
   Both numbers are on screen for both players at once, which is the whole
   point of the flanking columns — you play arcomage off the foe's stocks. */
.tc-rail {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
  min-height: 0;
  z-index: 1;
}
.tc-tile {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1.5px solid var(--stroke);
  border-radius: var(--r-sm);
  background: linear-gradient(160deg, rgba(19, 27, 38, 0.88), rgba(10, 15, 21, 0.94));
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.tc-tile-art {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tc-tile-glyph {
  font-size: 24px;
  line-height: 1;
  opacity: 0.9;
}
.tc-tile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 2px;
}
.tc-tile-prod {
  position: absolute;
  left: 4px;
  top: 2px;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 800;
  line-height: 1;
  color: var(--gold);
  text-shadow: 0 1px 3px #000, 0 0 10px rgba(241, 193, 82, 0.5);
  font-variant-numeric: tabular-nums;
}
.tc-tile-foot {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  gap: 5px;
  padding: 2px 6px 4px;
}
.tc-tile-stock {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 800;
  line-height: 1.1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.tc-tile-name {
  margin-left: auto;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tc-tile-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2.5px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}
.tc-tile-fill { display: block; height: 100%; transition: width var(--dur-med) ease; }
.tc-tile-bricks .tc-tile-foot { background: rgba(241, 193, 82, 0.14); }
.tc-tile-gems .tc-tile-foot { background: rgba(103, 232, 249, 0.14); }
.tc-tile-recruits .tc-tile-foot { background: rgba(53, 199, 131, 0.14); }
.tc-tile-bricks .tc-tile-fill { background: var(--tc-brick); }
.tc-tile-gems .tc-tile-fill { background: var(--tc-gem); }
.tc-tile-recruits .tc-tile-fill { background: var(--tc-recruit); }
.tc-tile-bricks .tc-tile-glyph { filter: drop-shadow(0 0 7px rgba(241, 193, 82, 0.5)); }
.tc-tile-gems .tc-tile-glyph { filter: drop-shadow(0 0 7px rgba(103, 232, 249, 0.5)); }
.tc-tile-recruits .tc-tile-glyph { filter: drop-shadow(0 0 7px rgba(53, 199, 131, 0.5)); }
.tc-rail.tc-self .tc-tile { border-color: rgba(124, 201, 255, 0.34); }

/* === the keep: name, tower, wall === */
.tc-keep {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  z-index: 1;
}
/* The original's nameplate: a thin framed bar at the top outer corner of each
   side, so «who is on which side» is answered before the towers are read. */
.tc-keep-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  width: fit-content;
  max-width: 100%;
  padding: 2px 9px;
  border: 1px solid rgba(241, 193, 82, 0.3);
  border-radius: 3px;
  background: rgba(6, 9, 14, 0.72);
  transition: border-color 220ms ease, box-shadow 300ms ease, background 220ms ease;
}
/* Turn cue: the mover's nameplate lights up in their side's colour. The view
   replays the class flip across its DOM rebuild so these transitions run. */
.tc-keep.tc-turn > .tc-keep-head { border-color: var(--gold); }
.tc-me-wrap .tc-keep.tc-turn > .tc-keep-head {
  background: rgba(20, 30, 44, 0.86);
  box-shadow: 0 0 20px rgba(124, 201, 255, 0.4), inset 0 0 14px rgba(124, 201, 255, 0.14);
}
.tc-foe-wrap .tc-keep.tc-turn > .tc-keep-head {
  background: rgba(34, 12, 18, 0.86);
  box-shadow: 0 0 20px rgba(255, 93, 108, 0.36), inset 0 0 14px rgba(255, 93, 108, 0.12);
}
.tc-foe-wrap > .tc-keep > .tc-keep-head { margin-left: auto; }
.tc-me-wrap > .tc-keep > .tc-keep-head { justify-content: flex-start; }
.tc-foe-wrap > .tc-keep > .tc-keep-head { justify-content: flex-end; }
.tc-keep-cards {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
/* The yard hugs the INNER edge of its rail and leaves the middle of the field
   empty, which is the original's proportion: at 1280 its towers stand at 19%
   and 81% of the window with 570px of sky between them. `row-reverse` is the
   mirror — the two WALLS face each other across the gap and the towers stay
   outboard, so damage always travels inward. */
.tc-yard {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  /* The floor is set by the number PLATES, which are wider than the graphics
     they stand under: at 6px «10» and «28 /50» touched under a 274px keep. */
  gap: clamp(14px, 4%, 30px);
  padding: 0 4px;
}
.tc-foe-wrap .tc-yard { flex-direction: row-reverse; }
.tc-tower-slot,
.tc-wall-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  min-height: 0;
}
.tc-tower-slot { width: clamp(44px, 22%, 80px); }
.tc-wall-slot { width: clamp(30px, 14%, 52px); }
/* Tower and wall are drawn to scale against the 50 that wins the game, so the
   match reads as a silhouette before it reads as a number — which is what the
   original is for. The height is written by the view (`towerPct`/`wallPct`)
   because only it knows the goal the server sent. */
/* Tower and wall use the original's model: the body is rendered at FULL
   height (the winning value) and slid downward via translateY so only the
   top portion shows.  The cap (battlements) sits at the top of the body
   and rises/falls with it.  The 0.62s overshoot curve is the design mock's
   motion pass: growth lands with a small bounce instead of a linear crawl.
   The repeating body texture tiles vertically so any height looks correct. */
.tc-tower {
  position: relative;
  width: 100%;
  flex: 1 1 auto;
  min-height: 26px;
  overflow: hidden;
}
.tc-tower-body {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100%;
  background-image: url("../../assets/arcomage/tower_body.webp");
  background-repeat: repeat-y;
  background-size: 100% auto;
  background-position: center top;
  transform: translateY(calc((1 - var(--tc-n, 0) / var(--tc-goal, 50)) * 100%)) translateZ(0);
  transition: transform 0.62s cubic-bezier(0.22, 1.28, 0.42, 1);
  will-change: transform;
  filter: drop-shadow(0 0 16px rgba(255, 93, 108, 0.55));
}
/* Left keep cool, right keep danger — by SIDE, not by role, so a spectator
   reads the same picture as the players (the self side is always drawn left). */
.tc-me-wrap .tc-tower-body {
  filter: drop-shadow(0 0 16px rgba(124, 201, 255, 0.55));
}
/* The flat stone strip reads as a wall of light, not of stone. Two overlays
   ride INSIDE the sliding body so they move with it: a cylindrical shade
   (dark edges, lit centre) and a 3px lip of skylight along the top course. */
.tc-tower-body::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(3, 6, 12, 0.66) 0%, rgba(3, 6, 12, 0.06) 30%, rgba(255, 255, 255, 0.08) 50%, rgba(3, 6, 12, 0.12) 70%, rgba(3, 6, 12, 0.7) 100%);
  pointer-events: none;
}
.tc-tower-body::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(180deg, rgba(255, 214, 219, 0.8), rgba(255, 255, 255, 0));
  pointer-events: none;
}
.tc-me-wrap .tc-tower-body::after {
  background: linear-gradient(180deg, rgba(214, 238, 255, 0.8), rgba(255, 255, 255, 0));
}
/* The battlements, alpha-cut (assets *_a.png): the old webp caps carried a
   black plate that killed the glow. The PNG dims are baked into the
   aspect-ratio — cool 200x132, danger 200x102 — so the art is never squashed. */
.tc-tower-cap {
  position: absolute;
  top: 0;
  left: -14%;
  width: 128%;
  aspect-ratio: 200 / 132;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: center bottom;
  transform: translateY(-86%);
  pointer-events: none;
  z-index: 1;
}
.tc-me-wrap .tc-tower-cap {
  background-image: url("../../assets/arcomage/tower_cap_cool_a.png");
  filter: drop-shadow(0 0 14px rgba(124, 201, 255, 0.8)) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.7));
}
.tc-foe-wrap .tc-tower-cap {
  background-image: url("../../assets/arcomage/tower_cap_danger_a.png");
  aspect-ratio: 200 / 102;
  filter: drop-shadow(0 0 14px rgba(255, 93, 108, 0.8)) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.7));
}
.tc-wall {
  position: relative;
  width: 100%;
  flex: 1 1 auto;
  min-height: 12px;
  overflow: hidden;
  border-radius: 2px 2px 0 0;
}
.tc-wall-body {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100%;
  background-image: url("../../assets/arcomage/wall_body.webp");
  background-repeat: repeat-y;
  background-size: 100% auto;
  background-position: center top;
  transform: translateY(calc((1 - var(--tc-n, 0) / var(--tc-goal, 50)) * 100%)) translateZ(0);
  transition: transform 0.62s cubic-bezier(0.22, 1.28, 0.42, 1);
  will-change: transform;
  filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.5));
}
.tc-wall-body::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(3, 6, 12, 0.6) 0%, rgba(3, 6, 12, 0.04) 32%, rgba(255, 255, 255, 0.07) 50%, rgba(3, 6, 12, 0.1) 68%, rgba(3, 6, 12, 0.64) 100%);
  pointer-events: none;
}
.tc-wall-body::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(180deg, rgba(226, 232, 244, 0.7), rgba(226, 232, 244, 0));
  pointer-events: none;
}
.tc-wall-cap {
  position: absolute;
  top: 0;
  left: -10%;
  width: 120%;
  aspect-ratio: 160 / 66;
  background-image: url("../../assets/arcomage/wall_cap_a.png");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: center bottom;
  transform: translateY(-84%);
  pointer-events: none;
  z-index: 1;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.75));
}
/* The original's number plates: gold on black under the thing they count. */
.tc-plate {
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 2px;
  margin-top: 3px;
  padding: 1px 8px;
  border: 1px solid rgba(241, 193, 82, 0.5);
  border-radius: 3px;
  background: rgba(6, 9, 14, 0.86);
  font-variant-numeric: tabular-nums;
}
.tc-plate-wall { border-color: rgba(255, 255, 255, 0.26); }
.tc-keep.tc-turn .tc-plate-tower {
  border-color: var(--gold);
  box-shadow: 0 0 14px rgba(241, 193, 82, 0.45);
}
.tc-side-head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.tc-side-name {
  font-family: var(--font-display);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 220ms ease, text-shadow 300ms ease;
}
.tc-me-wrap .tc-keep.tc-turn .tc-side-name { color: #bfe4ff; text-shadow: 0 0 12px rgba(124, 201, 255, 0.6); }
.tc-foe-wrap .tc-keep.tc-turn .tc-side-name { color: #ffc3c9; text-shadow: 0 0 12px rgba(255, 93, 108, 0.55); }
.tc-side-name.is-long { font-size: 11px; }
.tc-side-name.is-xlong { font-size: 10px; }
.tc-side-dot {
  color: var(--gold);
  font-size: 9px;
  animation: tcDotPulse 1.1s ease-in-out infinite;
}
@keyframes tcDotPulse {
  0%, 100% { opacity: 1; text-shadow: 0 0 8px rgba(241, 193, 82, 0.8); }
  50% { opacity: 0.35; text-shadow: none; }
}

.tc-tower-val {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 800;
  line-height: 1.2;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}
.tc-plate-goal { font-size: 10px; font-weight: 700; color: var(--text-faint); }
.tc-wall-val {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 800;
  line-height: 1.2;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* value delta flashes */
@keyframes tcFlashUp {
  0% { color: var(--ok); text-shadow: 0 0 14px rgba(53, 199, 131, 0.8); transform: scale(1.18); }
  100% { transform: scale(1); }
}
@keyframes tcFlashDown {
  0% { color: var(--danger); text-shadow: 0 0 14px rgba(255, 93, 108, 0.8); transform: scale(0.88); }
  100% { transform: scale(1); }
}
.tc-tower-val.tc-up, .tc-wall-val.tc-up, .tc-tile-stock.tc-up, .tc-tile-prod.tc-up { animation: tcFlashUp 0.7s var(--spring) both; }
.tc-tower-val.tc-down, .tc-wall-val.tc-down, .tc-tile-stock.tc-down, .tc-tile-prod.tc-down { animation: tcFlashDown 0.7s var(--spring) both; }

/* === the sky: the card that crossed the field, and the two piles === */
/* The original throws the played card into the air above the towers and shows
   no piles at all. The card is reproduced; the piles are ours — our players
   ask how much deck is left — and they are pushed into the far corners so they
   never crowd it. */
/* An OVERLAY, not a grid row, and that is the difference between a tower that
   grows and one that shrinks whenever a card is played: as a row it took its
   height out of the keeps, so casting a card made both towers shorter. It
   spans the sky BETWEEN the rails, which is the part of the field the duel
   leaves empty — the towers stand at the outer edges of their keeps. */
.tc-mid {
  position: absolute;
  left: calc(var(--tc-rail-w) + 14px);
  right: calc(var(--tc-rail-w) + 14px);
  top: 26px;
  /* Stops at the hand band: the sky is the FIELD row, and an overlay pinned to
     the bottom of the grid put the two piles behind the player's cards. */
  bottom: calc(var(--tc-hand-h) + 16px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  pointer-events: none;
  z-index: 2;
}
.tc-piles {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
}
/* The piles are the two interactive spots in a pointer-events:none sky:
   hovering (or tapping) the deck explains the deck, hovering the discard
   opens the played-cards trail. */
.tc-pile-wrap {
  position: relative;
  flex: 0 0 auto;
  pointer-events: auto;
}
.tc-pile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  flex: 0 0 auto;
  padding: 4px 9px;
  border: 1px dashed rgba(255, 255, 255, 0.18);
  border-radius: var(--r-md);
  background: rgba(8, 12, 18, 0.6);
  cursor: help;
  transition: border-color 200ms ease, box-shadow 200ms ease, opacity 200ms ease;
}
.tc-pile-deck { border-color: rgba(103, 232, 249, 0.32); }
.tc-pile-discard { border-color: rgba(255, 255, 255, 0.16); opacity: 0.82; }
.tc-pile-wrap.is-open .tc-pile-deck {
  border-color: rgba(103, 232, 249, 0.75);
  box-shadow: 0 0 18px rgba(103, 232, 249, 0.3);
}
.tc-pile-wrap.is-open .tc-pile-discard {
  border-color: rgba(241, 193, 82, 0.55);
  box-shadow: 0 0 18px rgba(241, 193, 82, 0.3);
  opacity: 1;
}
.tc-pile-ico { font-size: 14px; opacity: 0.85; }
.tc-pile-count {
  font-size: 11.5px;
  font-weight: 800;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
/* Shared popover shell above either pile. */
.tc-pop {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(-50%);
  padding-bottom: 10px;
  z-index: 30;
}
.tc-pop-frame {
  display: flex;
  flex-direction: column;
  padding: 11px 12px;
  border-radius: var(--r-md);
  background: rgba(8, 11, 18, 0.95);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 20px 55px rgba(0, 0, 0, 0.66);
}
.tc-pop-kicker {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(236, 231, 250, 0.5);
}
/* Deck tooltip: how much deck is left, where the rest of the cards are, and
   the reshuffle rule in one sentence. */
.tc-tip-frame { width: 234px; gap: 7px; border: 1px solid rgba(103, 232, 249, 0.32); }
.tc-tip-head { display: flex; align-items: center; gap: 8px; }
.tc-tip-head-ico { font-size: 15px; }
.tc-tip-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 19px;
  font-weight: 700;
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
}
.tc-tip-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 700;
  color: rgba(236, 231, 250, 0.5);
}
.tc-tip-line-sep { opacity: 0.5; }
.tc-tip-bar {
  height: 3px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}
.tc-tip-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--cyan), var(--cool));
  transition: width var(--dur-med) ease;
}
.tc-tip-note {
  font-size: 11px;
  font-weight: 600;
  line-height: 1.4;
  color: rgba(236, 231, 250, 0.6);
}
/* Played-cards trail: the last ten casts of each seat as thumbnails, faded
   when they were discards, with a peek card for the one under the pointer. */
.tc-hist-frame { width: 360px; gap: 8px; border: 1px solid rgba(241, 193, 82, 0.32); }
.tc-hist-head { display: flex; align-items: baseline; gap: 8px; }
.tc-hist-hint { font-size: 10px; font-weight: 700; color: var(--text-faint); }
.tc-hist-row { display: flex; align-items: center; gap: 7px; }
.tc-hist-label {
  flex: 0 0 66px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tc-hist-row-me .tc-hist-label { color: rgb(124, 201, 255); }
.tc-hist-row-foe .tc-hist-label { color: rgb(255, 93, 108); }
.tc-hist-thumbs {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 3px;
  min-width: 0;
  overflow: hidden;
}
.tc-hist-thumb {
  flex: 0 0 auto;
  width: 26px;
  height: 36px;
  border-radius: 4px;
  background: center / cover no-repeat;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
  cursor: default;
  transition: transform 160ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 160ms ease;
  border: 1px solid rgba(124, 201, 255, 0.55);
}
.tc-hist-row-foe .tc-hist-thumb { border-color: rgba(255, 93, 108, 0.55); }
.tc-hist-thumb.is-discard { filter: grayscale(0.85) brightness(0.7); }
.tc-hist-thumb.is-peek {
  border-color: rgba(241, 193, 82, 0.95);
  transform: translateY(-3px) scale(1.12);
  filter: none;
}
.tc-hist-empty { font-size: 10.5px; font-style: italic; color: var(--text-faint); }
/* The peek card, to the left of the trail so it never covers the thumbs. */
.tc-peek {
  position: absolute;
  right: calc(100% + 10px);
  bottom: 10px;
  width: 156px;
  height: 178px;
  display: flex;
  flex-direction: column;
  border-radius: var(--r-sm);
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: linear-gradient(150deg, #1c2431 0%, #121924 55%, #0c1119 100%);
  box-shadow: 0 20px 46px rgba(0, 0, 0, 0.72);
  overflow: hidden;
}
.tc-peek-name {
  flex: 0 0 auto;
  padding: 3px 6px;
  font-size: 12px;
  font-weight: 800;
  line-height: 1.15;
  text-align: center;
  border-bottom: 1px solid rgba(0, 0, 0, 0.45);
}
.tc-peek.tc-res-bricks .tc-peek-name { background: rgba(241, 193, 82, 0.26); }
.tc-peek.tc-res-gems .tc-peek-name { background: rgba(103, 232, 249, 0.26); }
.tc-peek.tc-res-recruits .tc-peek-name { background: rgba(53, 199, 131, 0.26); }
.tc-peek-art {
  flex: 1 1 auto;
  min-height: 56px;
  background: center / cover no-repeat;
}
.tc-peek.is-discard .tc-peek-art { filter: grayscale(0.6) brightness(0.85); }
.tc-peek-desc {
  flex: 0 1 auto;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  max-height: 40px;
  padding: 4px 7px 2px;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.22;
  text-align: center;
  color: rgba(236, 231, 250, 0.72);
  overflow: hidden;
}
.tc-peek-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 6px 4px;
}
.tc-peek-by {
  margin-right: auto;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tc-peek-by-me { color: var(--cool); }
.tc-peek-by-foe { color: #ff8a94; }
.tc-peek-cost {
  min-width: 18px;
  padding: 1px 5px;
  border-radius: var(--r-pill);
  font-size: 13px;
  font-weight: 800;
  text-align: center;
  color: #0b0f14;
  font-variant-numeric: tabular-nums;
}
.tc-peek.tc-res-bricks .tc-peek-cost { background: var(--tc-brick); }
.tc-peek.tc-res-gems .tc-peek-cost { background: var(--tc-gem); }
.tc-peek.tc-res-recruits .tc-peek-cost { background: var(--tc-recruit); }
.tc-peek-res { font-size: 13px; }
.tc-cast {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
.tc-cast-panel {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 0;
  max-width: 320px;
  border-radius: var(--r-md);
  border: 1.5px solid rgba(241, 193, 82, 0.4);
  background: rgba(10, 14, 20, 0.82);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.6), 0 0 28px rgba(241, 193, 82, 0.2);
  overflow: hidden;
  /* Origin-aware: the panel slides in FROM the side that cast it. */
  animation: tcCastL 380ms cubic-bezier(0.2, 0.9, 0.3, 1.25) both;
  pointer-events: auto;
}
.tc-cast-panel.tc-cast-from-foe { animation-name: tcCastR; }
/* Same cast as the previous paint: the DOM was rebuilt but nothing new was
   played, so the entrance must not replay. */
.tc-cast-panel.tc-cast-still { animation: none; }
.tc-cast-discard { filter: grayscale(0.6); opacity: 0.72; }
.tc-cast-art {
  flex: 0 0 100px;
  min-height: 0;
  position: relative;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.06), transparent 60%);
}
.tc-cast-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tc-cast-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 6px 10px 8px;
  gap: 3px;
}
.tc-cast-name {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.3px;
  line-height: 1.2;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  word-break: break-word;
}
.tc-cast-desc {
  flex: 1 1 auto;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text-dim);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.tc-cast-cost {
  align-self: flex-end;
  min-width: 22px;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 800;
  text-align: center;
  color: #0b0f14;
  background: var(--gold);
  box-shadow: 0 0 8px rgba(241, 193, 82, 0.5);
  font-variant-numeric: tabular-nums;
}
.tc-cast-by {
  position: absolute;
  bottom: 3px;
  left: 50%;
  transform: translateX(-50%);
  max-width: 120px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-shadow: 0 1px 3px #000;
}
@keyframes tcCastL {
  0% { opacity: 0; transform: translate(-56px, -10px) scale(0.86) rotate(-4deg); }
  100% { opacity: 1; transform: none; }
}
@keyframes tcCastR {
  0% { opacity: 0; transform: translate(56px, -10px) scale(0.86) rotate(4deg); }
  100% { opacity: 1; transform: none; }
}
.tc-events {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 300px;
  max-height: 96px;
  overflow: hidden;
  /* The sky is the one place that cannot grow: when the ticker meets its
     ceiling, the oldest line dissolves instead of being sliced mid-letter. */
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 16px), transparent 100%);
  mask-image: linear-gradient(180deg, #000 calc(100% - 16px), transparent 100%);
}
.tc-event {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 1px 8px;
  border-radius: var(--r-sm);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-dim);
  /* `backwards`, not `both`: a forwards fill would pin every row at the
     keyframe's opacity 1 and erase the nth-child fade ladder below. */
  animation: tcEvIn 280ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(var(--i, 0) * 45ms);
}
@keyframes tcEvIn {
  0% { opacity: 0; transform: translateY(-5px); }
  100% { opacity: 1; transform: none; }
}
.tc-event:nth-child(2) { opacity: 0.78; }
.tc-event:nth-child(3) { opacity: 0.62; }
.tc-event:nth-child(n + 4) { opacity: 0.45; }
.tc-event-ico { font-size: 11px; flex: 0 0 auto; }
.tc-event-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.tc-event-empty { justify-content: center; font-style: italic; opacity: 0.5; }

/* === cards === */
.tc-card {
  position: relative;
  display: flex;
  flex-direction: column;
  width: var(--tc-cw);
  height: var(--tc-ch);
  flex: 0 0 auto;
  border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: linear-gradient(150deg, #1c2431 0%, #121924 55%, #0c1119 100%);
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.07);
  line-height: 1.1;
  overflow: hidden;
}
.tc-card.tc-mini { --tc-cw: 64px; --tc-ch: 90px; }
.tc-card-art {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.05), transparent 60%);
}
.tc-card-glyph {
  font-size: calc(var(--tc-cw) * 0.34);
  opacity: 0.5;
  filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.15));
}
.tc-card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tc-res-bricks .tc-card-art { box-shadow: inset 0 -2px 0 rgba(241, 193, 82, 0.55); }
.tc-res-gems .tc-card-art { box-shadow: inset 0 -2px 0 rgba(103, 232, 249, 0.55); }
.tc-res-recruits .tc-card-art { box-shadow: inset 0 -2px 0 rgba(53, 199, 131, 0.55); }
/* The title bar sits ON TOP of the art, tinted by the resource that pays for
   the card — the original's own way of telling bricks, gems and recruits apart
   before you have read a word. */
.tc-card-name {
  flex: 0 0 auto;
  padding: 3px 5px;
  font-size: calc(var(--tc-cw) * 0.105);
  font-weight: 800;
  letter-spacing: 0.1px;
  color: var(--text);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  min-height: calc(var(--tc-cw) * 0.24);
  border-bottom: 1px solid rgba(0, 0, 0, 0.45);
}
.tc-res-bricks .tc-card-name { background: rgba(241, 193, 82, 0.26); }
.tc-res-gems .tc-card-name { background: rgba(103, 232, 249, 0.26); }
.tc-res-recruits .tc-card-name { background: rgba(53, 199, 131, 0.26); }
/* The effect, in words. Hidden by default and switched on only where a card is
   big enough to read it — a clipped sentence is worse than none, and the
   picked card's full text is on `.tc-sel-info` either way. */
.tc-card-desc {
  display: none;
  flex: 0 0 auto;
  padding: 3px 5px 0;
  font-size: calc(var(--tc-cw) * 0.088);
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-dim);
  text-align: center;
  overflow: hidden;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.tc-card-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 3px;
  padding: 2px 5px 4px;
}
.tc-card-cost {
  min-width: 17px;
  padding: 1px 4px;
  border-radius: var(--r-pill);
  font-size: calc(var(--tc-cw) * 0.125);
  font-weight: 800;
  text-align: center;
  color: #0b0f14;
  font-variant-numeric: tabular-nums;
}
.tc-res-bricks .tc-card-cost { background: var(--tc-brick); box-shadow: 0 0 7px rgba(241, 193, 82, 0.5); }
.tc-res-gems .tc-card-cost { background: var(--tc-gem); box-shadow: 0 0 7px rgba(103, 232, 249, 0.5); }
.tc-res-recruits .tc-card-cost { background: var(--tc-recruit); box-shadow: 0 0 7px rgba(53, 199, 131, 0.5); }
.tc-card-res { font-size: calc(var(--tc-cw) * 0.13); }
.tc-flag { font-size: calc(var(--tc-cw) * 0.115); opacity: 0.9; }

.tc-res-text-bricks { color: var(--tc-brick); }
.tc-res-text-gems { color: var(--tc-gem); }
.tc-res-text-recruits { color: var(--tc-recruit); }

/* card back */
.tc-card.tc-back {
  align-items: center;
  justify-content: center;
  border-color: rgba(241, 193, 82, 0.28);
  background:
    repeating-linear-gradient(45deg, rgba(241, 193, 82, 0.055) 0 6px, transparent 6px 12px),
    linear-gradient(150deg, #1a2233 0%, #101827 55%, #0b101c 100%);
}
.tc-back-mark { font-size: 22px; color: rgba(241, 193, 82, 0.55); text-shadow: 0 0 10px rgba(241, 193, 82, 0.35); }

/* === the hand === */
/* The original lays the hand out as a ROW of separated, readable cards across
   the bottom third of the screen — not as a fan. A fan is what you draw when
   the hand has no room, and the point of this whole change is that it now
   does: at 1280 the band is 810px for six cards.
   The cards are sized from the BAND's height rather than from a fixed pixel
   width, because the height is what the scene rations (`--board-cap`) and a
   card that overflows it takes the towers with it. */
.tc-hand {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 3px;
  min-width: 0;
  padding: 2px 4px;
}
.tc-fan {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(4px, 1%, 12px);
  width: 100%;
  min-width: 0;
  min-height: calc(var(--tc-ch) * 0.62);
  padding: 4px 2px 0;
}
.tc-fan-card {
  --rot: 0deg;
  /* The motion pass's ease-out-quint on lift/settle; filter joins the list so
     the waiting-hand grayscale fades instead of snapping. */
  transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 200ms ease, opacity 200ms ease, filter 240ms ease;
  transform-origin: 50% 120%;
}
/* The spectator/back fans keep the overlap: they are counts, not choices. */
.tc-fan-backs, .tc-fan-reveal { gap: 0; }
.tc-fan-backs .tc-fan-card { margin-left: calc(var(--tc-cw) * -0.7); }
.tc-fan-backs .tc-fan-card:first-child { margin-left: 0; }
.tc-fan-reveal .tc-fan-card { margin-left: calc(var(--tc-cw) * -0.42); }
.tc-fan-reveal .tc-fan-card:first-child { margin-left: 0; }
/* A hand card takes the band's height and derives its width from the card's
   own 5:7 proportion, then gives width back when six of them will not fit —
   which is the original's own behaviour on a wide hand. */
.tc-my-hand .tc-fan { align-items: stretch; height: var(--tc-hand-h); }
.tc-my-hand .tc-hand-card {
  flex: 1 1 0;
  width: auto;
  height: 100%;
  min-width: 0;
  max-width: calc(var(--tc-hand-h) * 5 / 7);
  --tc-cw: calc(var(--tc-hand-h) * 5 / 7);
  --tc-ch: var(--tc-hand-h);
  /* Touch hygiene for the long-press discard (views/arcomage.js): no OS
     callout or selection magnifier halfway through the 500ms hold, and
     `manipulation` kills double-tap zoom while leaving the hand scroller's
     pan alive. The JS suppresses only the post-discard click; these cover
     the rest. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

.tc-hand-card { cursor: default; }
@media (hover: hover) {
  .tc-hand-card:hover {
    transform: rotate(var(--rot, 0deg)) translateY(-14px) scale(1.05);
    z-index: 60 !important;
  }
}
/* Turn cue: while the foe thinks, the hand steps back into the shade — the
   board is where the answer will appear, not in your cards. */
.tc-my-hand.tc-wait .tc-hand-card {
  filter: grayscale(0.35) brightness(0.9);
  opacity: 0.72;
}
.tc-hand-card.tc-playable {
  cursor: pointer;
  box-shadow:
    0 3px 9px rgba(0, 0, 0, 0.5),
    0 0 13px rgba(103, 232, 249, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
.tc-hand-card.tc-discardable:not(.tc-playable) {
  cursor: pointer;
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.22);
}
/* Split, durak.css's argument verbatim: ungated hover sticks to the last
   tapped card on touch and forges a selected look; focus-visible stays for
   keyboards. */
.tc-hand-card.tc-playable:focus-visible {
  transform: rotate(var(--rot, 0deg)) translateY(-14px) scale(1.05);
  z-index: 60 !important;
}
@media (hover: hover) {
  .tc-hand-card.tc-playable:hover {
    transform: rotate(var(--rot, 0deg)) translateY(-14px) scale(1.05);
    z-index: 60 !important;
  }
}
.tc-hand-card.tc-dim { opacity: 0.5; filter: saturate(0.6); }
.tc-hand-card.tc-sel {
  transform: rotate(var(--rot, 0deg)) translateY(-16px);
  z-index: 60 !important;
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.55),
    0 0 0 2px rgba(241, 193, 82, 0.9),
    0 0 18px rgba(241, 193, 82, 0.55);
}
.tc-hand-card.tc-dd-active {
  transform: rotate(var(--rot, 0deg)) translateY(-16px);
  z-index: 60 !important;
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.55),
    0 0 0 2px rgba(103, 232, 249, 0.9),
    0 0 18px rgba(103, 232, 249, 0.55);
}
.tc-hand-card.tc-dd-target {
  cursor: pointer;
  animation: tcDdPulse 1.15s ease-in-out infinite;
}
/* A queued card (premove): lifted, gold-ringed, and exempt from the waiting
   shade — it is the one card that IS about to act. */
.tc-hand-card.tc-pre {
  transform: rotate(var(--rot, 0deg)) translateY(-10px);
  z-index: 60 !important;
  box-shadow:
    0 6px 16px rgba(0, 0, 0, 0.6),
    0 0 0 2px rgba(241, 193, 82, 0.85),
    0 0 22px rgba(241, 193, 82, 0.45);
}
.tc-my-hand.tc-wait .tc-hand-card.tc-pre { filter: none; opacity: 1; }
.tc-pre-mark {
  position: absolute;
  right: 4px;
  top: 4px;
  z-index: 4;
  font-size: 13px;
  line-height: 1;
  text-shadow: 0 1px 4px #000;
}
@keyframes tcDdPulse {
  0%, 100% { box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(241, 193, 82, 0.55), 0 0 10px rgba(241, 193, 82, 0.35); }
  50% { box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5), 0 0 0 2.5px rgba(241, 193, 82, 0.95), 0 0 22px rgba(241, 193, 82, 0.7); }
}
.tc-count {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  white-space: nowrap;
}

/* === selection info + actions === */
.tc-sel-info {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 10px;
  min-height: 22px;
  padding: 0 8px;
  text-align: center;
}
.tc-sel-name { font-size: 13.5px; font-weight: 800; letter-spacing: 0.3px; white-space: nowrap; }
.tc-sel-desc { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.tc-sel-hint { font-size: 13.5px; font-weight: 800; letter-spacing: 0.3px; color: var(--text-faint); }
.tc-sel-pre {
  font-size: 13.5px;
  font-weight: 800;
  letter-spacing: 0.3px;
  white-space: nowrap;
  color: var(--gold);
}
.tc-sel-prehint { font-size: 13.5px; font-weight: 800; letter-spacing: 0.3px; color: rgba(236, 231, 250, 0.5); }
.tc-sel-dd {
  font-size: 13.5px;
  font-weight: 800;
  color: var(--gold);
  text-shadow: 0 0 12px rgba(241, 193, 82, 0.45);
  animation: popIn var(--dur-med) var(--spring) both;
}
.tc-action-row {
  display: flex;
  justify-content: center;
  gap: 10px;
  min-height: 40px;
  margin-top: -2px;
}
.tc-action {
  min-width: 150px;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.3px;
  padding: 9px 22px;
}
.btn-primary.tc-action { box-shadow: var(--shadow-glow-ok); }
/* Folded in beside resign and the draw offer on a wide column — see the note
   in the view. The row gives up its own height and its buttons give up the
   width they only needed while they were alone on a line. */
.tc-action-row.tc-action-merged {
  min-height: 0;
  margin-top: 0;
  justify-content: flex-start;
  gap: 8px;
}
.tc-action-merged .tc-action { min-width: 118px; font-size: 13.5px; padding: 7px 12px; }

/* === fighter-card extras === */
/* Tower and wall are the game state, not decoration: unlike chess captures or
   battleship pips they must never be the thing that gets clipped when the row
   runs out of room on a 320px phone. So this card's counters keep their width
   and the name gives up its own — it already ellipsizes. */
.tc-material { gap: 6px; align-items: center; }
.view-tc .fighter-mid { flex: 0 0 auto; }
.tc-cap {
  font-size: 11.5px;
  font-weight: 800;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  padding: 2px 7px;
  border: 1px solid var(--stroke);
  border-radius: var(--r-pill);
  background: var(--panel-soft);
}

/* === finish splash === */
.tc-finish {
  position: absolute;
  inset: 0;
  z-index: 55;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  background: rgba(6, 9, 14, 0.55);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  transition: opacity 0.28s ease;
}
.tc-finish.is-active { opacity: 1; }
.tc-finish-frame {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: min(380px, 94%);
  padding: 22px 34px;
  border-radius: var(--r-lg);
  border: 1px solid var(--glass-brd);
  background: rgba(10, 14, 20, 0.86);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--shadow-card);
  animation: popIn var(--dur-med) var(--spring) both;
  overflow: hidden;
}
/* The battle banner behind the verdict: laurels for a win, the fallen keep
   otherwise (the draw shares the defeat art, as the mock does). */
.tc-finish-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  background: center / cover no-repeat url("../../assets/arcomage/banner_defeat.webp");
  opacity: 0.42;
}
.tc-finish.is-win .tc-finish-frame::before {
  background-image: url("../../assets/arcomage/banner_victory.webp");
}
.tc-finish-emoji, .tc-finish-title, .tc-finish-sub { position: relative; }
.tc-finish-emoji { font-size: 44px; line-height: 1; }
.tc-finish-title {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--text);
}
.tc-finish-sub { font-size: 13.5px; font-weight: 600; color: var(--text-dim); }
.tc-finish.is-win .tc-finish-title { color: var(--gold); text-shadow: 0 0 22px rgba(241, 193, 82, 0.6); }
.tc-finish.is-loss .tc-finish-title { color: var(--danger); text-shadow: 0 0 22px rgba(255, 93, 108, 0.55); }
.tc-finish.is-draw .tc-finish-title { color: var(--cool); text-shadow: 0 0 22px rgba(124, 201, 255, 0.55); }

/* === moves log === */
.tc-moves { max-height: 300px; }
.tc-move-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 8px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
}
.tc-move-row:nth-child(odd) { background: var(--panel-soft); }
.tc-move-ico { font-size: 12px; width: 16px; text-align: center; }
.tc-move-text { font-weight: 700; letter-spacing: 0.3px; }
.tc-move-who {
  font-weight: 600;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: right;
}

/* === responsive === */
/* The scene is the only thing that knows how wide the battlefield is — the
   window is spent on the rail and the context column before the scene gets
   anything (858 of a 1280px window, 1306 of 1728). */
@container scene (min-width: 760px) {
  .view-tc { --tc-rail-w: 118px; --tc-hand-h: 150px; }
  .tc-tile-prod { font-size: 19px; }
  .tc-tile-stock { font-size: 18px; }
  .tc-tile-glyph { font-size: 30px; }
}
@container scene (min-width: 1100px) {
  /* 180, not 212: the original's towers read at ~35% of field height because
     the hand does not eat a third of the screen. 32px returned to the
     battlefield row makes the towers visibly taller without shrinking the
     cards below readable. */
  .view-tc { --tc-rail-w: 132px; --tc-hand-h: 180px; }
  .tc-tile-glyph { font-size: 42px; }
  .tc-tower-val { font-size: 23px; }
  .tc-wall-val { font-size: 18px; }
  .tc-cast-panel { max-width: 380px; }
  .tc-cast-art { flex: 0 0 120px; }
  .tc-cast-name { font-size: 16px; }
  .tc-cast-desc { font-size: 13px; }
  /* 178px of card is where three lines of effect text stop being a clipped
     fragment. Below it the hand keeps title, art and cost. */
  .tc-my-hand .tc-card-desc { display: -webkit-box; }
}

/* The phone. The original's own answer to a 390x844 portrait viewport is to
   refuse: https://arcomage.github.io/ covers the game with a «rotate your
   device» overlay and does not deal a hand. We cannot refuse — the arena is a
   Telegram mini-app and portrait is where it lives — so the field is kept and
   made to fit instead, measured rather than folded: at 390 the board column is
   370, and 2x74 of rail plus two 94px keeps plus the gaps is 366. What the
   rails give up is their WORDS, not their numbers — the glyph already names
   the resource — and the sky gives up the event ticker, which is the move log
   that the sidebar carries in full anyway.
   The hand keeps card-sized cards and scrolls sideways instead of shrinking
   six of them to 50px slivers. */
@container scene (max-width: 619px) {
  /* game.css hands the board its NATURAL size in this class, because for the
     square boards that is the width-bound one and the slack belongs at the
     foot. This battlefield has no natural height — it is a landscape, and left
     to itself it collapses to the height of three resource tiles: measured
     208px of stage inside a 373px `--board-cap` at 390x844, towers pinned at
     their 26px floor. So arcomage takes the column's slack back and states a
     floor under it. When even the floor does not fit (a 905px window gives the
     scene 527 and the column 256), `.tc-stage` scrolls from its top edge. */
  .g-board-col > .tc-stage { flex: 1 1 auto; }
  .tc-field { min-height: 330px; }
  .view-tc { --tc-rail-w: 74px; --tc-hand-h: 116px; }
  .tc-field { gap: 3px 5px; padding: 5px 5px 7px; }
  .tc-rail { gap: 4px; }
  .tc-tile-name { display: none; }
  .tc-tile-prod { font-size: 15px; }
  .tc-tile-stock { font-size: 15px; }
  .tc-tile-glyph { font-size: 20px; }
  .tc-keep-cards { display: none; }
  .tc-keep-head { padding: 1px 3px 2px; }
  /* The plates are wider than the things they stand under — a 34px tower slot
     carries a 44px «27 /50» — so the gap is set from the PLATES here, not from
     the graphics: at 5px the foe's wall number was printed over its tower's.
     104px of keep holds 44 + 14 + 26. */
  .tc-yard { gap: 14px; padding: 0 2px; }
  .tc-plate { white-space: nowrap; }
  .tc-events { display: none; }
  .tc-cast-panel { max-width: 220px; }
  .tc-cast-art { flex: 0 0 64px; }
  .tc-cast-name { font-size: 11px; }
  .tc-cast-desc { font-size: 10px; -webkit-line-clamp: 2; }
  .tc-cast-cost { font-size: 11px; min-width: 18px; padding: 1px 5px; }
  /* The sky is 214px wide here and its floor is exactly where the two number
     plates are — measured at 390x844, the deck and discard counters landed on
     top of «20 /50» and clipped it. Both counts are already in the status card
     («Колода … · Сброс …»), so the field gives them up rather than the number
     that decides the game. */
  .tc-piles { display: none; }
  .tc-tower-val { font-size: 17px; }
  .tc-plate { padding: 1px 5px; }
  /* The hint rides in the same grid cell as the hand and bleeds through the
     cards' semi-transparent tops. At phone sizes the hand IS the affordance —
     playable cards glow, the action row enables — so the line is noise. */
  .tc-sel-info { display: none; }
  .tc-my-hand {
    display: flex;
    align-items: stretch;
    overflow-x: auto;
    overflow-y: visible;
    scrollbar-width: thin;
    scrollbar-color: rgba(241, 193, 82, 0.5) rgba(255, 255, 255, 0.08);
    padding-bottom: 4px;
  }
  /* Edge fade: the only honest scroll affordance on a track that macOS hides.
     Sticky flex items that pin to the viewport edges — invisible when the hand
     fits, unmistakable when it does not. Negative margin collapses the space
     they would otherwise take from the fan. */
  .tc-my-hand::before,
  .tc-my-hand::after {
    content: "";
    position: sticky;
    align-self: stretch;
    width: 20px;
    min-width: 20px;
    flex: 0 0 auto;
    pointer-events: none;
    z-index: 4;
  }
  .tc-my-hand::before {
    left: 0;
    margin-right: -20px;
    background: linear-gradient(to right, rgba(11, 15, 20, 0.95), transparent);
  }
  .tc-my-hand::after {
    right: 0;
    margin-left: -20px;
    background: linear-gradient(to left, rgba(11, 15, 20, 0.95), transparent);
  }
  .tc-my-hand .tc-fan { min-width: max-content; justify-content: flex-start; margin: 0 auto; padding-top: 4px; }
  .tc-my-hand .tc-hand-card { flex: 0 0 auto; width: calc(var(--tc-hand-h) * 5 / 7); }
  .tc-action { min-width: 0; flex: 1 1 0; max-width: 220px; }
  .tc-sel-info { flex-direction: column; align-items: center; gap: 2px; }
}
@container scene (max-width: 409px) {
  .view-tc { --tc-rail-w: 68px; --tc-hand-h: 108px; }
  .tc-finish-title { font-size: 24px; }
  .tc-finish-frame { padding: 16px 22px; }
}

/* === PHONE LANDSCAPE — the battlefield must fit ===
   At 844×390 the scene is ~812px wide (one-column compact from game.css) but
   only ~120-160px of height reaches the board-frame after the shell chrome,
   the two fighter bars, the countdown, and the action row. The original's
   answer at this exact viewport is a fully playable compact form: slim
   resource columns, short towers, and a hand of six small cards across the
   bottom — all inside the visible scene. We match that grammar.

   Orientation is a viewport property no container query can ask about (the
   scene is `container-type: inline-size`), so this is a media query scoped to
   `.view-tc` — the same pattern game.css uses for `.land-match`. */
@media (orientation: landscape) and (max-height: 500px) {
  .view-tc { --tc-rail-w: 52px; --tc-hand-h: 62px; }
  /* The stage must fill the column's available height so the field can fill
     the stage. game.css compact gives board-frame `flex: 0 0 auto` (content
     height) which would let the field grow unbounded and scroll the hand off. */
  .view-tc .g-board-col > .tc-stage { flex: 1 1 0; min-height: 0; }
  .view-tc .g-board-col { height: var(--match-h, 100%); overflow: hidden; }
  /* At 390px of height the chrome (two fighter bars + countdown + action row +
     utilities row) exceeds the column. The original's answer: the field IS the
     screen. We hide the player's own fighter bar (their info is in the keep
     head) and the utilities row (resign/draw/tools survive a rotation back).
     The top fighter bar and the play/discard row compress. */
  /* Both fighter bars and the utilities row vanish: the original at 844×390
     shows zero external chrome — names live in the keep heads, clocks in the
     sidebar below. The field IS the screen. */
  .view-tc .g-board-col > .fighter-slot { display: none; }
  .view-tc .g-board-col > .g-actions-row { display: none; }
  .view-tc .board-countdown { font-size: 11px; padding: 1px 0; min-height: 14px; }
  .view-tc .tc-action-row { min-height: 28px; gap: 6px; margin-top: 0; }
  .view-tc .tc-action { min-height: 28px; padding: 4px 10px; font-size: 11px; }
  /* The 28px above out-specifies base.css's 44px floor ((0,2,0) beats
     (0,1,1)), which is right for a short DESKTOP window — but on a phone held
     sideways «Разыграть»/«Сбросить» are the two most-pressed controls in the
     game and 28px is under any honest thumb. Coarse pointers get the floor
     back; the 16px comes out of the flexing field, whose tower/wall clamps
     absorb it. */
  @media (pointer: coarse) {
    .view-tc .tc-action-row { min-height: 44px; }
    .view-tc .tc-action { min-height: 44px; padding: 5px 10px; }
  }
  /* Fill the stage exactly — no min-height that overflows, no scroll that
     hides the hand. The field compresses to what the stage offers. */
  .view-tc .tc-field { min-height: 0; height: 100%; gap: 2px 4px; padding: 3px 4px 4px; }
  .view-tc .tc-stage { overflow: hidden; }
  .view-tc .tc-rail { gap: 2px; }
  .view-tc .tc-tile-glyph { font-size: 14px; }
  .view-tc .tc-tile-prod { font-size: 12px; }
  .view-tc .tc-tile-stock { font-size: 12px; }
  .view-tc .tc-tile-name { display: none; }
  .view-tc .tc-tile-foot { padding: 1px 3px 2px; }
  .view-tc .tc-keep-head { padding: 1px 4px; }
  .view-tc .tc-side-name { font-size: 9px; }
  .view-tc .tc-keep-cards { display: none; }
  .view-tc .tc-yard { gap: 8px; padding: 0 2px; }
  .view-tc .tc-tower-slot { width: clamp(28px, 18%, 50px); }
  .view-tc .tc-wall-slot { width: clamp(20px, 12%, 36px); }
  .view-tc .tc-tower-val { font-size: 13px; }
  .view-tc .tc-wall-val { font-size: 11px; }
  .view-tc .tc-plate { padding: 0 4px; margin-top: 1px; }
  .view-tc .tc-plate-goal { font-size: 8px; }
  /* The cast panel and event ticker are luxuries the landscape cannot afford.
     The move log in the sidebar carries the same information. */
  .view-tc .tc-mid { display: none; }
  .view-tc .tc-ground { height: 20%; min-height: 12px; }
  /* The hand: six cards in a row, small enough to fit the width without
     scrolling at 812px of scene. 62px height × 5/7 = 44px width × 6 + gaps
     = ~290px — fits easily. */
  .view-tc .tc-my-hand { display: flex; overflow-x: visible; }
  .view-tc .tc-my-hand::before,
  .view-tc .tc-my-hand::after { display: none; }
  .view-tc .tc-my-hand .tc-fan { min-width: 0; justify-content: center; padding-top: 2px; }
  .view-tc .tc-my-hand .tc-hand-card {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    max-width: calc(var(--tc-hand-h) * 5 / 7);
  }
  .view-tc .tc-card-name { font-size: 7px; min-height: 12px; padding: 1px 2px; }
  .view-tc .tc-card-foot { padding: 1px 3px 2px; }
  .view-tc .tc-card-cost { font-size: 8px; min-width: 12px; padding: 0 3px; }
  .view-tc .tc-card-res { font-size: 9px; }
  .view-tc .tc-flag { font-size: 8px; }
  .view-tc .tc-card-desc { display: none; }
  /* deadends-map S8: with the card faces down to 44px the desc and the
     title-attribute are both gone, and on touch there is no hover — the
     selected card's ONLY text is .tc-sel-info. It keeps a slim row of its own
     above the hand (the extra grid row below) instead of being deleted: name
     + effect line at ~12px of height, which the 20%-tall ground gives back. */
  .view-tc .tc-field {
    grid-template-rows: minmax(0, 1fr) auto auto;
    grid-template-areas:
      "meres mekeep foekeep foeres"
      "sel   sel    sel     sel"
      "hand  hand   hand    hand";
  }
  .view-tc .tc-sel-info {
    grid-area: sel;
    align-self: center;
    min-height: 12px;
    gap: 6px;
    padding: 0 4px;
    flex-wrap: wrap;
    align-items: center;
  }
  .view-tc .tc-sel-name { font-size: 10px; }
  .view-tc .tc-sel-desc,
  .view-tc .tc-sel-hint,
  .view-tc .tc-sel-dd { font-size: 9.5px; }
}

/* === floating +N/−N popups (the original's NumberDiff) === */
.tc-floats {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 10;
  overflow: visible;
}
.tc-float {
  position: absolute;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  line-height: 1;
  pointer-events: none;
  animation: tcFloatRise 1.2s ease-out forwards;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
  transform: translateX(-50%);
}
.tc-float-up { color: var(--ok); }
.tc-float-down { color: var(--danger); }
@keyframes tcFloatRise {
  0% { opacity: 0; transform: translateX(-50%) translateY(4px) scale(0.7); }
  12% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1.1); }
  25% { transform: translateX(-50%) translateY(-6px) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) translateY(-34px) scale(0.9); }
}

/* === screen shake on tower/wall damage === */
.tc-field.tc-shake {
  animation: tcShake 0.35s ease both;
}
@keyframes tcShake {
  0%, 100% { transform: translate(0); }
  15% { transform: translate(-3px, 1px); }
  30% { transform: translate(3px, -1px); }
  45% { transform: translate(-2px, -1px); }
  60% { transform: translate(2px, 1px); }
  75% { transform: translate(-1px, 0); }
}

/* === tower/wall damage flash overlay === */
.tc-tower-slot, .tc-wall-slot { position: relative; }
/* The lantern at the foot of each tower — the keep's own ground light. */
.tc-tower-slot::before {
  content: "";
  position: absolute;
  left: -55%;
  right: -55%;
  bottom: 18px;
  height: 38%;
  background: radial-gradient(58% 100% at 50% 100%, rgba(255, 93, 108, 0.2), transparent 72%);
  pointer-events: none;
}
.tc-me-wrap .tc-tower-slot::before {
  background: radial-gradient(58% 100% at 50% 100%, rgba(124, 201, 255, 0.2), transparent 72%);
}
.tc-tower-slot.tc-hit::after,
.tc-wall-slot.tc-hit::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: radial-gradient(ellipse at 50% 80%, rgba(255, 60, 40, 0.5), transparent 70%);
  animation: tcHitFlash 0.55s ease-out forwards;
  pointer-events: none;
  z-index: 5;
}
@keyframes tcHitFlash {
  0% { opacity: 1; }
  100% { opacity: 0; }
}
/* Squash on hit: the tower takes the blow bodily — compress, then a small
   rebound past rest. Rides the same .tc-hit class as the flash. */
.tc-tower-slot.tc-hit { animation: tcHitSquash 420ms cubic-bezier(0.22, 1.2, 0.36, 1) both; }
@keyframes tcHitSquash {
  0% { transform: none; }
  22% { transform: scaleY(0.955) scaleX(1.03) translateY(3px); }
  60% { transform: scaleY(1.012) scaleX(0.994); }
  100% { transform: none; }
}

/* === the hover preview (what would this card do) === */
/* ±N beside every number the hovered/selected card would change, on BOTH
   sides of the field, and a ring around a tower about to take or gain — the
   answer the engine gives before the card is ever committed. */
.tc-pv {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: -0.3px;
  font-variant-numeric: tabular-nums;
}
.tc-pv-plus { color: var(--ok); }
.tc-pv-minus { color: var(--danger); }
.tc-tile-art .tc-pv {
  position: absolute;
  right: 4px;
  top: 4px;
  text-shadow: 0 1px 3px #000;
}
.tc-plate-tower .tc-pv { font-size: 11.5px; }
.tc-tower-slot, .tc-wall-slot { transition: box-shadow 200ms ease; border-radius: 8px; }
.tc-tower-slot.tc-pv-up { box-shadow: 0 0 0 2px rgba(53, 199, 131, 0.6), 0 0 20px rgba(53, 199, 131, 0.3); }
.tc-tower-slot.tc-pv-down { box-shadow: 0 0 0 2px rgba(255, 93, 108, 0.7), 0 0 20px rgba(255, 93, 108, 0.35); }

/* === drag-to-play (phone) === */
.tc-drag-band {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: calc(var(--tc-hand-h) + 26px);
  display: flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  border-radius: 10px;
  border: 1.5px dashed rgba(53, 199, 131, 0.8);
  background: linear-gradient(180deg, rgba(53, 199, 131, 0.22), rgba(53, 199, 131, 0.06));
  box-shadow: 0 0 26px rgba(53, 199, 131, 0.35);
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.4px;
  color: #9ff0c4;
  text-shadow: 0 1px 4px #000;
  z-index: 35;
  pointer-events: none;
}

/* === card deal animation === */
/* Only a card that actually ENTERED the hand deals in (the view marks it);
   survivors of a rebuild hold their ground. `backwards`, never `both`: a
   forwards fill keeps the animation's `transform: none` applied after it
   ends, and an animation's transform beats :hover — the fill was why hand
   cards stopped lifting under the cursor. */
.tc-hand-card.tc-deal {
  animation: tcDealIn 0.3s ease backwards;
  animation-delay: calc(var(--i, 0) * 40ms);
}
@keyframes tcDealIn {
  0% { opacity: 0; transform: translateY(20px) scale(0.85); }
  100% { opacity: 1; transform: none; }
}

/* === PHONE STAGE — the mock's 880×400 scene ===
   The design's phone answer, adopted wholesale (owner's call): the duel is a
   WIDE scene, so a phone plays it landscape. The board column becomes a fixed
   880×400 stage scaled to the viewport (rotated 90° inside portrait when the
   device's rotation lock is on — see .tc-rotate below), the fighter bars and
   utility rows vanish (clocks ride the turn line, actions live in the 📜
   sheet), and the stage declares ITSELF the `scene` query container so the
   container queries above answer to its 880px, not to the portrait window
   behind it. The JS writes --tc-stage-scale/--tc-stage-rot (views/arcomage.js
   fitPhoneStage) and toggles .tc-stage-phone. */
/* The shell's fadeUp view-entrance fills `both`, pinning `transform` on the
   view AND the column forever (an animation's transform beats inline styles)
   — which both breaks the stage's own translate/scale and turns .view into
   the containing block for every position:fixed overlay (the stage, the
   rotate prompt). On a phone the stage IS the screen, so the entrance
   animation has nothing to animate anyway. */
.view-tc.tc-phone,
.view-tc.tc-stage-phone,
.view-tc.tc-stage-phone .g-board-col {
  animation: none;
}
/* Nothing of the page may peek around the stage: the stage IS the screen. */
.view-tc.tc-stage-phone::before {
  content: "";
  position: fixed;
  inset: 0;
  background: #08060f;
  /* Above the shell's topbar/tabbar (--z-topbar: 50): the stage IS the
     screen, and the chrome was eating the rotated stage's edges. */
  z-index: 51;
}
.view-tc.tc-stage-phone .g-board-col {
  position: fixed;
  left: 50%;
  top: 50%;
  /* JS height-fits the scale, then hands the width the WHOLE viewport
     (fitPhoneStage writes --tc-stage-w); 880 is the designed floor. The
     !important is deliberate: in a PORTRAIT viewport the mobile column
     layer (the ≤619 block) still matches and re-heights the column — the
     rotated stage is fixed geometry and must not lose that fight. */
  width: var(--tc-stage-w, 880px) !important;
  height: var(--tc-stage-h, 400px) !important;
  /* fitMatchColumn keeps writing --match-h for the normal layout, and the
     generic column rule turns it into a min-height that BEATS height — in a
     portrait viewport that inflated the rotated stage to 776px. */
  min-height: 0 !important;
  max-height: none !important;
  margin: 0;
  padding: 3px;
  gap: 5px;
  display: flex;
  flex-direction: column;
  transform: translate(-50%, -50%) rotate(var(--tc-stage-rot, 0deg)) scale(var(--tc-stage-scale, 1));
  transform-origin: center center;
  z-index: 52;
  background:
    radial-gradient(90% 60% at 12% -10%, rgba(167, 139, 250, 0.1), transparent 60%),
    radial-gradient(70% 50% at 100% 0%, rgba(241, 193, 82, 0.07), transparent 60%),
    #08060f;
  container-type: inline-size;
  container-name: scene;
  overflow: hidden;
}
.view-tc.tc-stage-phone .g-board-col > .fighter-slot { display: none; }
.view-tc.tc-stage-phone .g-board-col > .g-actions-row { display: none; }
.view-tc.tc-stage-phone .g-side-col { display: none; }
/* The mock's phone layout values (its layoutVals()), stated against the
   880px stage. Also un-does the phone-landscape media block above, which
   fires on the same devices but designed the pre-stage compact form. */
.view-tc.tc-stage-phone { --tc-rail-w: 62px; --tc-hand-h: 118px; }
.view-tc.tc-stage-phone .g-board-col > .tc-stage { flex: 1 1 auto; min-height: 0; overflow: hidden; }
.view-tc.tc-stage-phone .tc-field {
  /* Exactly the stage, never a pixel more: `min-height: 100%` let the content
     push the field past a clipping stage, and what hung over the edge was the
     bottom of the hand — the cost row of every card. The battlefield row (1fr)
     absorbs the shortfall instead; the hand keeps the size it was given. */
  height: 100%;
  min-height: 0;
  overflow: hidden;
  gap: 4px 8px;
  padding: 6px 8px 8px;
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas:
    "meres mekeep foekeep foeres"
    "hand  hand   hand    hand";
}
.view-tc.tc-stage-phone .tc-ground { height: 30%; min-height: 22px; }
/* The sky between the two keeps is the shortest thing on a phone: the cast
   panel and the two piles were sharing 78px and printed on top of each other.
   It starts at the top edge and stops just above the hand. */
.view-tc.tc-stage-phone .tc-mid {
  display: flex;
  /* Not 2px: the sky must start BELOW the nameplates, or the cast panel lands
     on the crowns of both towers. */
  top: 20px;
  bottom: calc(var(--tc-hand-h) + 6px);
  gap: 3px;
}
.view-tc.tc-stage-phone .tc-piles { gap: 7px; }
.view-tc.tc-stage-phone .tc-pile { padding: 3px 6px; gap: 2px; }
.view-tc.tc-stage-phone .tc-pile-ico { font-size: 13px; }
.view-tc.tc-stage-phone .tc-pile-count { font-size: 11px; }
.view-tc.tc-stage-phone .tc-events { display: none; }
.view-tc.tc-stage-phone .tc-piles { display: flex; }
.view-tc.tc-stage-phone .tc-cast-panel { max-width: 232px; padding: 3px 6px; gap: 6px; }
.view-tc.tc-stage-phone .tc-cast-art { flex: 0 0 42px; }
.view-tc.tc-stage-phone .tc-cast-name { font-size: 10.5px; }
.view-tc.tc-stage-phone .tc-cast-desc { font-size: 9.5px; -webkit-line-clamp: 2; }
.view-tc.tc-stage-phone .tc-cast-cost { font-size: 11px; min-width: 18px; padding: 1px 5px; }
.view-tc.tc-stage-phone .tc-rail { gap: 5px; }
.view-tc.tc-stage-phone .tc-tile-glyph { font-size: 20px; }
.view-tc.tc-stage-phone .tc-tile-prod { font-size: 13px; }
.view-tc.tc-stage-phone .tc-tile-stock { font-size: 13px; }
.view-tc.tc-stage-phone .tc-tile-name { display: none; }
.view-tc.tc-stage-phone .tc-tile-foot { padding: 2px 6px 4px; }
.view-tc.tc-stage-phone .tc-keep-head { padding: 1px 8px; font-size: 10px; }
.view-tc.tc-stage-phone .tc-keep-cards { display: inline; }
.view-tc.tc-stage-phone .tc-side-name { font-size: 9.5px; }
.view-tc.tc-stage-phone .tc-yard { gap: clamp(14px, 4%, 30px); padding: 0 4px; }
.view-tc.tc-stage-phone .tc-tower-slot { width: clamp(44px, 22%, 80px); }
.view-tc.tc-stage-phone .tc-wall-slot { width: clamp(30px, 14%, 52px); }
.view-tc.tc-stage-phone .tc-tower-val { font-size: 16px; }
.view-tc.tc-stage-phone .tc-wall-val { font-size: 13px; }
.view-tc.tc-stage-phone .tc-plate { padding: 1px 8px; margin-top: 3px; }
.view-tc.tc-stage-phone .tc-plate-goal { font-size: 10px; }
.view-tc.tc-stage-phone .tc-my-hand { display: flex; overflow: visible; padding-bottom: 2px; }
.view-tc.tc-stage-phone .tc-my-hand::before,
.view-tc.tc-stage-phone .tc-my-hand::after { display: none; }
.view-tc.tc-stage-phone .tc-my-hand .tc-fan { min-width: 0; justify-content: center; height: var(--tc-hand-h); padding-top: 4px; }
/* A hand of five across the whole width: the cap is what the width divided by
   five allows, not a desktop-sized thumbnail. A card that carries its effect
   in words needs about 120px to do it in three lines. */
.view-tc.tc-stage-phone .tc-my-hand .tc-hand-card {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
  max-width: 168px;
  /* The finger owns the card: no browser pan mid-drag. */
  touch-action: none;
}
.view-tc.tc-stage-phone .tc-card-name { font-size: 11px; min-height: 24px; padding: 3px 4px; }
/* The effect text is what makes a hand playable without tapping every card —
   the phone used to be the one place it was switched off, which left a hand of
   five pictures and a number. It is on, and the card is sized around it. */
.view-tc.tc-stage-phone .tc-card-desc {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  font-size: 10px;
  line-height: 1.25;
  padding: 2px 4px 0;
  color: var(--text);
}
/* On a phone the picture yields to the words: the art absorbs the squeeze
   (down to a strip) so the effect line is never the thing that gets cut. */
.view-tc.tc-stage-phone .tc-card-art { flex: 1 1 34%; min-height: 18px; }
/* The bubbles hang 4% outside the board by design; on a clipped stage that
   only inflates the scroll box. */
.view-tc.tc-stage-phone .board-emotes { inset: 0; }
.view-tc.tc-stage-phone .tc-card-foot { padding: 1px 4px 3px; }
.view-tc.tc-stage-phone .tc-card-cost { font-size: 11px; min-width: 14px; }
.view-tc.tc-stage-phone .tc-card-res { font-size: 11px; }
.view-tc.tc-stage-phone .tc-flag { font-size: 10px; }
/* The selection line lived in the hand's own grid cell and was painted OVER by
   the cards — a caption nobody could read. Every card now carries its own
   name and effect, the discard-pick prompt toasts, and a queued premove wears
   its ⏱ mark, so the stage drops the line instead of stacking it. */
.view-tc.tc-stage-phone .tc-sel-info { display: none; }
.view-tc.tc-stage-phone .board-countdown {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 9px;
  font-size: 11.5px;
  padding: 0;
  min-height: 16px;
}
/* The buttons duplicate the gestures on a phone (tap-tap plays, hold
   discards) — the stage spends their 44px on the battlefield instead. */
.view-tc.tc-stage-phone .tc-action-row { display: none; }
/* The mini cluster on the turn line: 📜 sheet, ❓ help, ⛶ fullscreen. */
.tc-mini-cluster { display: none; }
.view-tc.tc-stage-phone .tc-mini-cluster {
  display: inline-flex;
  gap: 7px;
  margin-left: auto;
  padding-right: 4px;
}
.tc-mini-btn {
  min-height: 26px;
  min-width: 34px;
  padding: 2px 8px;
  font-size: 13px;
  line-height: 1;
  border-radius: 8px;
  opacity: 0.85;
}
.tc-mini-btn.is-on {
  border-color: rgba(241, 193, 82, 0.75);
  color: #f1c152;
  opacity: 1;
}
/* The 📜 hides with the desktop tools; on the stage it rides the cluster. */
.view-tc.tc-stage-phone .tc-mini-cluster .tc-sheet-btn {
  display: inline-flex;
  min-width: 34px;
  max-width: none;
}
/* The ❓ face runs a touch small next to 📜 at the same font-size. */
.tc-help-btn { font-size: 12px; }
/* ⛶ is a glyph, not an emoji — give it a body. */
.tc-fs-btn { font-size: 15px; font-weight: 700; }
/* The turn line is chrome, not game: on a 291px screen every pixel it does
   not need belongs to the battlefield. */
.view-tc.tc-stage-phone .board-countdown { min-height: 24px; align-items: center; padding-left: 8px; }
.view-tc.tc-stage-phone .tc-mini-btn { min-height: 22px; padding: 1px 7px; }
/* The gesture help rides openLayer's modal root (outside the stage layer);
   this class only centers the card on the standard backdrop. */
.tc-help {
  display: flex;
  align-items: center;
  justify-content: center;
}
.tc-help-card {
  position: relative;
  /* The backdrop is symmetric; only the card needs to face the player when
     the stage plays rotated inside a locked-portrait viewport. */
  transform: rotate(var(--tc-stage-rot, 0deg));
  max-width: 340px;
  padding: 18px 22px 16px;
  border-radius: 14px;
  border: 1px solid rgba(167, 139, 250, 0.35);
  background: linear-gradient(180deg, rgba(26, 20, 44, 0.98), rgba(13, 10, 24, 0.98));
  box-shadow: 0 22px 60px rgba(0, 0, 0, 0.65);
}
.tc-help-title {
  font-weight: 800;
  font-size: 14px;
  letter-spacing: 0.04em;
  margin-bottom: 9px;
  color: #f1c152;
}
.tc-help-line { font-size: 12.5px; line-height: 1.55; color: rgba(236, 231, 250, 0.85); }
.tc-help-x {
  position: absolute;
  top: 6px;
  right: 8px;
  border: 0;
  background: none;
  color: rgba(236, 231, 250, 0.6);
  font-size: 14px;
  cursor: pointer;
  padding: 4px 6px;
}
.tc-rotate-hints {
  margin-top: 10px;
  font-size: 12px;
  line-height: 1.6;
  color: rgba(236, 231, 250, 0.72);
  text-align: center;
}
/* The inline clocks the hidden fighter bars leave behind: foe's on the left
   of the turn line, mine on the right. */
.tc-turn-clock {
  display: none;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.tc-turn-clock-foe { color: rgba(236, 231, 250, 0.55); }
.tc-turn-clock-mine { color: var(--gold); }
.view-tc.tc-stage-phone .tc-turn-clock { display: inline; }
/* 📜 opens the sheet; hidden anywhere the utility rows are on screen. */
.tc-sheet-btn {
  display: none;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto !important;
  min-width: 44px;
  max-width: 54px;
  font-size: 16px;
  padding: 0 6px;
}
/* === the 📜 sheet (phone): партия, actions, журнал, touch hints === */
.tc-sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: flex;
  align-items: stretch;
  justify-content: flex-end;
  background: rgba(6, 4, 12, 0.66);
}
.tc-sheet {
  width: min(420px, 86vw);
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background: rgba(12, 8, 24, 0.97);
  box-shadow: -20px 0 60px rgba(0, 0, 0, 0.6);
  overflow-y: auto;
}
.tc-sheet-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 800;
}
.tc-sheet-x { min-width: 44px; min-height: 36px; font-size: 15px; }
.tc-sheet-meta { font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--text-dim); }
.tc-sheet-row { display: flex; gap: 8px; }
.tc-sheet-row > .btn { flex: 1 1 0; min-height: 44px; }
.tc-sheet-kicker {
  margin-top: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(236, 231, 250, 0.5);
}
.tc-sheet-log { display: flex; flex-direction: column; gap: 2px; }
.tc-sheet-hints {
  margin-top: 6px;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.6;
  color: rgba(236, 231, 250, 0.45);
}
/* === «Поверни телефон» — portrait refuses the duel, as the original does === */
.tc-rotate {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 24px;
  background: radial-gradient(80% 60% at 50% 30%, rgba(167, 139, 250, 0.12), transparent 60%), #08060f;
  text-align: center;
}
.tc-rotate-frame {
  width: 92px;
  height: 148px;
  border: 3px solid rgba(241, 193, 82, 0.7);
  border-radius: 14px;
  box-shadow: 0 0 30px rgba(241, 193, 82, 0.25);
  animation: tcRotateHint 2.4s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
@keyframes tcRotateHint {
  0%, 22% { transform: rotate(0deg); }
  55%, 78% { transform: rotate(-90deg); }
  100% { transform: rotate(0deg); }
}
.tc-rotate-title {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 800;
  color: var(--gold);
}
.tc-rotate-sub {
  max-width: 320px;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-dim);
}
.tc-rotate-btn { min-height: 48px; padding: 0 22px; font-size: 14px; }

/* === short desktop windows — the sky runs out of vertical room first ===
   At ~800-860px of window the 180px hand plus the cast panel plus six ticker
   lines overdraw the sky (the clipped «Бот: −5» report). The hand gives back
   30px and the ticker keeps three whole lines. Heights above the phone
   landscape band only (its own block above owns ≤500), widths above the
   portrait-phone band (the container queries own those). */
@media (max-height: 860px) and (min-height: 501px) and (min-width: 1080px) {
  .view-tc { --tc-hand-h: 150px; }
  .tc-events { max-height: 64px; }
  .tc-cast-art { flex: 0 0 96px; }
  .tc-cast-panel { max-width: 340px; }
}

/* === reduced motion === */
@media (prefers-reduced-motion: reduce) {
  .tc-fan-card, .tc-tile-fill, .tc-tower-body, .tc-wall-body,
  .tc-keep-head, .tc-side-name { transition: none; }
  .tc-side-dot,
  .tc-cast-panel,
  .tc-hand-card.tc-dd-target,
  .tc-field.tc-draw-glow,
  .tc-tower-val.tc-up, .tc-tower-val.tc-down,
  .tc-wall-val.tc-up, .tc-wall-val.tc-down,
  .tc-tile-stock.tc-up, .tc-tile-stock.tc-down,
  .tc-tile-prod.tc-up, .tc-tile-prod.tc-down,
  .tc-event,
  .tc-float,
  .tc-turn-glow,
  .tc-rotate-frame,
  .tc-field.tc-shake,
  .tc-tower-slot.tc-hit,
  .tc-tower-slot.tc-hit::after,
  .tc-wall-slot.tc-hit::after,
  .tc-hand-card { animation: none; }
}

/* === the журнал card's ⌨ reveal (desktop sidebar) === */
.tc-kb-btn { min-width: 30px; height: 26px; font-size: 13px; }
.tc-kb-hints {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(167, 139, 250, 0.14);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.7;
  color: rgba(236, 231, 250, 0.5);
}

/* The stage IS the screen: the shell's chrome would eat its edges (the
   topbar lives at --z-topbar in the root stacking context, above anything
   the view itself can z past). Toggled by fitPhoneStage. */
body.tc-stage-full .topbar,
body.tc-stage-full #tabbar.shell-tabbar {
  display: none !important;
}
