/* Forgetful Fish — game table layout (UI scaffold, no engine yet).
   Left rail: players + shared graveyard. Centre: battlefields + phase bar +
   hand. Right rail: game log + chat. Modelled on a typical MTG client. */

.ff {
	position: relative;   /* anchors the floating end-of-game window */
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	width: 100%;
}

/* ── Full-display mode ─────────────────────────────────────────────────────
   The game page drops the site navbar/footer (body.ff-fullscreen, set by the
   builder) so the table can fill the whole display. 100dvh tracks the real
   viewport (mobile browser chrome included); 100vh is the universal fallback,
   which also covers browsers that can't report the live viewport. */
body.ff-fullscreen #site-header,
body.ff-fullscreen #site-nav,
body.ff-fullscreen #site-aside,
body.ff-fullscreen #site-footer { display: none; }
body.ff-fullscreen main { padding: 0.45rem; }

/* Fit the whole game to the viewport (no page scroll) on any window wide enough
   for the side-by-side rails — not just large monitors. A narrow second window
   used to miss this and fall back to the fixed-height table below, which
   overflowed and pushed the hand off-screen. */
/* A definite height chain top-down (body -> main -> .ff -> table -> board ->
   fields -> battlefield), so the battlefield's aspect-ratio cells always resolve
   their `height: %` against a real height instead of growing from their width.
   Without this, on a narrow/short window the cards inflate and the board balloons
   off-screen. The whole game then fits the viewport with no page scroll. */
@media (min-width: 520px) {
	body.ff-fullscreen { height: 100vh; height: 100dvh; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
	body.ff-fullscreen main { flex: 1 1 auto; min-height: 0; display: flex; }
	body.ff-fullscreen .ff { flex: 1 1 auto; min-height: 0; }
	/* fill the leftover height under the top bar instead of the fixed calc(). */
	body.ff-fullscreen .ff-table { height: auto; flex: 1 1 auto; min-height: 0; }
}

/* In-game buttons are about half the height and length of the site default
   (which is ~0.6rem 0.95rem / 0.95rem). The `.ff .btn` descendant selector
   beats the base `.btn` regardless of stylesheet order. */
.ff .btn {
	padding: 0.2rem 0.5rem;
	font-size: 0.8rem;
	line-height: 1;
	border-radius: var(--radius-sm);
}

/* ── Top bar ──────────────────────────────────────────────────────────── */
.ff__topbar {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 0.45rem 0.85rem;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
}

.ff__title {
	font-weight: 700;
	letter-spacing: 0.02em;
}

.ff__game {
	font-size: 0.82rem;
	color: color-mix(in srgb, var(--default) 50%, transparent);
}

.ff__debug {
	margin-left: auto;
	background: none;
	border: 1px solid color-mix(in srgb, var(--default) 25%, transparent);
	border-radius: var(--radius-sm);
	color: color-mix(in srgb, var(--default) 75%, transparent);
	cursor: pointer;
	font-size: 0.78rem;
	padding: 0.2rem 0.6rem;
}

.ff__debug:hover {
	background: color-mix(in srgb, var(--default) 8%, transparent);
	color: var(--default);
}

.ff__leave {
	font-size: 0.82rem;
	color: var(--dark_blue);
	text-decoration: none;
}

/* The debug button (admin) takes the auto margin when present; otherwise the
   keybindings button (always present) is first of the right-hand group. */
.ff__topbar > .ff__keys:nth-child(3) { margin-left: auto; }

.ff__keys {
	background: none;
	border: 1px solid color-mix(in srgb, var(--default) 25%, transparent);
	border-radius: var(--radius-sm);
	color: color-mix(in srgb, var(--default) 75%, transparent);
	cursor: pointer;
	font-size: 0.78rem;
	padding: 0.2rem 0.6rem;
}
.ff__keys:hover {
	background: color-mix(in srgb, var(--default) 8%, transparent);
	color: var(--default);
}

.ff__leave:hover { text-decoration: underline; }

.ff__concede {
	background: none;
	border: 1px solid color-mix(in srgb, var(--danger) 50%, transparent);
	border-radius: var(--radius-sm);
	color: var(--danger);
	cursor: pointer;
	font-size: 0.78rem;
	padding: 0.2rem 0.6rem;
}
.ff__concede:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); }

/* ── Admin debug popup ────────────────────────────────────────────────── */
.ff-debug {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
}

.ff-debug[hidden] { display: none; }

.ff-debug__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.5);
}

.ff-debug__panel {
	position: relative;
	width: min(480px, 92vw);
	max-height: 80vh;
	display: flex;
	flex-direction: column;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 18%, transparent);
	border-radius: var(--radius-lg);
	box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
	overflow: hidden;
}

.ff-debug__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.7rem 1rem;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
}

.ff-debug__title { font-weight: 700; }

.ff-debug__close {
	background: none;
	border: none;
	cursor: pointer;
	font-size: 1.3rem;
	line-height: 1;
	color: color-mix(in srgb, var(--default) 60%, transparent);
}
.ff-debug__close:hover { color: var(--default); }

.ff-debug__body {
	padding: 1rem;
	overflow-y: auto;
}

.ff-debug__empty {
	margin: 0;
	color: color-mix(in srgb, var(--default) 50%, transparent);
	font-size: 0.9rem;
}

/* Keybindings modal — same overlay/panel chrome as the debug menu. */
.ff-keybinds {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
}
.ff-keybinds[hidden] { display: none; }
.ff-keybinds__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.5); }
.ff-keybinds__panel {
	position: relative;
	width: min(460px, 92vw);
	max-height: 80vh;
	display: flex;
	flex-direction: column;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 18%, transparent);
	border-radius: var(--radius-lg);
	box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
	overflow: hidden;
}
.ff-keybinds__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.7rem 1rem;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
}
.ff-keybinds__title { font-weight: 700; }
.ff-keybinds__close {
	background: none;
	border: none;
	cursor: pointer;
	font-size: 1.3rem;
	line-height: 1;
	color: color-mix(in srgb, var(--default) 60%, transparent);
}
.ff-keybinds__close:hover { color: var(--default); }
.ff-keybinds__body { padding: 0.5rem 1rem; overflow-y: auto; }
.ff-keybinds__footer {
	display: flex;
	justify-content: flex-end;
	padding: 0.6rem 1rem;
	border-top: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
}

.ff-keybinds__row {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.5rem 0;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 8%, transparent);
}
.ff-keybinds__row:last-child { border-bottom: none; }
.ff-keybinds__label { flex: 1 1 auto; font-size: 0.85rem; }
.ff-keybinds__hint {
	display: block;
	font-size: 0.72rem;
	color: color-mix(in srgb, var(--default) 50%, transparent);
}
.ff-keybinds__key {
	flex: 0 0 auto;
	min-width: 3.2rem;
	padding: 0.3rem 0.6rem;
	border: 1px solid color-mix(in srgb, var(--default) 30%, transparent);
	border-radius: var(--radius-sm);
	background: color-mix(in srgb, var(--default) 6%, var(--contents));
	color: var(--default);
	font-weight: 700;
	font-variant: small-caps;
	text-align: center;
	cursor: pointer;
}
.ff-keybinds__key:hover { border-color: var(--dark_blue); }
.ff-keybinds__key--capturing {
	border-color: var(--dark_blue);
	background: color-mix(in srgb, var(--dark_blue) 18%, transparent);
	font-weight: 400;
	font-variant: normal;
}

/* ── Table grid ───────────────────────────────────────────────────────── */
.ff-table {
	display: grid;
	/* left rail | battlefield | drag handle | log+chat rail (resizable width). */
	grid-template-columns: 190px minmax(0, 1fr) 3px var(--ff-rail-w, 300px);
	/* 6px gaps so the panels sit flush against the handle's ±6px grab zone
	   (no dead space beyond it). */
	gap: 6px;
	height: calc(100vh - 200px);
	min-height: 0;   /* the full-display override fills the height; never force overflow on short/narrow screens */
}

