/* ---------------------------------------------------------------------------
   A playmat, not a dashboard.

   The whole surface is felt; zones are silkscreened onto it with dashed rules
   and corner ticks, the way a real mat marks out where cards go. Everything
   numeric is monospaced, because in a card game the numbers are the argument.
   Brass is reserved for exactly one thing: what is yours to act on right now.
--------------------------------------------------------------------------- */

@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Archivo:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  /* The page is dark. Saying so makes the browser draw its own furniture -
     dropdown lists, scrollbars, date pickers - to match, instead of picking
     light defaults that a translucent background then shows through. */
  color-scheme: dark;

  /* Interface size, set from the header. See initScale() in app.js. */
  --ui-scale: 1;
  --felt:        #1b3a34;
  --felt-deep:   #142b26;
  --felt-lift:   #234a42;
  --silk:        #6e9c8b;
  --silk-dim:    #47705f;
  --bone:        #f2ede1;
  --bone-dim:    #cfc7b6;
  --ink:         #10201c;
  --brass:       #d9a441;
  --brass-dim:   #8c6b2c;
  --alert:       #c8503f;
  /* Damage counters. Deliberately not --alert: the HP badge owns red. */
  --counter:     #b9b3a4;
  --counter-ink: #1c1a15;
  --ok:          #6fae7c;
  /* The two the stylesheet used to write in by hand. Named so the
     appearance settings can reach them; the values are what they were. */
  --panel-bg:    rgba(0,0,0,.2);
  --page-top:    #23483f;

  --display: 'Archivo Black', system-ui, sans-serif;
  --label:   'Archivo', system-ui, sans-serif;
  --mono:    'IBM Plex Mono', ui-monospace, monospace;

  --gap: 14px;
  --radius: 7px;
  /* Card size is separate from interface size. Interface size zooms the
     whole board - text, gaps, buttons - so asking for bigger cards used to
     mean a bigger everything that no longer fitted the screen. The base
     size is scaled on its own, and anything that sets --card-w directly
     (the hero, the preview, the Stadium) still overrides both. */
  --card-base-w: 78px;
  --card-base-h: 109px;
  --card-scale: 1;
  --card-w: calc(var(--card-base-w) * var(--card-scale));
  --card-h: calc(var(--card-base-h) * var(--card-scale));
  /* The tick on a picked card, sized off the card so it stays in
     proportion at every interface size without dominating a small one. */
  --sel-badge: clamp(13px, calc(var(--card-w) * .22), 22px);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--felt-deep);
  color: var(--bone);
  font-family: var(--label);
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}

body {
  background-image:
    radial-gradient(circle at 50% 0%, var(--page-top) 0%, var(--felt-deep) 70%);
}

button, input, select, textarea { font: inherit; color: inherit; }

/* --- shared bits ---------------------------------------------------------- */

.eyebrow {
  font-family: var(--label);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--silk);
}

h1, h2, h3 { font-family: var(--display); font-weight: 400; letter-spacing: -0.01em; }
h1 { font-size: 30px; margin: 0 0 4px; }
h2 { font-size: 19px; margin: 0 0 10px; }
h3 { font-size: 14px; margin: 0 0 8px; }

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.btn {
  font-family: var(--label);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 9px 15px;
  border-radius: var(--radius);
  border: 1px solid var(--silk-dim);
  background: var(--felt-lift);
  color: var(--bone);
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, transform .08s ease;
}
.btn:hover:not(:disabled) { background: #2c5b51; border-color: var(--silk); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .38; cursor: not-allowed; }
.btn:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }

.btn.primary {
  background: var(--brass);
  border-color: var(--brass);
  color: var(--ink);
}
.btn.primary:hover:not(:disabled) { background: #ecb95a; border-color: #ecb95a; }
.btn.danger { border-color: var(--alert); color: #f0a99e; }
.btn.tiny { padding: 5px 9px; font-size: 10px; }

.field {
  width: 100%;
  padding: 9px 11px;
  border-radius: var(--radius);
  border: 1px solid var(--silk-dim);
  background: rgba(0,0,0,.22);
  color: var(--bone);
}
/* The thing you meant, and the escape hatch. They were the same button
   twice, side by side, so the hatch was as easy to hit as the card - which
   is how a working card came to be resolved by hand and looked broken. */
.option.primary {
  border-color: var(--brass);
  background: color-mix(in srgb, var(--brass) 16%, transparent);
  font-weight: 600;
}
.option.quiet {
  font-size: 12px;
  padding: 6px 10px;
  opacity: .62;
  border-style: dashed;
}
.option.quiet:hover { opacity: 1; }

.field:focus { outline: 2px solid var(--brass); outline-offset: 0; border-color: transparent; }
.field::placeholder { color: var(--silk-dim); }

/* A dropdown needs a solid colour. The list that opens is drawn by the
   browser against its own surface, so a translucent background composites
   over white there and leaves pale text on a pale ground - readable in the
   closed box, unreadable the moment it opens. */
select, select.field {
  background-color: var(--felt-deep);
  color: var(--bone);
  /* Room for the arrow the browser draws, so a long option does not run
     underneath it. */
  padding-right: 26px;
}
select option, select optgroup {
  background-color: var(--felt-deep);
  color: var(--bone);
}
/* A deck the table will not take. Colour is the fast read; the option also
   says so in words, since option colours are the one piece of styling a
   browser still feels free to draw however it likes. */
select option.mismatch { color: var(--alert); }
select:disabled, select.field:disabled { color: var(--bone-dim); }

/* ---------------------------------------------------------- action bar ---
   What you can do now, and what the turn has cost. Sticky either at the top
   under the header or at the bottom beside your hand - which one is a
   setting, because on a wide screen the hand is what you are looking at and
   on a phone the header is the only thing that never moves. */
.act-bar {
  position: sticky;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border: 1px solid var(--silk-dim);
  border-radius: 10px;
  background: color-mix(in srgb, var(--felt-deep) 92%, transparent);
  backdrop-filter: blur(6px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .28);
}
body.bar-top .act-bar { top: 8px; margin-bottom: 10px; }
body.bar-bottom .act-bar {
  bottom: 8px;
  margin-top: 10px;
  /* Below the mat in the source, so it has to be moved to sit beside the
     hand rather than above the board. */
  order: 2;
}
body.bar-bottom .table > div:first-child { display: flex; flex-direction: column; }
body.bar-bottom .mat { order: 1; }
body.bar-bottom .hand-rail { order: 3; }

.act-chips { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.tchip {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--silk-dim);
  color: var(--silk);
  white-space: nowrap;
}
.tchip b { color: var(--bone); font-weight: 600; }
/* Spent, so there is nothing left to think about there. */
.tchip.spent { border-color: var(--brass-dim); color: var(--brass-dim); }
.tchip.spent b { color: var(--brass-dim); }

.act-do { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.act-state { font-size: 12.5px; color: var(--bone-dim); }
/* The question, while it is being answered on the board. */
.act-ask { font-size: 13px; color: var(--bone); font-weight: 600; }

/* ------------------------------------------------- picking on the table ---
   While a question is open the board answers it: what can be chosen is
   lifted and ringed, everything else steps back so there is no doubt which
   is which. */
.mat.picking .pokemon.pick-off {
  opacity: .32;
  filter: saturate(.4);
  cursor: default;
}
.mat.picking .pokemon.pick-on {
  cursor: pointer;
  outline: 2px solid var(--brass);
  outline-offset: 3px;
  border-radius: 8px;
  animation: pick-breathe 1.8s ease-in-out infinite;
}
.mat.picking .pokemon.pick-on:hover { outline-width: 3px; }
.mat.picking .pokemon.pick-chosen {
  outline-width: 3px;
  background: color-mix(in srgb, var(--brass) 22%, transparent);
  animation: none;
}
@keyframes pick-breathe {
  0%, 100% { outline-color: var(--brass); }
  50%      { outline-color: var(--brass-dim); }
}
@media (prefers-reduced-motion: reduce) {
  .mat.picking .pokemon.pick-on { animation: none; }
}

/* The header can be told to get out of the way while you scroll. Off by
   default: a header that moves on its own is one more thing happening. */
header.bar { transition: transform .18s ease; }
body.autohide-header.header-away header.bar { transform: translateY(-100%); }

.panel {
  background: var(--panel-bg);
  border: 1px solid rgba(110,156,139,.24);
  border-radius: 10px;
  padding: 16px;
}

.row { display: flex; gap: 10px; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.grow { flex: 1; }
.muted { color: var(--silk); }
.hidden { display: none !important; }

/* --- app shell ------------------------------------------------------------ */

#app { display: flex; flex-direction: column; min-height: 100%; }

header.bar {
  border-bottom: 1px solid rgba(110,156,139,.22);
  /* Room for the clock and the battery.
     Installed on a Home Screen, Playmat runs with no browser furniture and
     a see-through status bar, so the page owns the very top of the screen
     and the header was being drawn underneath the time. This reserves
     exactly the height iOS says that strip is - zero everywhere else, so
     no other device gains a gap. The bar's own background fills the strip,
     which is why the status bar keeps something dark to sit on. */
  padding-top: env(safe-area-inset-top, 0px);
  /* Opaque, because it is sticky: at 24% black the page scrolled underneath
     it and showed through. Barely noticeable on a desktop, where the bar is
     one line; on a phone it wraps to three and a quarter of the screen
     became a translucent band with the board ghosting behind it. */
  background: #12241f;
  position: sticky;
  top: 0;
  /* Above the board and the action bar (60), below the modal scrim (100).
     It is furniture; nothing on the table should paint over it. */
  z-index: 80;
}

/* Where it is supported, the felt behind still reads through as a tint
   rather than the bar sitting on the page as a flat slab. */
@supports (backdrop-filter: blur(6px)) {
  header.bar {
    background: color-mix(in srgb, #12241f 86%, transparent);
    backdrop-filter: blur(8px);
  }
}

/* The bar spans the window; its contents line up with the panels below.
   Without this the header ran edge to edge while the page was capped and
   centred, so the two disagreed about where the page began. */
header.bar .bar-inner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 22px;
  max-width: 1500px;
  width: 100%;
  margin: 0 auto;
}
header.bar .who .field { width: 150px; }
header.bar .brand { font-family: var(--display); font-size: 16px; letter-spacing: .02em; }
header.bar .brand span { color: var(--brass); }

.tabs { display: flex; gap: 4px; }
.tab {
  border: none;
  background: none;
  padding: 7px 13px;
  border-radius: 6px;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--silk);
  cursor: pointer;
}
.tab[aria-selected="true"] { background: var(--felt-lift); color: var(--bone); }

main { flex: 1; padding: 22px; max-width: 1500px; width: 100%; margin: 0 auto; }

/* --- lobby ---------------------------------------------------------------- */

.lobby-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--gap);
  align-items: start;
}

.list { display: flex; flex-direction: column; gap: 8px; }
.list-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(0,0,0,.2);
  border: 1px solid transparent;
}
.list-item:hover { border-color: rgba(110,156,139,.35); }
.list-item.selected {
  border-color: var(--brass);
  /* Mixed from the accent rather than written out, so it follows the
     colour the player chose instead of staying brass behind a blue ring. */
  background: color-mix(in srgb, var(--brass) 12%, transparent);
}