.ff-rail {
	display: grid;
	grid-template-columns: minmax(0, 1fr);   /* cap the column at the rail width so content clips, not widens */
	gap: 0.6rem;
	min-height: 0;
}

.ff-rail--left  { grid-template-rows: auto 1fr auto; }
/* card preview | log (resizable height) | drag handle | chat. The 6px row gaps
   keep the panels flush against the handle's grab zone, like the column handle
   above. */
.ff-rail--right { grid-template-rows: auto var(--ff-log-h, 1fr) 3px minmax(0, 1fr); gap: 6px; }

/* Drag handles that share space between the battlefield and the rail, and
   between the log and chat. The grid track (6px) is the hit area; a thin line
   shows in the middle and brightens on hover/drag. */
.ff-resize {
	align-self: stretch;
	justify-self: stretch;
	position: relative;
	background: transparent;
	transition: background 0.12s ease;
}
.ff-resize::before {       /* the thin visible line */
	content: "";
	position: absolute;
	inset: 0;
	margin: auto;
	background: color-mix(in srgb, var(--default) 16%, transparent);
	border-radius: 2px;
}
/* A wider invisible grab zone so the slim handle is still easy to catch. It
   overflows into the surrounding gap only (the panels sit further out), so it
   never steals clicks from the battlefield, log, or chat. */
.ff-resize::after { content: ""; position: absolute; }
.ff-resize--col { cursor: col-resize; }
.ff-resize--col::before { width: 2px; height: 40px; }
.ff-resize--col::after { top: 0; bottom: 0; left: -6px; right: -6px; }
.ff-resize--row { cursor: row-resize; }
.ff-resize--row::before { height: 2px; width: 40px; }
.ff-resize--row::after { left: 0; right: 0; top: -6px; bottom: -6px; }
.ff-resize:hover::before,
.ff-resize.ff-resize--active::before { background: color-mix(in srgb, var(--dark_blue) 70%, transparent); }
.ff.ff-resizing { user-select: none; cursor: inherit; }

/* Middle of the left rail: prompt (top half) + shared graveyard (bottom half). */
.ff-midrail {
	display: grid;
	grid-template-rows: 1fr 1fr;
	gap: 0.6rem;
	min-height: 0;
	min-width: 0;
}

.ff-prompt {
	min-height: 0;
	display: grid;
	/* messages centred in the top half, buttons centred in the bottom half
	   (renderPrompt regroups its children into the two halves) */
	grid-template-rows: 1fr 1fr;
	padding: 0.6rem;
	overflow: auto;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
}
.ff-prompt__half {
	min-height: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
}
.ff-prompt__msg {
	font-size: 0.88rem;
	text-align: center;
	line-height: 1.3;
}
/* The decision is yours: accent the prompt so the "what do I do now" surface
   stands out from the rest of the rail. */
.ff-prompt--attn {
	border-color: color-mix(in srgb, var(--dark_blue) 55%, transparent);
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--dark_blue) 35%, transparent),
	            0 0 12px color-mix(in srgb, var(--dark_blue) 18%, transparent);
}
.ff-prompt__hint {
	font-size: 0.68rem;
	opacity: 0.6;
	line-height: 1.3;
}
.ff-prompt__actions {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
}

/* ── Players (left rail) ──────────────────────────────────────────────── */
.ff-player {
	position: relative;   /* anchor for the floating life-delta badge */
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.2rem;
	padding: 0.85rem 0.6rem;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
}

.ff-player--opponent { border-top: 3px solid var(--danger-strong); }
.ff-player--you      { border-top: 3px solid var(--dark_blue); }

.ff-player__life {
	font-size: 2.1rem;
	font-weight: 800;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

.ff-player__name {
	font-weight: 600;
	font-size: 0.95rem;
}

.ff-player__counts {
	display: flex;
	gap: 0.6rem;
	margin-top: 0.25rem;
	font-size: 0.78rem;
	color: color-mix(in srgb, var(--default) 55%, transparent);
	font-variant-numeric: tabular-nums;
}

/* ── Shared graveyard (left rail, between players) ────────────────────── */
/* Graveyard + exile share a row, each a fanned pile. */
.ff-zones {
	display: flex;
	gap: 0.4rem;
	min-height: 0;
	min-width: 0;
}
.ff-zones > .ff-graveyard { flex: 1; min-width: 0; }

.ff-graveyard {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	min-height: 0;
	gap: 0.4rem;
	padding: 0.45rem;
	background: color-mix(in srgb, var(--default) 4%, var(--contents));
	border: 1px dashed color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	text-align: center;
	cursor: pointer;
}
.ff-graveyard:hover { border-color: color-mix(in srgb, var(--dark_blue) 60%, transparent); }

.ff-graveyard__title {
	font-size: 0.78rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: color-mix(in srgb, var(--default) 50%, transparent);
}

/* The stack and its thin hand-rolled scrollbar sit side by side. The native bars
   are off (overflow: hidden) and horizontal scroll is disallowed; the bar drives
   the vertical scroll. */
.ff-grave-body {
	display: flex;
	flex-direction: row;
	gap: 2px;
	flex: 1 1 auto;
	min-height: 0;
	overflow: hidden;
}
/* A downward-fanned stack: each card overlaps the one above it, leaving the name
   + a sliver of art showing (newest at the top). */
.ff-graveyard__stack {
	display: flex;
	flex-direction: column;
	align-items: center;
	flex: 1 1 auto;
	overflow: hidden;       /* scrolled via the hand-rolled bar; no thick native bars */
	min-height: 0;
	min-width: 0;
}
.ff-graveyard__stack .ff-graveyard__card {
	flex: 0 0 auto;
	width: 66px;
	height: 92px;           /* 5:7 */
	border-radius: 3px;
	border: 1px solid color-mix(in srgb, var(--default) 28%, transparent);
	background-size: contain;   /* whole card; only the top sliver is uncovered */
	background-position: top center;
}
.ff-graveyard__stack .ff-graveyard__card:not(:first-child) { margin-top: -78px; }   /* ~14px sliver (name) */
/* A graveyard card you can choose (e.g. Mystic Sanctuary). */
.ff-graveyard__card--pick { outline: 2px solid var(--selection); outline-offset: -1px; cursor: pointer; }

/* Hand-rolled vertical scrollbar for the graveyard/exile fans. */
.ff-grave-scroll {
	width: 5px;
	flex: 0 0 auto;
	align-self: stretch;
	border-radius: 3px;
	background: color-mix(in srgb, var(--default) 12%, transparent);
	position: relative;
	cursor: pointer;
	touch-action: none;
}
.ff-grave-scroll[hidden] { display: none; }
.ff-grave-scroll__thumb {
	position: absolute;
	left: 0;
	width: 100%;
	min-height: 18px;
	border-radius: 3px;
	background: color-mix(in srgb, var(--default) 42%, transparent);
}
.ff-grave-scroll__thumb:hover { background: color-mix(in srgb, var(--default) 62%, transparent); }
.ff-graveyard__empty {
	background:
		repeating-linear-gradient(135deg,
			color-mix(in srgb, var(--default) 9%, var(--contents)) 0 6px,
			color-mix(in srgb, var(--default) 5%, var(--contents)) 6px 12px) !important;
}

.ff-graveyard__count {
	font-size: 0.72rem;
	color: color-mix(in srgb, var(--default) 55%, transparent);
	font-variant-numeric: tabular-nums;
}

/* ── Centre: battlefields + phase bar + hand ──────────────────────────── */
.ff-main {
	display: grid;
	grid-template-rows: 1fr auto auto;   /* board, phase bar, hand */
	gap: 0.5rem;
	min-width: 0;
	min-height: 0;
}

/* The board = the (unified) battlefields + deck bar. The deck-reveal overlay
   is positioned over this whole area. */
.ff-board {
	position: relative;
	display: grid;
	grid-template-rows: auto 1fr auto;       /* opponent hand, fields, deck bar */
	grid-template-columns: minmax(0, 1fr);   /* let the single column shrink */
	gap: 0.5rem;
	min-width: 0;
	min-height: 0;
}

/* Opponent's hand: a one-line summary above the battlefield — the opponent's
   name, the names of any cards we know, then "+N unknown cards". */
.ff-ophand {
	display: flex;
	align-items: center;
	min-width: 0;
	gap: 0.6rem;
	padding: 0.3rem 0.7rem;
	font-size: 0.82rem;
}
.ff-ophand__name { flex: 0 0 auto; font-weight: 700; white-space: nowrap; }
.ff-ophand__content {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	line-height: 1.35;
	max-height: 2.7em;      /* wrap to at most two lines (fits a known 7-card hand) */
	padding-left: 0.6rem;
	border-left: 1px solid color-mix(in srgb, var(--default) 18%, transparent);
	color: color-mix(in srgb, var(--default) 70%, transparent);
}

/* Both battlefields share one rounded rectangle, split by a centre line. */
.ff-fields {
	display: grid;
	grid-template-rows: 1fr 1fr;
	min-height: 0;
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
	overflow: hidden;
}

/* Frosted deck-reveal panel: spans the fields + deck pane (inset slightly so
   their borders stay visible), blurring the battlefield behind it. Reused for
   in-game library reveals later. */
/* A moveable, resizable window: drag the header to move, drag the bottom-right
   corner to resize. Cards inside are laid out (and overlapped) to always fit —
   see layoutRevealGrid() — so there's no scrolling. */
.ff-reveal {
	position: absolute;
	left: 6%;
	top: 6%;
	width: 88%;
	height: 80%;
	min-width: 300px;
	min-height: 200px;
	resize: both;
	z-index: 30;
	display: flex;
	flex-direction: column;
	background: color-mix(in srgb, var(--contents) 55%, transparent);
	backdrop-filter: blur(9px) saturate(120%);
	-webkit-backdrop-filter: blur(9px) saturate(120%);
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
	overflow: hidden;
}

.ff-reveal[hidden] { display: none; }

.ff-reveal__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.4rem 0.75rem;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
	font-weight: 700;
	font-size: 0.85rem;
	cursor: move;            /* drag to move the window */
	user-select: none;
	touch-action: none;
}

.ff-reveal__close {
	background: none;
	border: none;
	cursor: pointer;
	font-size: 1.25rem;
	line-height: 1;
	color: color-mix(in srgb, var(--default) 60%, transparent);
}
.ff-reveal__close:hover { color: var(--default); }

.ff-reveal__grid {
	position: relative;      /* cards are absolutely positioned + sized to fit */
	flex: 1 1 auto;
	min-height: 0;
	overflow: hidden auto;   /* scroll vertically when overlapped rows don't all fit */
	scrollbar-gutter: stable;   /* steady width so the layout doesn't reflow when the bar shows */
}
.ff-reveal__grid > .ff-card-img,
.ff-reveal__grid > .ff-reveal-back {
	position: absolute;      /* placed + sized by layoutRevealGrid() */
}
/* Pick mode: choose a card from the deck to put in hand. */
.ff-reveal__pick { cursor: pointer; border-radius: 4px; box-shadow: 0 0 0 2px var(--selection); }
.ff-reveal__pick:hover { box-shadow: 0 0 0 3px var(--selection); }
.ff-reveal__pick--sel { box-shadow: 0 0 0 3px var(--selection); }

/* Reorder window: a moveable, stack-style panel (not a mask) so the board
   stays visible — the decision depends on game context. Drag the header to
   reposition; drag the cards to reorder. */
/* Fact or Fiction: the opponent split the revealed cards into two piles; take
   one to hand (the other goes to the graveyard). Same moveable-window look as
   the reorder/scry window. */
.ff-fof {
	position: absolute;
	left: 50%;
	top: 42%;
	transform: translate(-50%, -50%);
	z-index: 30;
	display: flex;
	flex-direction: column;
	max-width: 94%;
	background: color-mix(in srgb, var(--contents) 92%, transparent);
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lg);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	overflow: hidden;
}
.ff-fof[hidden] { display: none; }
.ff-fof__header {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--default) 8%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}
.ff-fof__body { padding: 0.7rem 0.8rem; }
.ff-fof__piles { display: flex; gap: 1rem; align-items: stretch; justify-content: center; }
.ff-fof__pile {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5rem;
	border: 1px dashed color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-sm);
}
.ff-fof__zonelabel {
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
}
.ff-fof__cards { display: flex; gap: 0.35rem; min-height: 134px; align-items: center; }
.ff-fof__cards .ff-card-img { height: 134px; width: auto; flex: 0 0 auto; }
.ff-fof__cards:empty::after {
	content: "(empty pile)";
	color: color-mix(in srgb, var(--default) 40%, transparent);
	font-size: 0.8rem;
	padding: 0 1.5rem;
}
.ff-fof__hint {
	margin-top: 0.6rem;
	text-align: center;
	font-size: 0.75rem;
	color: color-mix(in srgb, var(--default) 55%, transparent);
}
.ff-fof__actions { margin-top: 0.6rem; text-align: center; }

/* Name a card (Predict): a filter box over a scrollable list of the deck's card
   names — type to filter, click a name to choose it. */
.ff-name {
	position: absolute;
	left: 50%;
	top: 42%;
	transform: translate(-50%, -50%);
	z-index: 30;
	display: flex;
	flex-direction: column;
	width: 280px;
	max-width: 92%;
	background: color-mix(in srgb, var(--contents) 94%, transparent);
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lg);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	overflow: hidden;
}
.ff-name[hidden] { display: none; }
.ff-name__header {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--default) 8%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}
.ff-name__body { display: flex; flex-direction: column; padding: 0.5rem; gap: 0.4rem; }
.ff-name__filter {
	width: 100%;
	box-sizing: border-box;
	padding: 0.35rem 0.5rem;
	font-size: 0.85rem;
	border: 1px solid color-mix(in srgb, var(--default) 25%, transparent);
	border-radius: var(--radius-sm);
	background: var(--contents);
	color: var(--default);
}
.ff-name__list {
	max-height: 240px;
	overflow-y: auto;
	border: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
	border-radius: var(--radius-sm);
}
.ff-name__item {
	padding: 0.35rem 0.6rem;
	font-size: 0.85rem;
	cursor: pointer;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 8%, transparent);
}
.ff-name__item:last-child { border-bottom: none; }
.ff-name__item:hover { background: color-mix(in srgb, var(--dark_blue) 16%, transparent); }
/* Predict: the name matching the known top card is the guaranteed-hit pick —
   fill it with a gradient so it stands out in the list. */
.ff-name__item--predict {
	background: linear-gradient(90deg,
		color-mix(in srgb, var(--player-0-color) 45%, transparent) 0%,
		color-mix(in srgb, var(--player-0-color) 14%, transparent) 100%);
	font-weight: 700;
}
.ff-name__item--predict:hover {
	background: linear-gradient(90deg,
		color-mix(in srgb, var(--player-0-color) 58%, transparent) 0%,
		color-mix(in srgb, var(--player-0-color) 24%, transparent) 100%);
}
.ff-name__empty {
	padding: 0.5rem 0.6rem;
	font-size: 0.8rem;
	color: color-mix(in srgb, var(--default) 45%, transparent);
}