.rules-editor {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 10px 18px;
  max-height: 340px;
  overflow-y: auto;
  padding-right: 6px;
}
.rules-editor label { display: flex; justify-content: space-between; gap: 8px; align-items: center; font-size: 13px; }
.rules-editor input[type=number] { width: 74px; text-align: right; }
.rules-group-title { grid-column: 1 / -1; margin-top: 6px; }

/* --- cards ---------------------------------------------------------------- */

.card {
  width: var(--card-w);
  height: var(--card-h);
  border-radius: var(--radius);
  background: var(--bone);
  color: var(--ink);
  border: 1px solid rgba(0,0,0,.4);
  box-shadow: 0 2px 5px rgba(0,0,0,.4);
  overflow: hidden;
  position: relative;
  cursor: pointer;
  flex: 0 0 auto;
  transition: transform .12s ease, box-shadow .12s ease;
}
.card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card .fallback {
  padding: 6px;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  font-size: 9px;
  line-height: 1.15;
}
.card .fallback strong { font-size: 10px; }
.card:hover { transform: translateY(-4px); box-shadow: 0 8px 16px rgba(0,0,0,.5); z-index: 5; }
/* Picked. See the note by --sel-tick below for why this is three
   separate signals rather than one louder one. */
.card.selected {
  outline: 3px solid var(--brass);
  outline-offset: 2px;
  /* A dark hairline just inside the accent ring and a wider one just
     outside it. The accent is a colour the player chooses and could land
     anywhere; these two do not move, so the edge is always there. */
  box-shadow: 0 0 0 1px rgba(0,0,0,.6),
              0 0 0 6px rgba(0,0,0,.4),
              0 6px 14px rgba(0,0,0,.45);
  /* Deliberately not lifted. Both containers that hold choosable cards
     scroll, so anything drawn outside a card is clipped by them, and
     padding every container to suit one decoration is the wrong way
     round. The tick below is what reads without a mouse, and it is drawn
     inside the card, so it costs no room anywhere. */
  z-index: 6;
}

/* The tick. Fixed colours on purpose - it is the signal that still works
   when the accent has been set to something that vanishes into the mat,
   and for anybody who does not separate those colours easily. Drawn
   inside the card because the card clips whatever overflows it. */
.card.selected::before,
.card.selected::after {
  content: "";
  position: absolute;
  pointer-events: none;
}
.card.selected::before {
  top: 4px;
  right: 4px;
  width: var(--sel-badge);
  height: var(--sel-badge);
  border-radius: 50%;
  background: rgba(12,20,17,.82);
  box-shadow: 0 0 0 1.5px rgba(242,237,225,.85);
}
.card.selected::after {
  /* Two borders of a rotated box: a tick with no font to depend on. */
  top: calc(4px + var(--sel-badge) * .28);
  right: calc(4px + var(--sel-badge) * .38);
  width: calc(var(--sel-badge) * .24);
  height: calc(var(--sel-badge) * .46);
  border: solid var(--bone);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.card.back {
  background: repeating-linear-gradient(45deg, #2a4f6d 0 6px, #24455f 6px 12px);
  border-color: #14273a;
}

.card-badge {
  position: absolute;
  bottom: 3px; right: 3px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(16,32,28,.86);
  color: var(--bone);
}

/* --- the mat -------------------------------------------------------------- */

.table {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--gap);
  align-items: start;
}

.mat {
  background:
    linear-gradient(rgba(255,255,255,.02), rgba(0,0,0,.12)),
    var(--felt);
  border: 1px solid var(--silk-dim);
  border-radius: 12px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  position: relative;
}

/* the centre line of a real mat */
.mat .centerline {
  height: 1px;
  background: repeating-linear-gradient(90deg, var(--silk-dim) 0 10px, transparent 10px 20px);
  margin: 2px 0;
  position: relative;
}
.mat .centerline span {
  position: absolute;
  left: 50%;
  top: -8px;
  transform: translateX(-50%);
  background: var(--felt);
  padding: 0 10px;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--silk-dim);
}

.side { display: flex; gap: 14px; align-items: flex-start; }
.side.flip { flex-direction: row-reverse; }

/* silkscreened zone outlines with corner ticks */
.zone {
  border: 1px dashed rgba(110,156,139,.4);
  border-radius: 8px;
  padding: 8px;
  position: relative;
  min-height: calc(var(--card-h) + 30px);
}
.zone::before, .zone::after {
  content: '';
  position: absolute;
  width: 9px; height: 9px;
  border-color: var(--silk-dim);
  border-style: solid;
}
.zone::before { top: -1px; left: -1px; border-width: 1px 0 0 1px; border-top-left-radius: 8px; }
.zone::after { bottom: -1px; right: -1px; border-width: 0 1px 1px 0; border-bottom-right-radius: 8px; }
.zone > .eyebrow { position: absolute; top: -7px; left: 10px; background: var(--felt); padding: 0 6px; }
.zone.bench { flex: 1; }
.zone-body { display: flex; gap: 8px; flex-wrap: wrap; padding-top: 6px; min-height: var(--card-h); }
.zone-body.empty::after {
  content: attr(data-empty);
  color: var(--silk-dim);
  font-size: 12px;
  align-self: center;
}

.active-slot { display: flex; flex-direction: column; align-items: center; gap: 6px; }

/* the one place brass appears on the mat */
.card.is-active-target { outline: 2px solid var(--brass); outline-offset: 2px; }

/* The card, and beside it the rail. Everything used to stack downwards
   under the card - an HP bar, an HP line, status tags, a pip row, an energy
   count and the statuses over again - which came to about sixty pixels of
   furniture under a card barely a hundred tall. That was the scrolling.

   Deliberately one rule: a second .pokemon rule further down the file wins
   only on the properties it actually names, so the row has to be declared
   where the column used to be rather than argued with later.

   Cards in play run about a sixth larger than a card in hand, which is
   affordable now the furniture has gone. The card-size setting still
   scales this along with everything else. */
.pokemon {
  position: relative;
  /* The slot stacks things inside itself - the card over what is tucked
     under it, the badge and the pips over the card - up to 50. Without a
     stacking context of its own those numbers compete with the whole page
     rather than with each other, and the board painted over the header. */
  isolation: isolate;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  justify-content: center;
  gap: 3px;
  --card-w: calc(var(--card-base-w) * var(--card-scale) * 1.16);
  --card-h: calc(var(--card-base-h) * var(--card-scale) * 1.16);
}
.hptext { font-family: var(--mono); font-size: 10px; color: var(--bone-dim); }

.pip {
  width: 18px; height: 18px;
  border-radius: 50%;
  /* Two edges, not one. The dark border carries a pale pip against pale
     art; the light ring outside it carries a dark pip against dark art.
     One edge alone disappears whenever the art underneath happens to be
     the same shade as the Energy - which is exactly the case where you
     most need to count them. */
  border: 1px solid rgba(0,0,0,.6);
  box-shadow: 0 0 0 1px rgba(255,255,255,.72),
              0 1px 2px rgba(0,0,0,.5);
  background: var(--pip, var(--bone-dim));
  font-size: 11px;
  line-height: 1;
  display: grid;
  place-items: center;
  color: var(--ink);
  font-family: var(--mono);
  font-weight: 700;
}
/* Special Energy is a different shape, not a different shade - shape reads
   at a glance and survives being the same colour as the basic Energy it
   provides. Slightly larger because a star of the same box looks smaller
   than a circle of it. */