/* Text-change picker ("turn X to Y") — same floating window chrome as .ff-name. */
.ff-textchange {
	position: absolute;
	left: 50%;
	top: 42%;
	transform: translate(-50%, -50%);
	z-index: 30;
	display: flex;
	flex-direction: column;
	width: 300px;
	max-width: 92%;
	background: color-mix(in srgb, var(--contents) 94%, transparent);
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lg);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	overflow: hidden;
}
.ff-textchange[hidden] { display: none; }
.ff-textchange__header {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--default) 8%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}
.ff-textchange__body { display: flex; flex-direction: column; padding: 0.5rem; gap: 0.5rem; }
.ff-textchange__cols { display: flex; gap: 0.5rem; }
.ff-textchange__col { flex: 1; display: flex; flex-direction: column; gap: 0.25rem; }
.ff-textchange__label {
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: color-mix(in srgb, var(--default) 50%, transparent);
}
.ff-textchange__list {
	border: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
	border-radius: var(--radius-sm);
}
.ff-textchange__item {
	padding: 0.35rem 0.6rem;
	font-size: 0.85rem;
	cursor: pointer;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 8%, transparent);
}
.ff-textchange__item:last-child { border-bottom: none; }
.ff-textchange__item:hover { background: color-mix(in srgb, var(--dark_blue) 12%, transparent); }
.ff-textchange__item--sel,
.ff-textchange__item--sel:hover {
	background: color-mix(in srgb, var(--dark_blue) 30%, transparent);
	font-weight: 700;
}
.ff-textchange__preview {
	text-align: center;
	font-size: 0.82rem;
	color: color-mix(in srgb, var(--default) 70%, transparent);
}
.ff-textchange__confirm { align-self: stretch; }
.ff-textchange__confirm:disabled { opacity: 0.5; cursor: default; }

.ff-reorder {
	position: absolute;
	left: 50%;
	top: 42%;
	transform: translate(-50%, -50%);
	z-index: 30;
	display: flex;
	flex-direction: column;
	max-width: 92%;
	background: color-mix(in srgb, var(--contents) 92%, transparent);
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lg);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	overflow: hidden;
}
.ff-reorder[hidden] { display: none; }
.ff-reorder__header {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--default) 8%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}
.ff-reorder__body { padding: 0.7rem 0.8rem; text-align: center; }
/* Top-of-library and (for scry) bottom-of-library sit side by side. */
.ff-reorder__zones { display: flex; gap: 1rem; justify-content: center; align-items: flex-start; }
.ff-reorder__zone { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.ff-reorder__zonelabel {
	font-size: 0.62rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 55%, transparent);
}
.ff-reorder__zonelabel[hidden] { display: none; }
.ff-reorder__cards, .ff-reorder__slots { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; }
/* Two-class specificity so cards keep their size in EITHER container (a card
   dragged into the bottom must not stretch to fill it). */
.ff-reorder__zones .ff-reorder__card {
	flex: 0 0 auto;
	width: 96px;
	aspect-ratio: 5 / 7;
	border-radius: 5px;
	background-size: contain;
	cursor: grab;
}
.ff-reorder__card.ff-dragging { opacity: 0.5; }
.ff-reorder__placeholder {
	flex: 0 0 auto;
	width: 96px;
	aspect-ratio: 5 / 7;
	border: 2px dashed color-mix(in srgb, var(--default) 35%, transparent);
	border-radius: 5px;
	pointer-events: none;
}
.ff-reorder__hint { font-size: 0.74rem; opacity: 0.6; margin: 0.6rem 0; }
.ff-reorder__actions { display: flex; gap: 0.5rem; justify-content: center; }
.ff-reorder__actions .btn[hidden] { display: none; }   /* .btn display otherwise beats [hidden] */
.ff-reorder__bottom { padding-left: 1rem; border-left: 1px solid color-mix(in srgb, var(--default) 18%, transparent); }
.ff-reorder__bottom[hidden] { display: none; }
.ff-reorder__zone[hidden] { display: none; }
/* Brainstorm (putback): the hand pool zone is hidden — only the slots show, so
   drop the divider that separated the two zones. The zone label runs sideways
   down the left edge (same treatment as the deck bar's "Top of Deck"). */
.ff-reorder--putback .ff-reorder__bottom {
	padding-left: 0;
	border-left: none;
	flex-direction: row;
	align-items: stretch;
	gap: 0.5rem;
}
.ff-reorder--putback .ff-reorder__bottom .ff-reorder__zonelabel {
	writing-mode: vertical-rl;
	display: flex;
	align-items: center;
	justify-content: center;
	letter-spacing: 0.14em;
}
/* The hint used to space the slots off the actions row; without it the Confirm
   button crowds the dotted outlines. */
.ff-reorder--putback .ff-reorder__actions { margin-top: 0.8rem; }

.ff-debug__actions {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	align-items: stretch;
}

/* Shared-library bar — spans the board width, sits just above the phase bar.
   The deck is the centre of this format, so it's given prominence. */
.ff-deckbar {
	position: relative;
	min-width: 0;
	padding: 0.75rem 1rem;
	background: color-mix(in srgb, var(--default) 12%, var(--contents));
	border: 1px solid color-mix(in srgb, var(--default) 18%, transparent);
	border-radius: var(--radius-md);
	isolation: isolate;   /* contain the spread cards' z-index below the reveal */
}
.ff-deckbar__expanded {
	display: flex;
	align-items: center;
	gap: 0.55rem;
	cursor: pointer;   /* the whole bar is a click target to collapse it */
}

/* Small down-triangle handle at the bottom centre to collapse the deck bar. */
.ff-deckbar__collapse {
	position: absolute;
	bottom: -2px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 5;
	background: none;
	border: none;
	cursor: pointer;
	padding: 0 14px;
	line-height: 1;
	font-size: 0.7rem;
	color: color-mix(in srgb, var(--default) 55%, transparent);
}
.ff-deckbar__collapse:hover { color: var(--default); }

/* Collapsed one-line view: "Deck: x | known card names | View deck". */
.ff-deckbar__collapsed {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	min-width: 0;
	cursor: pointer;
	font-size: 0.82rem;
}
.ff-deckbar__cline { font-weight: 600; white-space: nowrap; }
.ff-deckbar__known {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	color: color-mix(in srgb, var(--default) 65%, transparent);
}
.ff-deckbar__view { flex: 0 0 auto; }

/* Collapsed bar matches the turn-phase bar's height. */
.ff-deckbar--collapsed { padding: 0.3rem 0.9rem; }
.ff-deckbar--collapsed .ff-deckbar__expanded { display: none; }
.ff-deckbar:not(.ff-deckbar--collapsed) .ff-deckbar__collapsed { display: none; }

.ff-deckbar__deck {
	display: flex;
	flex-direction: column;
	align-items: center;
	line-height: 1;
}

.ff-deckbar__count {
	font-size: 2.6rem;
	font-weight: 800;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

.ff-deckbar__caption {
	margin-top: 0.2rem;
	font-size: 0.66rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 50%, transparent);
}

.ff-deckbar__label {
	writing-mode: vertical-rl;
	align-self: stretch;
	display: flex;
	align-items: center;
	font-size: 0.66rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	color: color-mix(in srgb, var(--default) 45%, transparent);
}

/* Spread of the whole library: a left-over-right fan across the deck bar.
   Cards are absolutely positioned by JS (left offset + descending z-index). */
.ff-deckbar__spread {
	position: relative;
	flex: 1;
	align-self: stretch;
	min-width: 0;
}
.ff-deckbar__spread .ff-deck-spread__card {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: auto;
	height: 4.5rem;
	filter: drop-shadow(2px 2px 3px rgba(0, 0, 0, 0.5));   /* volume on the fan */
}
/* Unknown positions: a card back sized like the face-up spread cards. */
.ff-deckbar__spread .ff-deck-spread__card--back {
	aspect-ratio: 2.5 / 3.5;
	background: url(/static/img/forgetful_fish_cardback.svg) center / cover no-repeat;
}

/* A face-down slot in the deck reveal (a position the player doesn't know). */
.ff-reveal-back {
	width: 100%;
	aspect-ratio: 2.5 / 3.5;
	background: url(/static/img/forgetful_fish_cardback.svg) center / cover no-repeat;
}

.ff-battlefield {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	padding: 1.4rem 0.5rem 0.5rem;   /* top padding clears the corner hint */
	overflow: auto;
	background: color-mix(in srgb, var(--default) 5%, var(--contents));
	min-height: 0;
}
/* The centre dividing line between the two halves. */
.ff-battlefield--opponent { border-bottom: 1px solid color-mix(in srgb, var(--default) 16%, transparent); }

/* Two rows per battlefield: lands (centred) and other permanents. DOM order
   gives lands at your lower centre / the opponent's upper centre. */
/* The battlefield is split into two equal halves (creatures + lands); each
   row centres its cards within its half so the rows never overlap. */
.ff-bf__row {
	display: flex;
	flex: 1 1 0;
	flex-wrap: nowrap;
	justify-content: center;
	align-items: center;
	gap: 5px;            /* a few px so cells never touch */
	min-height: 0;
	overflow-x: auto;
	overflow-y: hidden;
}

/* Cards sit in a square cell sized to (most of) the half, with the 5:7 image
   centred, so a tapped card rotates 90deg in place without overlapping its
   neighbours or the other row. Two-class selector beats .ff-card-img. */
.ff-battlefield .ff-bf__card {
	position: relative;
	flex: 0 0 auto;
	height: 92%;
	aspect-ratio: 1;
	width: auto;
	background-size: contain;
}
.ff-battlefield--you .ff-bf__card { cursor: pointer; }

/* Tapped permanents (e.g. lands tapped for mana) lie sideways, resting on the
   bottom of their square cell rather than pivoting in the middle. The card art
   is contain-fit (full height) so rotating leaves a ~1/7 gap top and bottom;
   translating down by that gap drops it to the bottom edge. */
.ff-battlefield .ff-bf__card.ff-tapped { transform: translateY(14.3%) rotate(90deg); }

/* A pile of 3+ same-name lands, overlapped to save space (left card on top of
   the right; the overlap is set live by JS to scale with the card size).
   align-self: stretch gives the cards' % height a definite height to resolve. */
.ff-bf__landpile {
	flex: 0 0 auto;
	align-self: stretch;
	display: flex;
	align-items: center;
	cursor: pointer;
}

/* Combat markers: attacking (red), selected (blue), blocking (green); the
   numbered badge pairs blockers with the attacker they block. The ring is
   drawn by a pseudo-element inset to hug the 5:7 card inside its square cell
   (the image is contain-fit, so it occupies the middle ~71% of the width). */
.ff-bf__card.ff-combat-atk::after,
.ff-bf__card.ff-combat-sel::after,
.ff-bf__card.ff-combat-block::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 14.3%;
	right: 14.3%;
	border-radius: 3px;
	pointer-events: none;
}
.ff-bf__card.ff-combat-atk::after { box-shadow: 0 0 0 2px var(--danger-strong), 0 0 6px var(--danger-strong); }
.ff-bf__card.ff-combat-sel::after { box-shadow: 0 0 0 2px var(--selection), 0 0 6px var(--selection); }
.ff-bf__card.ff-combat-block::after { box-shadow: 0 0 0 2px var(--success), 0 0 6px var(--success); }
/* A blocker slid sideways to stand directly opposite its attacker across the
   centre line (alignCombatPairs sets the translate). Raised above its
   neighbours; the slide eases when the pairing changes in place. */
.ff-bf__card.ff-combat-pull {
	z-index: 5;
	transition: translate 0.15s ease;
}
@media (prefers-reduced-motion: reduce) {
	.ff-bf__card.ff-combat-pull { transition: none; }
}
/* Summoning sickness: a dizzy overlay centred on the creature, as wide as the
   contain-fit card art (the middle ~71.4% of the square cell) and semi-transparent
   so the art still reads. Sits above the art but below combat badges (z 4). The
   black line-art is inverted to white on the dark theme so it stays visible. */
.ff-bf__card .ff-card-sick {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 71.43%;
	transform: translate(-50%, -50%);
	opacity: 0.7;
	pointer-events: none;
	z-index: 3;
}
html[data-theme="dark"] .ff-bf__card .ff-card-sick { filter: invert(1); }

.ff-combat-badge {
	position: absolute;
	top: -2px;
	left: 50%;
	transform: translateX(-50%);
	min-width: 14px;
	height: 14px;
	padding: 0 3px;
	border-radius: 8px;
	background: var(--danger-strong);
	color: #fff;
	font-size: 9px;
	font-weight: 700;
	line-height: 14px;
	text-align: center;
	z-index: 4;
	pointer-events: none;
}

/* Mana available (untapped lands): your lower-left, opponent's upper-right. */
.ff-bf__mana {
	position: absolute;
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;                 /* space between per-colour chips */
}
.ff-bf__mana--you { bottom: 0.45rem; left: 0.5rem; }
.ff-bf__mana--opp { top: 0.45rem; right: 0.5rem; }
.ff-bf__mana[hidden] { display: none; }

/* One chip per mana colour: a pip + its count. */
.ff-mana__chip {
	display: inline-flex;
	align-items: center;
	gap: 0.28rem;
	padding: 0.08rem 0.45rem;
	border-radius: var(--radius-pill);
	background: color-mix(in srgb, var(--default) 9%, var(--contents));
	border: 1px solid color-mix(in srgb, var(--default) 16%, transparent);
	font-weight: 700;
	font-size: 0.82rem;
	font-variant-numeric: tabular-nums;
}
.ff-mana__chip--clickable { cursor: pointer; }
.ff-mana__chip--clickable:hover {
	border-color: var(--dark_blue);
	filter: brightness(1.06);
}

.ff-battlefield--opponent { background: color-mix(in srgb, var(--danger-strong) 5%, var(--contents)); }
.ff-battlefield--you      { background: color-mix(in srgb, var(--dark_blue) 6%, var(--contents)); }

.ff-battlefield__hint {
	position: absolute;
	top: 0.5rem;
	left: 0.65rem;
	font-size: 0.72rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: color-mix(in srgb, var(--default) 35%, transparent);
}

/* Phase bar */
.ff-phasebar {
	display: flex;
	align-items: stretch;
	gap: 1px;
	padding: 0 0.2rem;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
	overflow: hidden;          /* clip the highlight as it loops off the edges */
	position: relative;        /* anchor for the sliding active-phase highlight */
}

/* A single blue pill that slides between phases (its left/width are set live to
   the active phase) instead of the highlight jumping from cell to cell. */
.ff-phase__highlight {
	position: absolute;
	border-radius: var(--radius-sm);
	background: color-mix(in srgb, var(--dark_blue) 16%, transparent);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dark_blue) 45%, transparent);
	transition: left 0.2s ease, width 0.2s ease;
	pointer-events: none;
	z-index: 0;
}
.ff-phase__highlight[hidden] { display: none; }

.ff-phasebar__turn {
	flex: 0 0 9.5rem;          /* fixed allocation so the phases never shift */
	align-self: stretch;
	display: flex;
	align-items: center;
	position: relative;
	z-index: 2;                /* hide the looping highlight as it crosses back to the left */
	background: var(--contents);
	overflow: hidden;
	text-overflow: ellipsis;   /* truncate a long name with an ellipsis */
	white-space: nowrap;
	padding: 0.18rem 0.7rem;
	margin-right: 0.3rem;
	font-size: 0.78rem;
	font-weight: 700;
	border-right: 1px solid color-mix(in srgb, var(--default) 15%, transparent);
}