/* The star is drawn in two stacked layers rather than clipped in one.
   A clip-path cuts away everything outside the shape *including* the
   element's own border and shadows, so the star had no edge at all: on
   art of its own colour it simply disappeared. Nothing added to the star
   itself could fix that - the edge has to be a second star behind it.

   The lower layer is the rim, and it is deliberately two-tone: pale down
   one side, dark down the other, the way a bevel is. A single-tone rim
   always has a background it vanishes against, and card art is every
   colour there is. */
.pip.special {
  width: 23px; height: 23px;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: none;
  filter: none;
  position: relative;
}
.pip.special::before,
.pip.special::after {
  content: '';
  position: absolute;
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%,
                     79% 91%, 50% 70%, 21% 91%, 32% 57%,
                     2% 35%, 39% 35%);
}
.pip.special::before {
  inset: 0;
  background: linear-gradient(150deg,
              rgba(255,255,255,.95) 0%, rgba(255,255,255,.95) 46%,
              rgba(0,0,0,.88) 54%, rgba(0,0,0,.88) 100%);
}
.pip.special::after {
  inset: 2px;
  background: var(--pip, var(--bone-dim));
}

/* Along the bottom edge of the card, wrapping upwards when a Mega is
   carrying more Energy than fits across. Centred, because a part-full last
   row hanging off one end reads as a mistake. */
.pokemon .face { position: relative; }
.pokemon .pips {
  position: absolute;
  left: 0; right: 0; bottom: 2px;
  display: flex;
  flex-wrap: wrap-reverse;
  justify-content: center;
  align-content: flex-end;
  gap: 2px;
  padding: 0 2px;
  z-index: 3;
  pointer-events: none;
}
.pokemon .pips .pip { pointer-events: auto; cursor: pointer; }

/* Over the card's own printed HP, which is where the eye already goes for
   it. Current HP only - the maximum is on the card and in the tooltip, and
   two numbers at this size means both are too small to read. */
.hp-badge {
  position: absolute;
  top: -5px; right: -5px;
  z-index: 4;
  min-width: 26px;
  padding: 1px 5px;
  border-radius: 9px;
  background: var(--ok);
  color: #10160f;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
  border: 1px solid rgba(0,0,0,.55);
  box-shadow: 0 1px 3px rgba(0,0,0,.5);
}
.hp-badge.hurt { background: var(--brass); color: #191308; }
.hp-badge.critical { background: var(--alert); color: var(--bone); }

/* Damage, conditions and static modifiers, to the right of the card. Built
   only when it has something in it, so an untouched Pokemon is exactly as
   wide as its card and the Bench keeps its slots per row. */
.slot-rail {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  max-width: 58px;
  padding-top: 4px;
}
/* A grey disc, not a red one. The HP badge already goes red when a Pokemon
   is in trouble, and two red badges on one card saying different things
   read as one alarm rather than two facts. Its own token, so the colour can
   be offered as a setting without hunting for it. */
.ctr {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  color: var(--counter-ink);
  background: var(--counter);
  min-width: 21px;
  height: 21px;
  padding: 0 5px;
  border-radius: 11px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(0,0,0,.5);
}
.slot-rail .status-tag { font-size: 10px; }
.slot-rail .buff { font-size: 9px; }

/* A row of a menu sheet is the whole button. A small Open at the end of
   each of six rows, a finger's width apart, is a mis-tap waiting to
   happen. Tall enough to be a comfortable target on a phone. */
.sheet-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 48px;
  margin: 0 0 8px;
  padding: 10px 14px;
  border: 1px solid var(--silk-dim);
  border-radius: 8px;
  background: rgba(255,255,255,.03);
  color: var(--bone);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}
.sheet-row:hover { border-color: var(--brass); background: rgba(255,255,255,.06); }
.sheet-row:active { background: rgba(255,255,255,.1); }
.sheet-row .chev { color: var(--silk); font-size: 18px; line-height: 1; }
.sheet-row.danger { border-color: rgba(200,80,63,.5); color: #f0b6ac; }
.sheet-row.danger:hover { border-color: var(--alert); }

/* A dot on whatever leads to the thing that is waiting. Positioned on the
   element rather than added to its text, so it cannot widen a header that
   has no room to spare. */
.has-waiting { position: relative; }
.has-waiting::after {
  content: "";
  position: absolute;
  top: -3px; right: -3px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--brass);
  border: 1px solid var(--felt-deep);
  pointer-events: none;
}
/* And the row itself, so the sorted-to-the-top games look sorted rather
   than shuffled. */
.list-item.waiting-on-you { border-left: 3px solid var(--brass); padding-left: 9px; }

/* The icon settings. The preview is the point of the whole section, so it
   gets the room and sits where the eye lands first. */
.icon-studio { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.icon-preview { display: flex; flex-direction: column; gap: 8px; align-items: center; }
.icon-big svg {
  display: block;
  border-radius: 20px;
  box-shadow: 0 0 0 1px rgba(147,176,164,.35), 0 6px 18px rgba(0,0,0,.4);
}
.icon-row { display: flex; gap: 10px; align-items: flex-end; }
.icon-cell { display: grid; justify-items: center; gap: 3px; }
.icon-cell svg { display: block; border-radius: 4px; }
.icon-cell em {
  font-family: var(--mono); font-style: normal;
  font-size: 9px; color: var(--silk);
}
.icon-marks { display: flex; gap: 6px; flex-wrap: wrap; }
.icon-mark {
  background: rgba(255,255,255,.03);
  border: 1px solid var(--silk-dim);
  border-radius: 9px;
  padding: 6px 5px 4px;
  cursor: pointer;
  display: grid; justify-items: center; gap: 3px;
  color: var(--silk); font-family: inherit; font-size: 9.5px;
}
.icon-mark svg { display: block; border-radius: 7px; }
.icon-mark.on { border-color: var(--brass); color: var(--bone); }
.icon-swatch { display: grid; justify-items: center; gap: 3px; font-size: 10px; color: var(--silk); }
.icon-swatch input {
  width: 42px; height: 28px; padding: 0; cursor: pointer;
  border: 1px solid var(--silk-dim); border-radius: 6px; background: none;
}
.pal-chip {
  width: 9px; height: 9px; border-radius: 50%;
  display: inline-block; margin-right: 5px;
  vertical-align: middle;
}

/* The table page as four pages, on a phone only. A desktop has the room
   to show all of it at once, so the picker is not drawn there and the
   attribute it sets is ignored - one page arranged two ways rather than
   two pages to keep in step. */
#lobby-section { display: none; }

@media (max-width: 620px) {
  #lobby-section {
    display: block;
    width: 100%;
    margin-bottom: 12px;
    font-size: 15px;
    padding: 10px 12px;
  }
  #view-lobby[data-part] .panel[data-part] { display: none; }
  #view-lobby[data-part="progress"]  .panel[data-part="progress"],
  #view-lobby[data-part="new"]       .panel[data-part="new"],
  #view-lobby[data-part="open"]      .panel[data-part="open"],
  #view-lobby[data-part="standings"] .panel[data-part="standings"],
  #view-lobby[data-part="tournaments"] .panel[data-part="tournaments"] {
    display: block;
  }
  /* Seasons keep their own hiding: the panel is empty until there are any,
     and a heading with nothing under it is worse than no heading. */
  #view-lobby[data-part="standings"] #tourney-panel.hidden { display: none; }
}