.ff-phase {
	position: relative;             /* anchor for the edge-pinned triangles */
	z-index: 1;                     /* text/triangles sit above the sliding highlight */
	display: flex;
	flex: 1;                        /* phases spread evenly across the bar */
	align-items: center;
	justify-content: center;
	padding: 7px 0.3rem;            /* room for the triangles at top/bottom */
	font-size: 0.72rem;
	white-space: nowrap;
	color: color-mix(in srgb, var(--default) 55%, transparent);
	border-radius: var(--radius-sm);
	cursor: pointer;
	user-select: none;
	transition: color 0.2s ease;
}
.ff-phase--nostop { cursor: default; }

/* The blue background/border now lives on .ff-phase__highlight, which slides; the
   active phase only changes its text colour/weight. */
.ff-phase--active {
	color: var(--default);
	font-weight: 700;
}

/* Stop triangles: a small SVG pinned flush to the bar's edges (top = opponent
   pointing down, bottom = you pointing up). Hollow outline when no stop is set
   (evidently empty), solid fill when a stop is set. */
.ff-phase__stop {
	position: absolute;
	left: 50%;
	width: 12px;
	height: 6px;
	transform: translateX(-50%);
	line-height: 0;
	color: color-mix(in srgb, var(--default) 50%, transparent);
}
.ff-phase__stop[data-ff-stop="theirs"] { top: 0; }
.ff-phase__stop[data-ff-stop="mine"]   { bottom: 0; }
.ff-phase__stop svg { display: block; width: 12px; height: 6px; overflow: visible; }
.ff-phase__stop polygon { fill: none; stroke: currentColor; stroke-width: 1.2; }
.ff-phase__stop--set { color: var(--default); }
.ff-phase__stop--set polygon { fill: currentColor; }
/* The yield-until-here marker: the target triangle turns solid yellow until
   that phase is reached (then the engine clears it). */
.ff-phase__stop--yield { color: var(--warning); }
.ff-phase__stop--yield polygon { fill: var(--warning); stroke: var(--warning); }
.ff-phase__label { line-height: 1.1; }

/* Pop-up menu for a phase: opponent / you, each with Set stop + Yield here. */
.ff-phasemenu {
	position: fixed;
	z-index: 60;
	min-width: 150px;
	padding: 0.25rem;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 20%, transparent);
	border-radius: var(--radius-sm);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.ff-phasemenu__section + .ff-phasemenu__section {
	margin-top: 0.2rem;
	padding-top: 0.2rem;
	border-top: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
}
.ff-phasemenu__name {
	padding: 0.15rem 0.4rem;
	font-weight: 700;
	font-size: 0.72rem;
	color: color-mix(in srgb, var(--default) 70%, transparent);
}
.ff-phasemenu__item {
	display: block;
	width: 100%;
	text-align: left;
	padding: 0.25rem 0.4rem;
	background: none;
	border: none;
	border-radius: var(--radius-sm);
	color: var(--default);
	font-size: 0.8rem;
	cursor: pointer;
}
.ff-phasemenu__item:hover { background: color-mix(in srgb, var(--dark_blue) 22%, transparent); }
.ff-phasemenu__item:disabled:hover { background: none; }
.ff-cardmenu__item--off { opacity: 0.4; cursor: default; }

/* Hand */
.ff-hand {
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 0.4rem;
	min-height: 150px;
	padding: 0.4rem;
	background: color-mix(in srgb, var(--default) 4%, var(--contents));
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
	overflow-x: auto;
}

/* Hand cards: reuse the theme-switching card image, sized to fill the hand.
   Two-class selector so it beats `.ff-card-img { width: 100% }`. */
.ff-hand .ff-hand__card {
	position: relative;
	flex: 0 0 auto;
	width: auto;
	height: 134px;
	cursor: pointer;
	transition: transform 0.08s ease;
}
.ff-hand .ff-hand__card:hover { transform: translateY(-6px); }

/* Selecting cards to put on the bottom (mulligan). */
.ff-card-img.ff-bottom-sel { outline: 3px solid var(--dark_blue); outline-offset: -2px; filter: brightness(0.62); }

/* Targeting (casting) is highlight-free — legal targets just take a click
   cursor, and choosing all targets casts at once. */
.ff-targeting .ff-card-img.ff-target-legal { cursor: pointer; }
/* Hovering a stack object red-outlines the cards it targets (anywhere on the
   board — battlefield or the stack). The hovered spell isn't the target, so
   there's no clash with the stack card's own :hover outline. */
.ff-card-img.ff-targeted { outline: 3px solid var(--danger); outline-offset: -1px; }
/* On the battlefield the card sits in a square cell with the 5:7 art contain-fit
   to the middle ~71.4%, so an element-box outline floats off the art onto the
   empty cell margins. Hug the art with the same inset pseudo-element the combat
   markers use. */
.ff-battlefield .ff-bf__card.ff-targeted { outline: none; }
.ff-battlefield .ff-bf__card.ff-targeted::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 14.3%;
	right: 14.3%;
	border-radius: 3px;
	box-shadow: 0 0 0 3px var(--danger);
	pointer-events: none;
}

/* Choosing a hand card to put onto the battlefield (e.g. Metamorphose): eligible
   cards are click-to-pick (blue), the chosen one red. */
.ff-hand__card--pick { outline: 2px solid var(--selection); outline-offset: -2px; cursor: pointer; }
.ff-hand__card--sel,
.ff-hand__card--sel:hover { outline: 3px solid var(--danger); outline-offset: -2px; }
.ff-bottom-badge {
	position: absolute;
	top: -6px;
	left: -6px;
	min-width: 18px;
	height: 18px;
	padding: 0 4px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--radius-pill);
	background: var(--dark_blue);
	color: #fff;
	font-size: 0.72rem;
	font-weight: 800;
	z-index: 2;
}
.ff-card-img.ff-dragging { opacity: 0.45; transform: none; }

/* The stack — a small moveable window on the board (drag the header); click a
   card to resolve it. */
.ff-stack {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	z-index: 25;
	display: flex;
	flex-direction: column;
	max-width: 92%;
	background: color-mix(in srgb, var(--contents) 90%, transparent);
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lg);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	overflow: hidden;
}
.ff-stack[hidden] { display: none; }

.ff-stack__header {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--default) 8%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}

.ff-stack__cards {
	display: flex;
	align-items: center;    /* the square abilities sit centred next to the taller cards */
	gap: 0.4rem;
	padding: 0.5rem 0.6rem 1.7rem;   /* extra room below for the viewer's lowered items */
	overflow-x: auto;
}
/* Your own spells/abilities sit ~15% of a card height lower than the opponent's,
   so it's clear at a glance whose objects are on the stack. */
.ff-stack__cards > .ff-stack__mine { transform: translateY(20px); }
.ff-stack .ff-stack__card {
	flex: 0 0 auto;
	width: auto;
	height: 134px;            /* same size as cards in the hand */
	cursor: pointer;
}
.ff-stack .ff-stack__card:hover { outline: 2px solid var(--dark_blue); outline-offset: 1px; }

/* A triggered/activated ability on the stack: a square info "card" — the
   source's art + name, the ability type, and the effect text. Square, sized to
   the width of a stack card. */
.ff-stack__ability {
	flex: 0 0 auto;
	box-sizing: border-box;
	width: 96px;               /* same width as a stack card */
	height: 108px;             /* taller for the text; ~80% of the 134px card height */
	min-width: 0;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border-radius: var(--radius-sm);
	border: 1px solid color-mix(in srgb, var(--default) 30%, transparent);
	background: color-mix(in srgb, var(--default) 7%, var(--contents));
}
.ff-stack__ability .ff-ability__art {
	flex: 0 0 auto;
	width: 100%;
	height: 34px;
	aspect-ratio: auto;
	background-size: cover;
	background-position: center 25%;
	border-radius: 0;
}
/* Name (above the art) and type each stay on a single line, ellipsised if too
   long; the text fills the rest of the square and clips. */
.ff-ability__name {
	flex: 0 0 auto;
	font-weight: 700;
	font-size: 0.6rem;
	line-height: 1.1;
	padding: 0.2rem 0.34rem 0.12rem;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.ff-ability__type {
	flex: 0 0 auto;
	font-size: 0.42rem;        /* small enough that "Activated ability" fits one line */
	text-transform: uppercase;
	letter-spacing: 0.02em;
	color: var(--dark_blue);
	padding: 0.1rem 0.34rem 0;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.ff-ability__text {
	flex: 1 1 auto;
	min-height: 0;
	font-size: 0.48rem;
	line-height: 1.14;
	padding: 0.05rem 0.34rem 0.18rem;
	overflow: hidden;
	color: color-mix(in srgb, var(--default) 82%, transparent);
}

/* Trigger holding area: a moveable panel of the abilities you must put on the
   stack. Click each (in order) to place it; the first goes on the bottom. */
.ff-triggers {
	position: absolute;
	left: 50%;
	top: 38%;
	transform: translate(-50%, -50%);
	z-index: 28;
	display: flex;
	flex-direction: column;
	max-width: 92%;
	background: color-mix(in srgb, var(--contents) 92%, transparent);
	border: 1px solid color-mix(in srgb, var(--dark_blue) 45%, transparent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lg);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
	overflow: hidden;
}
.ff-triggers[hidden] { display: none; }
.ff-triggers__header {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--dark_blue) 12%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}
.ff-triggers__hint {
	padding: 0.35rem 0.7rem 0;
	font-size: 0.72rem;
	text-align: center;
	color: color-mix(in srgb, var(--default) 60%, transparent);
}
.ff-triggers__cards {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
	padding: 0.5rem 0.7rem 0.7rem;
	overflow-x: auto;
}
.ff-triggers__card { cursor: pointer; }
.ff-triggers__card:hover { outline: 2px solid var(--dark_blue); outline-offset: 1px; }
/* An ability that still needs a target gets a gold ring. */
.ff-triggers__card--target { box-shadow: 0 0 0 2px var(--warning); }
/* The ability whose target you're choosing right now: highlighted, not clickable. */
.ff-triggers__card--targeting {
	cursor: default;
	box-shadow: 0 0 0 3px var(--dark_blue);
}
.ff-triggers__card--targeting:hover { outline: none; }

.ff-card {
	flex: 0 0 auto;
	width: 64px;
	aspect-ratio: 5 / 7;
	border-radius: var(--radius-sm);
	border: 1px solid color-mix(in srgb, var(--default) 25%, transparent);
}

.ff-card--back {
	background-image: url(/static/img/forgetful_fish_cardback.svg);
	background-size: cover;
	background-position: center;
	border-color: color-mix(in srgb, var(--default) 22%, transparent);
}

/* ── Right rail: log + chat ───────────────────────────────────────────── */
.ff-panel {
	display: flex;
	flex-direction: column;
	min-height: 0;
	background: var(--contents);
	border: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
	border-radius: var(--radius-md);
	overflow: hidden;
}

.ff-panel__title {
	flex: 0 0 auto;
	padding: 0.5rem 0.75rem;
	font-size: 0.8rem;
	font-weight: 700;
	border-bottom: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
}

.ff-panel__body {
	flex: 1 1 auto;
	overflow-y: auto;
	padding: 0.6rem 0.75rem;
	font-size: 0.84rem;
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
}

/* Log panel header with the admin-only privileged-log copy affordance. */
.ff-log__titlebar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
}
.ff-log__priv {
	background: none;
	border: 1px solid color-mix(in srgb, var(--default) 22%, transparent);
	border-radius: var(--radius-sm);
	color: color-mix(in srgb, var(--default) 65%, transparent);
	cursor: pointer;
	font: inherit;
	font-size: var(--fs-xs);
	padding: 0.1rem 0.45rem;
	white-space: nowrap;
}
.ff-log__priv:hover {
	background: color-mix(in srgb, var(--default) 8%, transparent);
	color: var(--default);
}

.ff-log__entry { line-height: 1.35; }
.ff-log__entry--muted { color: color-mix(in srgb, var(--default) 45%, transparent); }
/* "Turn N: name." lines act as separators between turns. */
.ff-log__entry--turn {
	margin-top: 0.35rem;
	padding-top: 0.4rem;
	border-top: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
	font-weight: 700;
}
.ff-log__entry:first-child.ff-log__entry--turn { margin-top: 0; padding-top: 0; border-top: none; }
/* Player names carry their seat colour (matches the battlefield tints). */
.ff-log__name--you { color: var(--dark_blue); }
.ff-log__name--opp { color: var(--danger-strong); }
/* A card name in the log: hover previews it in the card preview pane. */
.ff-log__cardname {
	text-decoration: underline dotted color-mix(in srgb, var(--default) 45%, transparent);
	text-underline-offset: 2px;
	cursor: help;
}

/* Card preview pane (above the log): a fixed enlarged view of the hovered card,
   collapsible from its header. (.ff-panel__body sets display:flex, so [hidden]
   needs an explicit rule to win.) */
.ff-cardview[hidden], .ff-cardview__img[hidden] { display: none; }
.ff-preview__title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	width: 100%;
	background: none;
	border: none;
	color: var(--default);
	cursor: pointer;
	text-align: left;
}
.ff-preview__chev { transition: transform 0.15s ease; color: color-mix(in srgb, var(--default) 55%, transparent); }
.ff-preview--collapsed .ff-preview__chev { transform: rotate(-90deg); }
.ff-preview--collapsed .ff-cardview { display: none; }
.ff-preview--collapsed .ff-preview__title { border-bottom: none; }
.ff-cardview {
	align-items: center;
	justify-content: center;
	height: clamp(160px, 30vh, 340px);
	flex: 0 0 auto;
}
.ff-cardview__img {
	flex: 1 1 auto;
	align-self: stretch;
	width: 100%;
	min-height: 0;
	aspect-ratio: auto;   /* fill the panel — background-size:contain fits the art */
}
.ff-cardview__empty {
	margin: auto;
	text-align: center;
	color: color-mix(in srgb, var(--default) 45%, transparent);
}

.ff-chat__msg { line-height: 1.35; }
.ff-chat__msg b { color: var(--dark_blue); }

.ff-chat__form {
	flex: 0 0 auto;
	display: flex;
	gap: 0.4rem;
	padding: 0.5rem;
	border-top: 1px solid color-mix(in srgb, var(--default) 12%, transparent);
}

.ff-chat__input {
	flex: 1 1 auto;
	min-width: 0;
	padding: 0.4rem 0.55rem;
	border-radius: var(--radius-sm);
	border: 1px solid color-mix(in srgb, var(--default) 20%, transparent);
	background: color-mix(in srgb, var(--default) 4%, var(--contents));
	color: var(--default);
	font-size: 0.85rem;
}

.ff-chat__send { flex: 0 0 auto; }

/* ── Responsive: stack the rails under the table on narrow screens ────── */
@media (max-width: 900px) {
	.ff-table {
		grid-template-columns: 1fr;
		height: auto;
	}
	.ff-rail--left  { grid-template-rows: none; grid-auto-flow: column; }
	.ff-rail--right { grid-template-rows: auto 320px 320px; }
	.ff-main { min-height: 60vh; }
	.ff-resize { display: none; }   /* stacked layout — nothing to drag */
}

/* Below ~560px the phase strip is wider than the screen: let it scroll
   sideways instead of clipping later phases off the board (a scroll container
   still clips the sliding highlight, and the highlight's offsetLeft-based
   positioning is content-relative, so it survives the scroll). The topbar
   wraps instead of pushing "Return to Lobby" off the right edge. */
@media (max-width: 560px) {
	.ff__topbar { flex-wrap: wrap; gap: 0.3rem 0.75rem; }
	.ff-phasebar { overflow-x: auto; overflow-y: hidden; }
	.ff-phasebar__turn { flex: 0 0 auto; max-width: 9.5rem; }
	.ff-phase { flex: 1 0 auto; }
}