.status-tags { display: flex; gap: 3px; flex-wrap: wrap; justify-content: center; }
.status-tag {
  font-family: var(--mono);
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--alert);
  color: var(--bone);
}
.status-tag.asleep { background: #5a6f9c; }
.status-tag.paralyzed { background: var(--brass-dim); }
.status-tag.confused { background: #7d5a9c; }
.status-tag.poisoned { background: #5f8c4a; }

/* prizes: a stack of brass-edged face-down cards */
/* One pile, slightly fanned so it reads as a stack of cards, with what
   actually matters written on it. A taken Prize drops out of the stack
   rather than sitting in it greyed out, so the pile shrinks as they go. */
.prizes {
  position: relative;
  width: 34px;
  height: 44px;
  flex: 0 0 auto;
}
.prizes .prize {
  position: absolute;
  left: calc(var(--p, 0) * 1.4px);
  top: calc(var(--p, 0) * 1.4px);
}
/* A Prize already taken is not in the pile any more. */
.prizes .prize.taken { display: none; }
.prizes .prize-count {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 600;
  color: var(--bone);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .9);
  pointer-events: none;
  z-index: 10;
}
.prize {
  width: 26px; height: 36px;
  border-radius: 4px;
  background: repeating-linear-gradient(45deg, #2a4f6d 0 4px, #24455f 4px 8px);
  border: 1px solid var(--brass-dim);
  box-shadow: inset 0 0 0 1px rgba(217,164,65,.25);
}
.prize.taken { background: none; border-style: dotted; border-color: var(--silk-dim); box-shadow: none; }

.pile { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.pile .count { font-family: var(--mono); font-size: 12px; }
.pile .stub {
  width: 44px; height: 62px;
  border-radius: 5px;
  border: 1px dashed var(--silk-dim);
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 13px;
  cursor: pointer;
}
.pile .stub.deck { background: repeating-linear-gradient(45deg,#2a4f6d 0 5px,#24455f 5px 10px); border-style: solid; border-color: #14273a; }

/* --- hand ----------------------------------------------------------------- */

.hand-rail {
  border-top: 1px solid rgba(110,156,139,.22);
  background: rgba(0,0,0,.3);
  padding: 12px 16px;
  border-radius: 12px;
}
/* The side padding is not spacing: it is room for the ring on a picked
   card, which this would otherwise clip at either end. */
.hand { display: flex; gap: 6px; overflow-x: auto; padding: 6px 6px 10px; }
.hand .card { transition: transform .14s ease; }
.hand .card:hover { transform: translateY(-10px); }

/* --- side rail ------------------------------------------------------------ */

.rail { display: flex; flex-direction: column; gap: var(--gap); position: sticky; top: 78px; }

.turn-flag {
  border-radius: 10px;
  padding: 12px 14px;
  border: 1px solid var(--silk-dim);
  background: rgba(0,0,0,.24);
}
.turn-flag.yours { border-color: var(--brass); background: rgba(217,164,65,.12); }
.turn-flag .who { font-family: var(--display); font-size: 17px; }

/* scorekeeper's tape */
.tape {
  background: rgba(0,0,0,.32);
  border: 1px solid rgba(110,156,139,.22);
  border-radius: 10px;
  padding: 10px 12px;
  height: 340px;
  overflow-y: auto;
  display: flex;
  flex-direction: column-reverse;
}
.tape-line {
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.5;
  padding: 2px 0;
  color: var(--bone-dim);
  border-bottom: 1px dotted rgba(110,156,139,.16);
}
.tape-line .t { color: var(--silk-dim); margin-right: 6px; }
.tape-line.k-damage { color: #f0a99e; }
.tape-line.k-ko { color: var(--alert); font-weight: 600; }
.tape-line.k-prize, .tape-line.k-win { color: var(--brass); font-weight: 600; }
.tape-line.k-turn { color: var(--bone); font-weight: 600; border-bottom-color: var(--silk-dim); }
.tape-line.k-flip { color: #c9dcd2; }
/* Who did what, and why. A card the engine could not resolve, a card
   somebody chose to resolve themselves anyway, a player reaching in with
   Board tools, and an effect moving cards around on its own are four
   different facts — and reading a finished game to find out what the engine
   missed only works if they do not look alike. */
.tape-line.k-manual { color: #d9b9e0; }
.tape-line.k-override { color: #c3a7cc; font-style: italic; }
.tape-line.k-adjust { color: #cbb99a; }
.tape-line.k-move { color: var(--silk); }

.attack-list { display: flex; flex-direction: column; gap: 8px; }
.attack {
  text-align: left;
  border: 1px solid var(--silk-dim);
  background: rgba(0,0,0,.2);
  border-radius: 8px;
  padding: 10px 12px;
  cursor: pointer;
  width: 100%;
}
.attack:hover:not(:disabled) { border-color: var(--brass); }
.attack:disabled { opacity: .45; cursor: not-allowed; }
.attack .name { font-weight: 700; display: flex; justify-content: space-between; gap: 8px; }
.attack .dmg { font-family: var(--mono); color: var(--brass); }
.attack .cost { font-family: var(--mono); font-size: 10px; color: var(--silk); }
.attack .why { font-size: 11px; color: var(--alert); }
.attack .hand-flag { font-size: 10px; color: #d9b9e0; }

/* --- modal / prompt ------------------------------------------------------- */

.scrim {
  position: fixed;
  inset: 0;
  background: rgba(8,18,16,.78);
  display: grid;
  place-items: center;
  z-index: 100;
  padding: 20px;
}
.modal {
  background: var(--felt);
  border: 1px solid var(--silk);
  border-radius: 12px;
  padding: 20px;
  max-width: 720px;
  width: 100%;
  max-height: 84vh;
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(0,0,0,.55);
}
.modal.prompt { border-color: var(--brass); }

.option-grid { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.option {
  border: 1px solid var(--silk-dim);
  border-radius: 8px;
  padding: 10px 12px;
  background: rgba(0,0,0,.2);
  cursor: pointer;
  text-align: left;
}
.option:hover { border-color: var(--brass); }
/* What your Active can do, beside it. A column so the card keeps its own
   width, and narrow enough that the Bench underneath is unaffected. */
.active-actions {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-self: center;
  max-width: 230px;
  margin-left: 14px;
}
.act-opt {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12px;
  text-align: left;
  padding: 5px 9px;
  border-radius: 6px;
  border: 1px solid var(--silk-dim);
  background: rgba(0, 0, 0, .18);
  color: inherit;
  cursor: pointer;
}
.act-opt:hover:not(:disabled) { border-color: var(--brass); }
.act-opt:disabled { opacity: .45; cursor: default; }
.act-opt .num { margin-left: auto; color: var(--brass); }
.act-opt.atk b { font-weight: 600; }

.option:disabled { opacity: .5; cursor: default; }
.option:disabled:hover { border-color: var(--silk-dim); }
/* An attack is the widest thing in the list and wants its own line, so the
   name, the cost and the damage can sit apart from each other. */
.option.attack-opt { flex: 1 1 100%; }
.option.attack-opt .dmg { color: var(--brass); font-weight: 600; }
.option .why { font-size: 11px; color: var(--alert); margin-top: 3px; }
.option .hand-flag { font-size: 11px; color: var(--brass-dim); margin-top: 3px; }

/* --- deck builder --------------------------------------------------------- */

.builder { display: grid; grid-template-columns: minmax(0,1fr) 340px; gap: var(--gap); align-items: start; }
.card-results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 10px;
  max-height: 66vh;
  overflow-y: auto;
  /* Room for the ring on a picked card. This grid scrolls, so a tighter
     padding clips the top row's highlight rather than showing it. */
  padding: 8px;
}
.result { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.result .cap { font-size: 10px; text-align: center; color: var(--bone-dim); line-height: 1.2; }

/* No inner scrollbar. A sixty-card list is thirty-odd lines, and scrolling
   the page is what the wheel does anyway wherever the cursor happens to be. */
.deck-lines { display: flex; flex-direction: column; gap: 3px; }
.deck-line { display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 6px; border-radius: 5px; }
.deck-line:hover { background: rgba(0,0,0,.24); }
.deck-line .n { font-family: var(--mono); color: var(--brass); width: 22px; }
.deck-line .nm { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.verdict { font-size: 12.5px; line-height: 1.5; }
.verdict .err { color: #f0a99e; }
.verdict .warn { color: var(--brass); }
.verdict .good { color: var(--ok); }

.toast {
  position: fixed;
  bottom: 18px; left: 50%;
  transform: translateX(-50%);
  background: var(--felt-lift);
  border: 1px solid var(--brass);
  color: var(--bone);
  padding: 10px 18px;
  border-radius: 8px;
  z-index: 200;
  font-size: 13px;
  box-shadow: 0 10px 30px rgba(0,0,0,.5);
}

/* --- responsive ----------------------------------------------------------- */

@media (max-width: 1100px) {
  .table, .builder { grid-template-columns: minmax(0,1fr); }
  .rail { position: static; }
}
@media (max-width: 700px) {
  :root { --card-base-w: 58px; --card-base-h: 81px; }

  /* Nothing may push the page sideways. A phone that scrolls horizontally
     hides half of whatever you are reading. */
  html, body { overflow-x: hidden; }
  /* The shell is zoomed by the interface-size setting. Percentages are
     resolved in the zoomed element's own units and come out right on their
     own; vw is not, so a plain 100vw here is the screen's width measured in
     the wrong units, and at 0.9 it clamped the board to 90% of the screen
     and left a strip of empty table down the right. Above 1 it did the
     reverse and pushed the page sideways. */
  #app { max-width: calc(100vw / var(--ui-scale)); }
  .modal { padding: 14px; }
  .lobby-grid { grid-template-columns: minmax(0, 1fr); }
  .row { flex-wrap: wrap; }
  .field { max-width: 100%; }

  /* Admin rows carry five or six buttons; they belong under the text on a
     narrow screen rather than squeezed beside it. */
  #view-admin .list-row { flex-wrap: wrap; gap: 4px; }
  #view-admin .list-row .grow { flex-basis: 100%; }
  main { padding: 12px; }
  .side { gap: 8px; }
  /* Too much for one line on a phone: brand, tabs, a name and two buttons.
     Let it wrap rather than run off the side. */
  header.bar .bar-inner {
    flex-wrap: wrap;
    gap: 8px 10px;
    padding: 10px 12px;
  }
  header.bar .brand { font-size: 13px; }
  header.bar .grow { display: none; }
  header.bar .who {
    order: 3;
    width: 100%;
    justify-content: flex-start;
  }
  header.bar .who .field { width: auto; flex: 1 1 auto; min-width: 0; }
  header.bar .tabs { flex: 1 1 auto; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* Card art sits on top of a readable text face. If the image 404s or the
   machine is offline, app.js removes the <img> and the text shows through —
   so a card is always identifiable, never a blank rectangle. */
.card .fallback { position: absolute; inset: 0; }
.card img { position: relative; z-index: 1; }
.card.no-art img { display: none; }


/* --- readability ---------------------------------------------------------
   Card art at 150px is unreadable, which defeats the point of showing the
   real card. The detail view goes large and pulls the hi-res image; the rail
   carries a preview so you can read a card without opening anything. */

/* Three pieces: the picture, the vitals, and the card's own words.
   On a desktop the words sit beside the picture with the vitals under it.
   On a phone the picture is most of the width, so the words go underneath
   at full width and only the vitals stay beside it - which is the part you
   opened the card to read mid-game. */
.card-view {
  display: grid;
  grid-template-areas: "hero detail" "vitals detail";
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  gap: 12px 16px;
  align-items: start;
}
.card-view.plain { grid-template-areas: "hero detail"; grid-template-rows: auto; }
.card-view > .card-hero { grid-area: hero; }
.card-view > .card-vitals { grid-area: vitals; }
.card-view > .card-detail { grid-area: detail; min-width: 0; }

.card-vitals { display: flex; flex-direction: column; gap: 10px; }
.card-vitals .vital { display: flex; flex-direction: column; gap: 3px; }
.card-vitals .hpnow {
  font-family: var(--mono);
  font-size: 21px;
  line-height: 1.1;
  color: var(--ok);
}
.card-vitals .hpnow.hurt { color: var(--brass); }
.card-vitals .hpnow.critical { color: var(--alert); }
/* The maximum is context for the number in front of it, not a second
   number to read. */
.card-vitals .hpnow span { font-size: 12px; color: var(--silk); }
.card-vitals .vrow { display: flex; align-items: center; gap: 6px; }
.card-vitals .vname {
  font-size: 12px;
  color: var(--bone-dim);
  overflow-wrap: anywhere;
}
.card-vitals .vrow .num { font-family: var(--mono); font-size: 12px; }
.card-vitals .vnone { font-size: 12px; }
/* The counters sit beside the HP rather than under it: they are the same
   fact said the other way round, and reading them together is how you work
   out what survives the next attack. */
.card-vitals .vhp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.card-vitals .vtags { display: flex; flex-wrap: wrap; gap: 3px; }
/* The full word here, not the four-letter version the board uses - there is
   room for it, and "confused" is worth reading in full when you are about
   to attack with it. */
.card-vitals .status-tag { font-size: 10px; text-transform: capitalize; }
/* What the condition will actually do, which is a house rule rather than
   something you can look up. */
.card-vitals .vnote { font-size: 10.5px; line-height: 1.35; }
.card-vitals .veff {
  font-size: 11.5px;
  line-height: 1.3;
  color: var(--brass);
  padding: 3px 6px;
  border: 1px solid var(--brass-dim);
  border-radius: 5px;
  margin-bottom: 3px;
}
.card-vitals .vfrom { display: block; font-size: 10px; color: var(--silk); }

.card-hero { --card-w: 330px; --card-h: 462px; flex: 0 0 auto; }
.card-hero .card { box-shadow: 0 10px 30px rgba(0,0,0,.5); cursor: default; }
.card-hero .card:hover { transform: none; }
.card-hero .fallback { font-size: 15px; padding: 18px; }
.card-hero .fallback strong { font-size: 20px; }

.card-preview { --card-w: 100%; --card-h: auto; }
.card-preview .card { width: 100%; height: auto; aspect-ratio: 5 / 7; cursor: default; }
.card-preview .card:hover { transform: none; }

#preview { max-height: 70vh; overflow-y: auto; }
#preview .muted { font-size: 12.5px; }

.result { position: relative; }
.result .btn.tiny { padding: 2px 7px; font-size: 9px; }
/* A card that is a legal card but not a legal *second* pick - the other
   half of a "cards of different types" choice. Dimmed rather than hidden,
   because knowing it is there and why it cannot be taken is the point. */
.result.spent { opacity: .38; cursor: not-allowed; }
.result.spent .btn.tiny { opacity: 1; cursor: pointer; }

@media (max-width: 800px) {
  /* A 330px card is most of a phone screen; step it down but keep it legible. */
  /* The words move underneath, so the picture shares this row with the
     vitals rather than owning it. */
  .card-view {
    grid-template-areas: "hero vitals" "detail detail";
    grid-template-columns: auto minmax(0, 1fr);
  }
  .card-view.plain { grid-template-areas: "hero" "detail"; grid-template-columns: 1fr; }
  /* Small enough that the vitals beside it are readable rather than one
     word to a line. The picture is no longer carrying the text - that is
     underneath in real type now - so it can afford to be smaller. */
  .card-hero { --card-w: min(44vw, 300px); --card-h: auto; }
  /* Nothing beside it to make room for. */
  .card-view.plain .card-hero { --card-w: min(78vw, 300px); }
  .card-hero .card { width: var(--card-w); height: auto; aspect-ratio: 5 / 7; }
  #preview { display: none; }
}


/* The Stadium belongs to neither player, so it lives on the centre line —
   the one part of the mat that is shared. */
.mat .centerline span { max-width: 70%; white-space: nowrap; overflow: hidden;
                        text-overflow: ellipsis; }
.mat .centerline span:hover { color: var(--brass); }


/* Build badge: small, quiet, always visible. Knowing which build is running
   turns "is that fixed?" into something you can answer. */
.build-badge {
  font-family: var(--mono);
  font-size: 10px;
  padding: 3px 7px;
  border-radius: 5px;
  border: 1px solid var(--silk-dim);
  background: rgba(0,0,0,.25);
  color: var(--silk);
  cursor: pointer;
}
.build-badge:hover { color: var(--brass); border-color: var(--brass); }


/* A card mid-resolution. You reach Board tools from its prompt, so without
   this there is nothing on screen saying what you were in the middle of. */
.resolving {
  border: 1px solid var(--brass);
  background: rgba(217,164,65,.1);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 10px 0;
  font-size: 13px;
  line-height: 1.5;
}
.resolving strong { display: block; margin-bottom: 4px; }


/* The plan a scripted card will follow, shown before you commit to it. */
.plan {
  border: 1px solid var(--silk-dim);
  border-left: 3px solid var(--brass);
  border-radius: 0 8px 8px 0;
  background: rgba(0,0,0,.2);
  padding: 10px 12px;
  margin: 10px 0;
  font-size: 13px;
}
.plan ol { margin: 6px 0 0; padding-left: 20px; line-height: 1.6; }
.plan.manual { border-left-color: var(--silk); color: var(--bone-dim); }


/* --- attached Energy ------------------------------------------------------
   Colour carries the type and a superscript carries the count, so a glance
   tells you whether an attack is payable without opening the card. */
.pip { font-weight: 600; position: relative; }
.pip i { font-style: normal; font-size: 7px; position: absolute; top: -3px; right: -4px;
         background: var(--ink); color: var(--bone); border-radius: 50%;
         width: 10px; height: 10px; display: grid; place-items: center; }
/* Named rather than painted directly, because a star draws its colour in
   a layer of its own and cannot read a background off the box. */
.pip.e-grass     { --pip: #7fb069; }
.pip.e-fire      { --pip: #e0654a; }
.pip.e-water     { --pip: #6aa9d8; }
.pip.e-lightning { --pip: #e8c547; }
.pip.e-psychic   { --pip: #a97fc4; }
.pip.e-fighting  { --pip: #c98a4b; }
.pip.e-darkness  { --pip: #4a5568; color: var(--bone); }
.pip.e-metal     { --pip: #9aa5b1; }
.pip.e-fairy     { --pip: #e6a0c4; }
.pip.e-dragon    { --pip: #b8a145; }
.pip.e-colorless { --pip: #e4dfd2; }
/* Special Energy that names no type of its own - Double Turbo, Reversal.
   Its own shade, or a cream star sits invisible on pale card art. */
.pip.e-special { --pip: #bfb8cf; }
.pip.tool { --pip: var(--brass); }

/* --- Stadium --------------------------------------------------------------
   It belongs to neither player, so it sits on the centre line — but as an
   actual card you can read, not a caption. */
.stadium-zone {
  border: 1px dashed rgba(110,156,139,.45);
  border-radius: 8px;
  padding: 10px 12px 8px;
  margin: 4px auto 6px;
  max-width: 460px;
  position: relative;
  cursor: pointer;
}
.stadium-zone:hover { border-color: var(--brass); }
.stadium-zone > .eyebrow { position: absolute; top: -7px; left: 12px;
                           background: var(--felt); padding: 0 6px; }
.stadium-body { display: flex; gap: 12px; align-items: center; }
.stadium-body .card { --card-w: 52px; --card-h: 73px; }
.stadium-text { font-size: 12.5px; line-height: 1.45; }
.stadium-text .muted { font-size: 11.5px; }

@media (max-width: 700px) {
  .stadium-text .muted { display: none; }
}


/* A card you cannot legally play yet. Dimmed rather than hidden — you still
   need to see it is there, and hovering says why. */
.hand .unplayable .card { opacity: .42; filter: grayscale(.5); }
.hand .unplayable .card:hover { opacity: .75; }


/* Static modifiers, shown on the Pokemon they change. A Tool that silently
   alters a number is worse than no Tool at all. */
/* Capped to the card, so a long label wraps onto the next line instead of
   stretching the slot. Two of them side by side made one Pokemon's box
   visibly wider than its neighbours and threw the whole Bench row out. */
.hptext {
  max-width: var(--card-w);
  margin-left: auto;
  margin-right: auto;
}
.buff, .cond { max-width: 100%; overflow-wrap: anywhere; }
/* "120/280 · 16 ctr" is wider than the card it sits under, and a span that
   cannot wrap stretches the whole slot. This was making the Active box wider
   than every Pokemon beside it. */
.hptext { display: block; line-height: 1.25; }
.buff {
  font-family: var(--mono);
  font-size: 8.5px;
  padding: 0 4px;
  border-radius: 3px;
  background: rgba(217,164,65,.22);
  border: 1px solid var(--brass-dim);
  color: var(--bone);
}

/* What an attack would actually do, once Tools and Weakness are counted. */
.attack .dmg b { color: var(--ok); font-weight: 700; }
.attack .dmg b.lethal { color: var(--alert); }
.preview-steps {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--silk);
  margin-top: 3px;
}


/* A selected branch on a modal card. */
.option.selected {
  border-color: var(--brass);
  background: color-mix(in srgb, var(--brass) 14%, transparent);
}


/* A once-per-game power, and whether it is still available. */
.attack .cost b.once {
  color: var(--brass);
  font-size: 9px;
  border: 1px solid var(--brass-dim);
  border-radius: 3px;
  padding: 0 3px;
}


/* --- interface size ------------------------------------------------------
   `zoom` scales layout and text together and, unlike transform, keeps the
   document flow and scrollbars correct. Applied to the app shell rather than
   the root so overlays scale with it. */
#app, #overlay { zoom: var(--ui-scale); }

.scale-picker { display: flex; gap: 2px; margin-left: 6px; }
.scale-picker button {
  font-family: var(--mono);
  font-size: 10px;
  width: 20px;
  padding: 3px 0;
  border-radius: 4px;
  border: 1px solid var(--silk-dim);
  background: rgba(0,0,0,.25);
  color: var(--silk);
  cursor: pointer;
}
.scale-picker button:hover { color: var(--brass); border-color: var(--brass); }
.scale-picker button.on { color: var(--ink); background: var(--brass); border-color: var(--brass); }

/* The scale picker used to be hidden here, which meant a phone inherited
   whatever size had been chosen on a desktop and had no way to change it.
   Shrink it instead of removing it — that is the screen where it matters
   most. */
@media (max-width: 700px) {
  .scale-picker button { padding: 2px 5px; font-size: 9px; }
}


/* --- board layout --------------------------------------------------------
   Active centred, Bench centred beneath it. Prizes and piles sit at the outer
   edges so they never push the Pokemon off-centre. The flipped side mirrors
   vertically so both players read from their own edge inward. */
.side {
  display: grid;
  grid-template-columns: minmax(64px, auto) 1fr minmax(64px, auto);
  align-items: start;
  gap: 14px;
}
.side-centre {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.side.flip .side-centre { flex-direction: column-reverse; }

.side-centre .zone { width: 100%; }
.side-centre .zone-body { justify-content: center; flex-wrap: wrap; }
.side-centre .active-zone .zone-body { justify-content: center; }
.side-centre .bench .zone-body { gap: 8px; }

/* Compact Active area. The Active box stretches the full width of the
   centre column because .side-centre .zone sets width:100%, which the Bench
   needs and the Active does not - it holds one Pokemon and the rest is
   empty, pushing the Bench down the screen. */
.mat.compact-active .side-centre .active-zone {
  width: auto;
  min-width: calc(var(--card-w) + 90px);
  max-width: 100%;
  align-self: center;
  min-height: 0;
}
.mat.compact-active .side-centre .active-zone .zone-body {
  min-height: var(--card-h);
}

/* ...and the space it gives up has to go somewhere useful.

   The board is three columns and the middle one was 1fr - take everything
   going - so a narrower Active box simply left more empty middle, and the
   deck, discard and Prizes stayed pinned to the far edges. Sizing the middle
   column to its contents puts the slack in the outside columns instead, and
   aligning each edge towards the centre brings them in against the board.

   The Bench sets the width, so the board never becomes narrower than the
   widest row on it. */
.mat.compact-active .side {
  grid-template-columns: 1fr auto 1fr;
}
.mat.compact-active .side > .side-edge.left { justify-self: end; }
.mat.compact-active .side > .side-edge.right { justify-self: start; }

@media (max-width: 900px) and (min-width: 621px) {
  /* Between a phone and a desktop there is too little slack for moving the
     edges to be worth it, so compact only changes the Active box here.
     Below 621px the columns come from named areas and this would fight
     them - which is how compact came to do nothing at all on a phone. */
  .mat.compact-active .side { grid-template-columns: auto 1fr auto; }
  .mat.compact-active .side > .side-edge.left,
  .mat.compact-active .side > .side-edge.right { justify-self: auto; }
}

/* On a phone the Active row is as tall as the piles beside it - 266px of
   piles against 108px of Active. So compact turns each pile on its side:
   the stub, its count and its label on one line. */
@media (max-width: 620px) {
  .mat.compact-active .side-edge .piles { gap: 4px; }
  .mat.compact-active .pile {
    flex-direction: row;
    align-items: center;
    gap: 5px;
  }
  .mat.compact-active .pile .stub { width: 26px; height: 36px; font-size: 11px; }
  .mat.compact-active .pile .count { font-size: 11px; }
  .mat.compact-active .pile .eyebrow { font-size: 9px; letter-spacing: .06em; }
  /* Wider now that each pile is a row, so the Active gives up a little of
     its width in exchange for a much shorter row. */
  .mat.compact-active .side { grid-template-columns: auto minmax(0, 1fr) auto; }
}

.side-edge { display: flex; flex-direction: column; gap: 4px; }
.side-edge.right { align-items: flex-end; }
.side-edge .piles { display: flex; gap: 10px; padding-top: 6px; }
.side-edge .prizes { padding-top: 6px; }

@media (max-width: 900px) {
  .side { grid-template-columns: auto 1fr auto; gap: 8px; }
  /* The section heading goes; "deck", "discard" and "hand" stay. Losing
     those turned every pile into a bare number, and a bare number beside
     the discard reads as a second prize stack. */
  .side-edge > .eyebrow { display: none; }
}

/* A phone. Three columns cannot all be useful across 375 pixels: the edges
   held their width and the play area took what was left, which was one card
   wide. So the edges stop being columns and become a strip, and the board
   gets the lot. */
@media (max-width: 620px) {
  /* Prizes and piles flank the Active, which is one card wide; the Bench
     gets a row to itself. display:contents lifts the Active and the Bench
     out of their wrapper so all four can be placed as one grid. */
  .side {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "prizes active piles"
      "bench  bench  bench";
    gap: 6px;
    align-items: start;
  }
  /* The opponent's side reads from their Bench down to their Active, the
     way it sits across the table. */
  .side.flip {
    grid-template-areas:
      "bench  bench  bench"
      "prizes active piles";
  }
  .side-centre { display: contents; }
  .side-edge.left { grid-area: prizes; }
  .side-centre .active-zone { grid-area: active; }
  .side-centre .bench { grid-area: bench; }
  .side-edge.right { grid-area: piles; }
  .side-edge { flex-direction: column; align-items: center; gap: 4px; }
  /* Stacked, so the column stays about one card wide beside the Active. */
  .side-edge .piles { flex-direction: column; gap: 6px; padding-top: 0; }
  .side-edge .prizes { padding-top: 0; }
  /* The Bench is the thing that needed the room. */
  .side-centre .bench .zone-body { flex-wrap: wrap; gap: 6px; }
  .zone { padding: 6px; min-height: 0; }
  .mat { padding: 8px; }

  /* Two rows of size pickers and four buttons is most of a phone screen
     before anything of the game is on it. The sizes live in the header for
     quick reach on a desktop; on a phone they are worth the trip. */
  header.bar .scale-picker { display: none; }

  /* The Bench gap tightens too; six pixels between five cards is most of a
     card's width on a phone. */
  .side-centre .bench .zone-body { gap: 4px; }

  /* No room beside anything here. Tapping the Pokemon opens the same list. */
  .active-actions { display: none; }

  /* Full width and hard against the edge, so it reads as furniture rather
     than as another card on the board. */
  .act-bar { border-radius: 8px; padding: 7px 10px; gap: 8px; }
  .act-do { margin-left: auto; }
  .tchip { font-size: 10px; padding: 2px 6px; }

  /* The row of tools beside the hand is seven buttons wide. Behind one on a
     phone; the desktop row is fine as it is. */
  .hand-rail .row .btn.tiny:not(#more-tools) { display: none; }
  #more-tools { display: inline-block; }

  /* One row: the brand, the tabs, and a menu holding everything you do not
     touch mid-game. Three rows of header was most of a phone screen before
     any of the board was on it. */
  header.bar .who .field,
  header.bar #inbox,
  header.bar #account,
  header.bar #house-rules,
  header.bar #appearance,
  header.bar #sign-out,
  /* In the menu instead. The tabs want the width more than a full stop
     does, and on beta the server tag has already taken some of it. */
  header.bar #build-badge,
  header.bar .who .eyebrow { display: none; }
  header.bar #header-menu { display: inline-block; }
  /* The 700px rules give this block a full-width row of its own. With only
     the menu button left in it, it goes back to being one more button on
     the same line. */
  header.bar .who { order: 0; width: auto; flex: 0 0 auto; }
  header.bar .bar-inner {
    flex-wrap: nowrap;
    gap: 8px;
    padding: 6px 10px;
    /* One row that cannot wrap has to be a row that cannot overflow, or
       what is at the end of it leaves the screen instead. */
    overflow: hidden;
  }
  /* The spacer would take the slack the tabs need. */
  header.bar .grow { display: none; }
  /* Whatever is left after the brand and the menu, and the tabs scroll
     inside it. Five of them - Table, Decks, Profile, Game, Admin - come to
     285 pixels on their own, so on a phone something has to give, and it
     is better that the tabs slide than that the menu disappears. */
  header.bar .tabs {
    flex: 1 1 auto;
    min-width: 0;
    gap: 2px;
    overflow-x: auto;
    scrollbar-width: none;
  }
  header.bar .tabs::-webkit-scrollbar { display: none; }
  header.bar .tab { padding: 6px 8px; font-size: 11px; flex: 0 0 auto; }
  /* Pinned. Everything else can shrink; this is the way back to the rest
     of the site. */
  header.bar .who,
  header.bar #header-menu,
  header.bar .brand { flex: 0 0 auto; }
}

/* Off everywhere but a phone, and stated after the media query rather than
   before it - the reverse is how the More button stayed hidden. */
@media (min-width: 621px) {
  #more-tools, header.bar #header-menu { display: none; }
}


/* Splitting a pool of damage counters across several Pokemon. */
.spread { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; }
.spread-row {
  display: grid;
  grid-template-columns: 1fr auto 28px auto 70px;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border: 1px solid var(--silk-dim);
  border-radius: 6px;
  background: rgba(0,0,0,.18);
}
.spread-name { display: flex; flex-direction: column; min-width: 0; }
.spread-name strong { font-size: 13px; }
.spread-name .muted { font-size: 11px; }
.spread-count { text-align: center; font-size: 15px; color: var(--brass); }


/* Special Conditions, named on the Pokemon rather than left to a coloured dot. */
.cond {
  font-family: var(--mono);
  font-size: 8px;
  text-transform: uppercase;
  padding: 0 3px;
  border-radius: 3px;
  color: var(--ink);
}
.cond.c-asleep    { background: #9aa5b1; }
.cond.c-burned    { background: #e0654a; }
.cond.c-confused  { background: #c9a0dc; }
.cond.c-paralyzed { background: #e8c547; }
.cond.c-poisoned  { background: #7fb069; }


/* The handover step: deliberately prominent, because it is the last point at
   which a mistake can still be fixed. */
.btn.wide { width: 100%; padding: 10px; font-size: 15px; }


/* Something being prevented is why an attack did less than you expected, so
   it stands out rather than reading as an aside. */
.log .prevent { color: var(--brass); border-left: 2px solid var(--brass); padding-left: 6px; }


/* Search results say how many printings a card has, so a stack is obvious
   before you open it. */
.result .cap .arts {
  display: block;
  font-family: var(--mono);
  font-size: 9px;
  color: var(--brass);
  letter-spacing: .04em;
}
.card-detail .hand > div { cursor: pointer; }
.card-detail .card.selected { outline: 3px solid var(--brass); }


/* The build this tab is actually running, so a cached script is visible
   rather than something you deduce from behaviour. */
.brand #build-tag {
  font-family: var(--mono);
  font-style: normal;
  font-size: 9px;
  color: var(--bone-dim);
  margin-left: 6px;
  letter-spacing: .04em;
}

/* Which server this tab is talking to, beside the build it is running.
   Deliberately louder than the build number: reading a result off the wrong
   server is a worse mistake than running a stale script. */
.brand #server-tag {
  font-family: var(--mono);
  font-style: normal;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .08em;
  margin-left: 5px;
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid var(--brass-dim);
  color: var(--brass);
  vertical-align: 1px;
}

/* Anything that is not the live server reads as a warning, so you never take
   a beta result for a real one. */
.brand #server-tag:not([data-label="live"]) {
  border-color: var(--alert);
  color: var(--bone);
  background: var(--alert);
}


/* An ACE SPEC is limited to one per deck across all of them, and the card
   itself says so — the deck list should too. */
.deck-line .ace {
  font-family: var(--mono);
  font-size: 8px;
  color: var(--brass);
  border: 1px solid var(--brass);
  border-radius: 3px;
  padding: 0 3px;
  margin-left: 5px;
  vertical-align: middle;
}


/* Chat sits beside the log rather than in it. Same storage, separate boxes:
   mixing them makes the log harder to scan when you are working out why a
   card did what it did. */
.tape.chat { max-height: 26vh; }
.tape .chat-line { white-space: normal; line-height: 1.45; }
.tape .chat-line b { color: var(--brass); margin-right: 4px; }
.tape .chat-line.mine b { color: var(--bone-dim); }


/* Standings. Wins decide the order; the rate is context, not the ranking. */
#leaderboard .list-row { padding: 4px 2px; gap: 8px; font-size: 13px; }
#leaderboard .n { width: 18px; color: var(--brass); }
#leaderboard .rate { width: 44px; text-align: right; color: var(--bone-dim); }


/* Admin. Dense on purpose: these are lists you scan, not read.

   Striped, because the label sits at the left and the control that changes
   it at the right, with a lot of empty space between: on a wide screen it
   was genuinely hard to be sure which switch belonged to which setting.
   The banding carries the eye across, the rule under each row closes it
   off, and the hover tint confirms the row you are actually on. */
#view-admin .list-row, #leaderboard .list-row, #invites .list-row, #my-games .list-row {
  padding: 7px 8px;
  border-radius: 4px;
}
#view-admin .list-row:nth-child(odd),
#leaderboard .list-row:nth-child(odd),
#invites .list-row:nth-child(odd),
#my-games .list-row:nth-child(odd) { background: rgba(255,255,255,.035); }
#view-admin .list-row:hover,
#leaderboard .list-row:hover,
#invites .list-row:hover,
#my-games .list-row:hover { background: rgba(217,164,65,.09); }

#view-admin .list-row { gap: 8px; font-size: 13px;
                        /* --hairline was never defined, so this rule was
                           dropped and the rows have never had a separator. */
                        border-bottom: 1px solid rgba(110,156,139,.22); }
#view-admin .list-row:last-child { border-bottom: 0; }
#view-admin .btn.tiny { flex: 0 0 auto; }
/* --danger was never defined either, so admin errors have been rendering in
   the ordinary text colour rather than red. */
#view-admin .err { color: var(--alert); }
#admin-tabs .btn { flex: 0 0 auto; }
#admin-body { max-height: 68vh; overflow-y: auto; }


/* Reports read as short paragraphs rather than table rows, since the whole
   point is the sentence somebody wrote. */
#view-admin .list-row .muted[style*="pre-wrap"] { max-width: 62ch; }

#play-cam, #replay-cam {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 900;
  overflow: hidden;
}

#play-cam .cam-card, #replay-cam .cam-card {
  position: absolute;
  left: 50%;
  top: 78px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px 10px 10px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  border: 1px solid var(--brass-dim);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  animation: cam-in .28s ease-out both;
}
#play-cam .cam-card.going, #replay-cam .cam-card.going { animation: cam-out .3s ease-in both; }

#play-cam .cam-card img, #replay-cam .cam-card img {
  width: 86px;
  border-radius: 5px;
  display: block;
}
#play-cam .cam-card .cam-what, #replay-cam .cam-card .cam-what {
  font-size: 11px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--brass);
}
#play-cam .cam-card .cam-name, #replay-cam .cam-card .cam-name {
  font-size: 17px;
  font-weight: 600;
  margin-top: 2px;
  max-width: 240px;
}
#play-cam .cam-card .cam-who, #replay-cam .cam-card .cam-who {
  font-size: 12px;
  color: var(--muted);
  margin-top: 3px;
}
#play-cam .cam-card.ko, #replay-cam .cam-card.ko { border-color: var(--alert); }
#play-cam .cam-card.ko .cam-what, #replay-cam .cam-card.ko .cam-what { color: var(--alert); }

@keyframes cam-in {
  from { opacity: 0; transform: translate(-50%, -14px) scale(.96); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
@keyframes cam-out {
  from { opacity: 1; transform: translate(-50%, 0) scale(1); }
  to   { opacity: 0; transform: translate(-50%, -10px) scale(.98); }
}

/* Sitting in the strip above the board: placed from the board's own
   coordinates, and lifted clear of it by its own height. */
/* A marker on the Game tab, for when something is waiting behind it. */
#tabs .tab.needs-you::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--brass);
  vertical-align: middle;
}

#play-cam .cam-card.above, #replay-cam .cam-card.above {
  /* The strip between the header and the board is empty at every width, so
     this needs no measuring: it is pinned there and the board is never
     covered. Measuring the board and placing the bar from it was worse in
     every way - it moved with the scroll, and the position fought the
     transform the animation needs. */
  left: 50%;
  top: 58px;
  transform: translateX(-50%);
  animation-name: cam-in-above;
  padding: 6px 14px 6px 6px;
  gap: 10px;
}
#play-cam .cam-card.above.going, #replay-cam .cam-card.above.going { animation-name: cam-out-above; }
#play-cam .cam-card.above img, #replay-cam .cam-card.above img { width: 62px; }
#play-cam .cam-card.above .cam-name, #replay-cam .cam-card.above .cam-name { font-size: 19px; max-width: 400px; }
#play-cam .cam-card.above .cam-who, #replay-cam .cam-card.above .cam-who { display: inline; margin-left: 7px; }
#play-cam .cam-card.above .cam-what, #replay-cam .cam-card.above .cam-what { display: inline; margin-right: 2px; }

@keyframes cam-in-above {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes cam-out-above {
  from { opacity: 1; transform: translate(-50%, 0); }
  to   { opacity: 0; transform: translate(-50%, -6px); }
}

#play-cam .cam-hit, #replay-cam .cam-hit {
  position: absolute;
  font-family: var(--mono);
  font-size: 24px;
  font-weight: 700;
  color: var(--alert);
  text-shadow: 0 2px 6px rgba(0, 0, 0, .8);
  transform: translate(-50%, -50%);
  animation: cam-hit 1s ease-out both;
}
#play-cam .cam-hit.heal, #replay-cam .cam-hit.heal { color: var(--good, #6fd08c); }

@keyframes cam-hit {
  0%   { opacity: 0; transform: translate(-50%, -20%) scale(.7); }
  18%  { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
  70%  { opacity: 1; transform: translate(-50%, -95%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -130%) scale(.95); }
}

#play-cam .cam-ring, #replay-cam .cam-ring {
  position: absolute;
  border-radius: 8px;
  border: 2px solid var(--brass);
  animation: cam-ring .75s ease-out both;
}
#play-cam .cam-ring.hurt, #replay-cam .cam-ring.hurt { border-color: var(--alert); }

@keyframes cam-ring {
  from { opacity: .9; box-shadow: 0 0 0 0 currentColor; }
  to   { opacity: 0; transform: scale(1.06); }
}

/* Anyone who has asked their machine for less movement gets none of it. */
@media (prefers-reduced-motion: reduce) {
#play-cam .cam-card, #replay-cam .cam-card, #play-cam .cam-card.going, #replay-cam .cam-card.going, #play-cam .cam-hit, #replay-cam .cam-hit, #play-cam .cam-ring, #replay-cam .cam-ring { animation-duration: .01ms; }
}

/* An open deck is a thing your opponent can see in the log, so it should not
   be invisible to you. It used to be silently clickable and nothing else. */
.stub.deck.open {
  border-color: var(--brass);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--brass) 35%, transparent);
  cursor: pointer;
}

/* The opponent's hand: card backs, fanned, so it reads as cards held rather
   than another pile. The number underneath is the part that matters — hand
   size is public at a real table — but a blank dashed box said nothing. */
.pile .stub.hand-stub {
  border-style: solid;
  border-color: #14273a;
  background: repeating-linear-gradient(45deg, #2a4f6d 0 5px, #24455f 5px 10px);
  position: relative;
  cursor: default;
}
.pile .stub.hand-stub::before,
.pile .stub.hand-stub::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 5px;
  border: 1px solid #14273a;
  background: inherit;
}
.pile .stub.hand-stub::before { transform: rotate(-8deg) translateX(-2px); }
.pile .stub.hand-stub::after  { transform: rotate(7deg) translateX(2px); }

/* ------------------------------------------------- layered attachments
   The Pokemon sits on top at full size; its Energy and Tools sit under it
   at full size too, offset so a strip of each shows — the way they are
   tucked under a Pokemon on a real table. Tools go left and tilt so more
   of the card is readable; Energy goes right.

   A sliver is one card, which is the thing a pip can never be: it can be
   clicked and identified. */
.pokemon .stack { position: relative; display: inline-block; }
/* The Pokemon itself sits above everything tucked under it - that is the
   whole point of the layering, and only a sliver of each attached card
   should show. Numbered well clear of the tuck bands below (Energy in the
   20s, Tools in the 40s) so both can order within themselves. */
.pokemon .stack .face { position: relative; z-index: 50; }

.pokemon .tuck {
  position: absolute;
  /* Each card further from the Pokemon sits a little lower and behind the
     one before it, so the stack reads as running outwards from the
     Pokemon. Painted in DOM order it was the reverse: the card furthest
     away covered the rest. */
  top: calc(var(--i) * 3px);
  z-index: calc(20 - var(--i));
  transition: transform .12s ease-out;
}
/* The fan tightens after the fourth so a Pokemon carrying six Energy does
   not grow wider than the space it has. */
/* One-based: the first card has to peek out too. At zero it sat exactly
   under the Pokemon and was invisible. */
.pokemon .tuck.energy {
  left: calc((min(var(--i), 3) + 1) * 9px + max(var(--i) - 3, 0) * 3px);
}
.pokemon .tuck.tools {
  left: calc(((min(var(--i), 3) + 1) * 11px + max(var(--i) - 3, 0) * 4px) * -1);
  transform: rotate(-7deg);
  transform-origin: bottom right;
  /* Tools keep their own band above the Energy, and step back within it. */
  z-index: calc(40 - var(--i));
}
/* Hover deliberately does nothing. Cards shuffling themselves about as
   the pointer crosses the board was distracting, and there is nothing to
   read in a lifted sliver that clicking it does not show properly. */
.pokemon .tuck { cursor: pointer; }

/* The slot has to make room for what is tucked under it, or the slivers
   would sit on top of the Pokemon beside it. */
.pokemon.layered { padding: 0 26px; }

/* On a phone that room is the difference between a Bench of five on two
   rows and on four. The slivers tuck in tighter: they are a reminder that
   something is attached, and the card is still tapped to read it.
   Deliberately here rather than with the other phone rules - the plain rule
   above has the same specificity, so whichever comes last wins, and this
   one has to. */
@media (max-width: 620px) {
  .pokemon.layered { padding: 0 6px; }
}

/* A deck laid out as cards. Wide, because sixty cards want the room, and
   scrolling inside the dialog rather than growing it off the screen. */
.deck-gallery { max-width: 1080px; }
.deck-gallery .hand { gap: 6px; }
.deck-gallery .gal { cursor: pointer; position: relative; width: var(--card-w); }
/* The count leads the caption and is the one thing worth reading at a
   glance, so it gets the brass rather than the muted caption colour. */
/* A touch larger than the search results' captions: those are scanned for
   a picture, this is read for what is in the deck. */
.deck-gallery .gal .cap { max-width: var(--card-w); font-size: 11px; }
.deck-gallery .gal .cap b { color: var(--brass); margin-right: 2px; }
.deck-gallery .gal:hover .card { outline: 2px solid var(--brass); }

/* Tags on a deck you played against: what it is, and whether you already
   have it. Both are worked out from the cards, not stored, so they stay
   true when somebody renames a deck. */
.chip-tag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .04em;
  padding: 1px 6px;
  border-radius: 3px;
  border: 1px solid var(--silk-dim);
  color: var(--silk);
  white-space: nowrap;
}
.chip-tag.starred { border-color: var(--brass); color: var(--brass); }

/* --------------------------------------------------- dragging
   Sorting your hand, and dropping Energy onto a Pokemon. Both are additions
   to tapping, never replacements: drag is the one interaction that does not
   survive a touchscreen. */
#hand [data-hand] { cursor: grab; }
#hand [data-hand].dragging { opacity: .45; cursor: grabbing; }
#hand [data-hand].drop-before { box-shadow: -3px 0 0 0 var(--brass); }

.pokemon.drop-target .stack .face {
  outline: 2px solid var(--brass);
  outline-offset: 2px;
  border-radius: 6px;
}


/* --- binder --------------------------------------------------------------- */

/* The count controls sit under each card rather than on it, so the picture
   stays a picture and a mis-tap changes a number rather than opening a
   dialog. */
.bnd-ctl { display: flex; align-items: center; gap: 4px; }
.bnd-ctl .btn.tiny { padding: 2px 8px; font-size: 12px; line-height: 1.2; }
.bnd-search { max-height: 44vh; }

.build-list { display: flex; flex-direction: column; gap: 6px; }
.build-row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 8px 10px; border-radius: 6px;
  border: 1px solid rgba(255,255,255,.06);
  cursor: pointer;
}
.build-row:hover { background: rgba(0,0,0,.24); border-color: var(--brass-dim); }
.build-n {
  flex: 0 0 38px; text-align: center; font-size: 18px; font-weight: 600;
  color: var(--brass); padding-top: 2px;
}
.build-n.ready { color: var(--ok); }
.build-body { flex: 1; min-width: 0; }
.build-title { font-size: 13.5px; }
.build-title .muted { font-size: 12px; }
.build-sub { font-size: 12px; }
.build-more { font-size: 11.5px; padding: 0 0 4px 60px; }