/* ----------------------------------------------------------
   Waiting-for-opponent overlay (host of a not-yet-joined game)
   ---------------------------------------------------------- */
.ff-wait {
	position: fixed;
	inset: 0;
	z-index: 60;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(0, 0, 0, 0.5);
	padding: 1rem;
}
.ff-wait[hidden] { display: none; }

.ff-wait__modal {
	background: var(--contents);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
	padding: 1.5rem;
	width: 100%;
	max-width: 380px;
	display: grid;
	gap: 1rem;
	text-align: center;
}

.ff-wait__title {
	font-size: 1.05rem;
	font-weight: 700;
}

.ff-wait__sub {
	margin: 0;
	font-size: 0.85rem;
	color: var(--form_text);
}

.ff-wait__share {
	display: flex;
	justify-content: center;
}
.ff-wait__share[hidden] { display: none; }   /* a display rule otherwise defeats [hidden] */

/* The chip itself is styled by minecraft.css (.minecraft-host-*); drop its
   default negative margin-left so it sits centred in the modal. The link text
   auto-shrinks to fit (copy_tooltip.js / fitChipText). */
.ff-wait__chip {
	margin-left: 0;
	max-width: 100%;
}

.ff-wait__actions {
	display: flex;
	justify-content: center;
}

/* End-of-game window — a non-modal floating panel (no backdrop), so the final
   board stays inspectable: hover previews, the log, graveyard/deck views all
   keep working. Game state is safe regardless - the client's gameOver() guard
   and the server's 409-on-finished reject every mutation. Drag the header to
   move it aside; the close button dismisses it for the rest of the game. */
.ff-end {
	position: absolute;
	left: 50%;
	top: 40%;
	transform: translate(-50%, -50%);
	z-index: 61;
	width: min(380px, 92vw);
}
.ff-end[hidden] { display: none; }

.ff-end__modal {
	background: var(--contents);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
	overflow: hidden;
	text-align: center;
}

.ff-end__handle {
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
	padding: 0.3rem 0.7rem;
	cursor: move;
	user-select: none;
	touch-action: none;
	font-size: var(--fs-xs);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: color-mix(in srgb, var(--default) 60%, transparent);
	background: color-mix(in srgb, var(--default) 8%, transparent);
	border-bottom: 1px solid color-mix(in srgb, var(--default) 14%, transparent);
}

.ff-end__close {
	position: absolute;
	right: 0.4rem;
	top: 50%;
	transform: translateY(-50%);
	background: none;
	border: none;
	cursor: pointer;
	font-size: 1.1rem;
	line-height: 1;
	color: color-mix(in srgb, var(--default) 60%, transparent);
}
.ff-end__close:hover { color: var(--default); }

.ff-end__body {
	padding: 1.25rem 1.5rem 1.5rem;
	display: grid;
	gap: 0.85rem;
}

.ff-end__title {
	font-size: 1.7rem;
	font-weight: 800;
	letter-spacing: 0.01em;
}
.ff-end__title--win { color: var(--success); }
.ff-end__title--lose { color: var(--danger-strong); }

.ff-end__reason {
	margin: 0;
	font-size: 0.9rem;
	color: var(--form_text);
}

.ff-end__elo {
	margin: 0;
	font-size: 0.85rem;
	font-weight: 600;
}
.ff-end__elo[hidden] { display: none; }

.ff-end__actions {
	display: flex;
	justify-content: center;
	margin-top: 0.4rem;
}

/* ── Transient feedback: rejection toast ───────────────────────────────────
   Surfaces a server-rejected action, which was previously silent. Floats over
   the board but below the modal overlays (z 60+). */
.ff-toast {
	position: fixed;
	bottom: 175px;      /* clears the hand */
	left: 50%;
	transform: translateX(-50%);
	max-width: min(420px, 90vw);
	padding: 0.45rem 0.95rem;
	background: color-mix(in srgb, var(--danger-strong) 14%, var(--contents));
	border: 1px solid color-mix(in srgb, var(--danger-strong) 55%, transparent);
	border-radius: var(--radius-md);
	font-size: 0.82rem;
	text-align: center;
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
	z-index: 55;
}
.ff-toast[hidden] { display: none; }
.ff-toast--show { animation: ff-toast-fade 3.2s ease forwards; }
@keyframes ff-toast-fade {
	0%       { opacity: 0; transform: translate(-50%, 6px); }
	6%, 82%  { opacity: 1; transform: translate(-50%, 0); }
	100%     { opacity: 0; transform: translate(-50%, 0); }
}

/* ── Change-driven one-shot animations ─────────────────────────────────────
   Classes added by animateChanges() after each re-render, removed on
   animationend. Keyframes (not transitions) because the DOM is rebuilt each
   render, so the node carrying the change is always freshly inserted. */
/* Tap/untap: animate the rotation the .ff-tapped class otherwise snaps to.
   (Zone changes deliberately move instantly - no entry fades.) */
@keyframes ff-tap-rot { from { transform: none; } to { transform: translateY(14.3%) rotate(90deg); } }
@keyframes ff-untap-rot { from { transform: translateY(14.3%) rotate(90deg); } to { transform: none; } }
.ff-battlefield .ff-bf__card.ff-anim-tap { animation: ff-tap-rot 0.16s ease; }
.ff-battlefield .ff-bf__card.ff-anim-untap { animation: ff-untap-rot 0.16s ease; }

/* Life swing: pulse the total and float a +N/-N beside it. */
@keyframes ff-life-pulse {
	0%   { transform: scale(1); }
	30%  { transform: scale(1.18); }
	100% { transform: scale(1); }
}
.ff-player__life.ff-life-flash { animation: ff-life-pulse 0.45s ease; }
.ff-life-delta {
	position: absolute;
	top: 0.45rem;
	right: 0.6rem;
	font-weight: 800;
	font-size: 1rem;
	font-variant-numeric: tabular-nums;
	pointer-events: none;
	animation: ff-life-delta 1.3s ease forwards;
}
.ff-life-delta--down { color: var(--danger-strong); }
.ff-life-delta--up   { color: var(--success); }
@keyframes ff-life-delta {
	0%   { opacity: 0; transform: translateY(5px); }
	15%  { opacity: 1; transform: translateY(0); }
	70%  { opacity: 1; }
	100% { opacity: 0; transform: translateY(-14px); }
}

@media (prefers-reduced-motion: reduce) {
	.ff-toast--show { animation: none; }
	.ff-battlefield .ff-bf__card.ff-anim-tap,
	.ff-battlefield .ff-bf__card.ff-anim-untap,
	.ff-player__life.ff-life-flash,
	.ff-life-delta { animation: none; }
}

/* ── Spectator (read-only) ──────────────────────────────────────────────────
   A watcher sees the host's seat with mutually-known cards only. The host's own
   hand is collapsed to the same one-line "known + N unknown" summary used for an
   opponent's hand, rather than a strip of dead card-backs. */
.ff-ophand--self { width: 100%; }
.ff[data-ff-mode="spectator"] [data-ff-hand] {
	min-height: 0;
	justify-content: flex-start;
	padding: 0.3rem 0.4rem;
}
/* No interactive affordances for spectators: no chat input, concede, or prompt
   action panel, and the board can't be clicked/dragged (hover/zoom still work). */
.ff[data-ff-mode="spectator"] [data-ff-chat-form],
.ff[data-ff-mode="spectator"] [data-ff-concede],
.ff[data-ff-mode="spectator"] [data-ff-prompt] {
	display: none !important;
}
.ff[data-ff-mode="spectator"] [data-ff-bf-you],
.ff[data-ff-mode="spectator"] [data-ff-bf-opponent],
.ff[data-ff-mode="spectator"] [data-ff-stack] {
	cursor: default;
}
