/*
 * Contested Realm — the client's whole stylesheet.
 *
 * The palette comes off the cards themselves: Sorcery's frames are dark with
 * old-gold ornament, and the art is heavy on teal, so the ground is a deep
 * blue-green and the accent is that gold. The four elements keep their own
 * colours throughout — thresholds, costs and card edges all read from the
 * same four hues, because element is the thing a player is always counting.
 *
 * One display face (Marcellus, roman capitals) for headings only; everything
 * that is operated rather than read is set in Alegreya Sans, which keeps its
 * numerals legible at the sizes the board needs.
 */

:root {
    --ground: #0d1315;
    --ground-deep: #070b0c;
    --panel: #151f21;
    --panel-high: #1d292c;
    --edge: #2b3a3d;
    --edge-bright: #3d5257;
    --ink: #e9e3d5;
    --ink-dim: #9aa8aa;
    --ink-faint: #66767a;
    --gold: #c9a227;
    --gold-dim: #8a7020;
    --air: #7fb8d4;
    --earth: #94a55e;
    --fire: #cd5f3c;
    --water: #4f86bd;
    --danger: #c0503f;
    --good: #6f9f6a;

    --card-ratio: 380 / 531;
    --radius: 3px;
    --shadow: 0 2px 10px rgba(0, 0, 0, .5);

    --display: "Marcellus", "Palatino Linotype", Palatino, Georgia, serif;
    --body: "Alegreya Sans", "Avenir Next", "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body {
    height: 100%;
}

body {
    margin: 0;
    background:
        radial-gradient(120% 90% at 50% 0%, #14202300 0%, var(--ground-deep) 100%),
        var(--ground);
    color: var(--ink);
    font-family: var(--body);
    font-size: 15px;
    line-height: 1.45;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
    font-family: var(--display);
    font-weight: 400;
    letter-spacing: .02em;
    margin: 0;
    text-wrap: balance;
}

h1 { font-size: 1.4rem; }
h2 { font-size: 1.05rem; color: var(--gold); margin-bottom: .5rem; }
h3 {
    font-size: .8rem;
    text-transform: uppercase;
    letter-spacing: .14em;
    color: var(--ink-faint);
}

/* Only the screen the body names is on. The showing rules are compounded so
   they outrank each screen's own id selector; a bare `main { display: none }`
   loses to `#gate { display: grid }` and every screen stacks up at once. */
main { display: none; }
body[data-screen="gate"] #gate { display: grid; }
body[data-screen="table"] #table { display: flex; }
body[data-screen="builder"] #builder { display: block; }

/* The hall is the height of the screen and no taller: the tables and the
   controls take what they need, and the chat takes the rest and scrolls
   inside it. Left to grow, a busy evening's chat pushed the whole page off
   the bottom and the only way to the newest line was to scroll the window. */
body[data-screen="lobby"] #lobby {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
}

/* ---------- controls ---------- */

button {
    font: inherit;
    font-size: .9rem;
    color: var(--ink);
    background: var(--panel-high);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    padding: .4rem .85rem;
    cursor: pointer;
    transition: border-color .12s, background .12s, color .12s;
}

button:hover { border-color: var(--edge-bright); background: #243236; }
button:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
button:disabled { opacity: .4; cursor: not-allowed; }

button.primary {
    background: linear-gradient(#3a3013, #241e0c);
    border-color: var(--gold-dim);
    color: #f0e2b4;
}
button.primary:hover { border-color: var(--gold); background: linear-gradient(#4a3d18, #2e260f); }
button.wide { width: 100%; margin-top: .6rem; }

input, select {
    font: inherit;
    width: 100%;
    color: var(--ink);
    background: var(--ground-deep);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    padding: .4rem .55rem;
}

input:focus, select:focus { outline: none; border-color: var(--gold-dim); }

label {
    display: block;
    margin-bottom: .6rem;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--ink-faint);
}

label input, label select { margin-top: .25rem; text-transform: none; letter-spacing: 0; }

.row { display: flex; gap: .5rem; }
.row button { flex: 1; }
.muted { color: var(--ink-faint); font-size: .8rem; }
.error { color: var(--danger); font-size: .85rem; margin: .6rem 0 0; }
.tally { color: var(--ink-dim); font-size: .85rem; font-variant-numeric: tabular-nums; }

/* ---------- signing in ---------- */

#gate {
    /* min-height, not height, and it scrolls: the card is centred in a tall
       window and the small print below it has to stay reachable in a short
       one, rather than being cut off at the bottom of the viewport. */
    min-height: 100%;
    place-items: center;
    align-content: center;
    padding: 2rem;
    overflow-y: auto;
}

.gate-card {
    width: min(340px, 100%);
    background: var(--panel);
    border: 1px solid var(--edge);
    border-top: 2px solid var(--gold-dim);
    border-radius: var(--radius);
    padding: 1.8rem;
    box-shadow: var(--shadow);
}

/* The small print under the card: what the game is, and whose it is. Set
   narrow and quiet -- it is there to be read once and then not again, so it
   must not compete with the two fields above it. */
.gate-print {
    width: min(540px, 100%);
    margin-top: 1.6rem;
    color: var(--ink-faint);
    font-size: .74rem;
    line-height: 1.65;
    text-align: center;
}

.gate-print p + p {
    margin-top: .7rem;
}

/* Underlined rather than coloured: the print is quiet on purpose, and a link
   in it should read as a link without becoming the brightest thing there. */
.gate-print a {
    color: var(--ink-dim);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.gate-print a:hover,
.gate-print a:focus-visible {
    color: var(--gold);
}

.gate-card h1 { font-size: 1.6rem; text-align: center; }

/* The wordmark stands in for the title and the tagline both -- it says
   "Sorcery / Contested Realm" itself, so repeating either in text beneath it
   would be saying the same thing twice. Still an h1, with the words in the
   alt text, so the page keeps its heading and a screen reader keeps the name.
   Width and height are on the img as well so nothing jumps while it loads. */
.gate-logo {
    margin: 0 0 1.5rem;
    line-height: 0;
}

.gate-logo img {
    display: block;
    width: 100%;
    height: auto;
}

/* ---------- shared chrome ---------- */

.bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .7rem 1.1rem;
    background: var(--panel);
    border-bottom: 1px solid var(--edge);
}

.bar-right { display: flex; align-items: center; gap: .7rem; }
#who { color: var(--gold); font-family: var(--display); }

.panel {
    background: var(--panel);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    padding: 1rem;
}

.list { list-style: none; margin: 0; padding: 0; }

.list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    padding: .45rem .1rem;
    border-bottom: 1px solid var(--edge);
    font-size: .9rem;
}

.list li:last-child { border-bottom: none; }
.list.dense li { padding: .2rem .1rem; font-size: .85rem; }
.list .sub { color: var(--ink-faint); font-size: .78rem; }
.list button { padding: .18rem .5rem; font-size: .78rem; }
.list .empty { color: var(--ink-faint); font-style: italic; justify-content: center; }

/* ---------- lobby ---------- */

.lobby-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1rem;
    padding: 1rem;
    align-items: start;
}

/* ---------- deck builder ---------- */

.builder-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 1rem;
    padding: 1rem;
    height: calc(100vh - 56px);
}

.pool { display: flex; flex-direction: column; min-height: 0; }

.pool-controls {
    display: flex;
    gap: .5rem;
    align-items: center;
    margin-bottom: .8rem;
}

.pool-controls input { flex: 2; }
.pool-controls select { flex: 1; }

.pool-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: .5rem;
    overflow-y: auto;
    min-height: 0;
    padding-right: .3rem;
}

.pool-card {
    position: relative;
    aspect-ratio: var(--card-ratio);
    border-radius: var(--radius);
    border: 1px solid var(--edge);
    background-color: var(--ground-deep);
    background-size: cover;
    background-position: center;
    cursor: pointer;
}

.pool-card:hover { border-color: var(--gold); }
.pool-card[data-in]::after {
    content: attr(data-in);
    position: absolute;
    top: 3px;
    right: 3px;
    background: var(--gold);
    color: #1a1405;
    font-size: .7rem;
    font-weight: 700;
    border-radius: 999px;
    min-width: 17px;
    text-align: center;
    padding: 0 4px;
}

.pool-card .fallback {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: .3rem;
    text-align: center;
    font-size: .7rem;
    color: var(--ink-dim);
}

.deck { display: flex; flex-direction: column; min-height: 0; overflow-y: auto; }
.deck h3 { margin-top: .9rem; }
.deck-name { margin-bottom: .5rem; }
.deck-avatar { font-size: .78rem; text-transform: uppercase; letter-spacing: .1em; color: var(--ink-faint); }
.deck-avatar strong { display: block; color: var(--ink); text-transform: none; letter-spacing: 0; font-weight: 500; }

/* ---------- the table ---------- */

/* The table fills the window and does not scroll: a player should never have
   to hunt for their own rail or the question they are being asked. The realm
   is sized from the height left over, and everything else is fixed. */
body[data-screen="table"] { overflow: hidden; }

#table { height: 100vh; flex-direction: column; }

.table-grid {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: 1rem;
    padding: 1rem;
    /* Stretched, not start-aligned: both columns need a definite height for
       the realm to size itself from what is left over. */
    align-items: stretch;
}

.board-column {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    min-width: 0;
    height: 100%;
    /* Whatever happens to the board, the hand below it stays on the screen. */
    overflow: hidden;
}

.rail {
    display: flex;
    align-items: center;
    gap: 1.1rem;
    flex-wrap: wrap;
    padding: .4rem .8rem;
    background: var(--panel);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    font-size: .82rem;
    flex: 0 0 auto;
}

.rail .name { font-family: var(--display); color: var(--gold); font-size: 1rem; }
.rail.turn { border-color: var(--gold-dim); box-shadow: inset 0 0 0 1px rgba(201, 162, 39, .18); }
.rail .stat { color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.rail .stat b { color: var(--ink); font-weight: 700; }

.thresholds { display: flex; gap: .3rem; }

.pip {
    display: inline-grid;
    place-items: center;
    width: 21px;
    height: 21px;
    border-radius: 50%;
    font-size: .72rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: #0b0f10;
}

.pip.AIR { background: var(--air); }
.pip.EARTH { background: var(--earth); }
.pip.FIRE { background: var(--fire); }
.pip.WATER { background: var(--water); }
.pip.spent { opacity: .28; }

/* The realm: five across, four down, drawn as the viewer's own side up.
   Twenty card-shaped squares, so the whole board is 5:4 of a card - it takes
   whichever of the width and the height runs out first and centres itself. */
.realm-holder {
    flex: 1;
    min-height: 0;
    display: flex;
    justify-content: center;
}

.realm {
    display: grid;
    /* minmax(0, 1fr) rather than 1fr: a 1fr track will not shrink below its
       content, and each square asks for aspect-ratio 1, so a wide board gave
       every row a tall minimum and the whole grid grew past the height it was
       given -- pushing the hand off the bottom of the screen. The board's own
       5:4 ratio is what keeps the cells square; the tracks must be free to
       shrink to it. */
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: repeat(4, minmax(0, 1fr));
    gap: 4px;
    /* Square cells, five by four, so the board takes whichever of the height
       and the width runs out first: the holder gives it the height, the ratio
       gives it the width back. */
    height: 100%;
    width: auto;
    max-width: 100%;
    aspect-ratio: 5 / 4;
    /* A picture of somewhere, laid under the board and dimmed almost to
       nothing: the squares and what stands on them have to stay the brightest
       things on the screen, so the art is atmosphere rather than scenery. The
       flat colour is painted over it rather than behind it, which keeps the
       board legible whatever the picture happens to be -- and keeps it
       looking right when there is no picture at all. */
    background-color: var(--ground-deep);
    background-image:
        linear-gradient(rgba(9, 14, 16, .84), rgba(9, 14, 16, .84)),
        var(--ground-art, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    padding: 4px;
}

.square {
    position: relative;
    min-height: 0;
    aspect-ratio: 1;
    border-radius: 2px;
    border: 1px solid var(--edge);
    /* Translucent, so the picture under the board shows through the empty
       squares. Opaque cells hid all of it but the four pixels of gap. */
    background-color: rgba(10, 16, 18, .58);
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    overflow: hidden;
}

/*
 * A site lies sideways in its square, the way it is laid on a table.
 *
 * The card is portrait, so it is turned a quarter turn and sized from the
 * square's height: at 380/531 of the square's width it comes out exactly as
 * wide as the square once turned, and the whole card stays visible with a
 * band above and below for the site's name and whatever is standing on it.
 * Centred by `inset: 0; margin: auto` rather than a translate, because a
 * translate after a rotate moves along the rotated axes.
 */
.site-art {
    position: absolute;
    inset: 0;
    margin: auto;
    height: 100%;
    width: 71.56%;
    transform: rotate(90deg);
    background-size: cover;
    background-position: center;
    border-radius: 2px;
}

/*
 * The other player's ground, laid facing them.
 *
 * A site is sideways on the board, so facing the other way is the same turn
 * plus half a revolution. Only the board is turned: the picture that pops up
 * on hover is built separately and stays the right way up, because the point
 * of it is to be read.
 */
.site-art.theirs {
    transform: rotate(270deg);
}

.square.void {
    /* The hatching that says "nothing here", drawn in translucent ink so the
       picture under the board still reads through it. */
    background-image:
        repeating-linear-gradient(45deg,
            rgba(12, 18, 20, .75) 0 6px, rgba(10, 15, 17, .55) 6px 12px);
    border-style: dashed;
    border-color: #222e30;
}

.square.flooded::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(79, 134, 189, 0) 30%, rgba(79, 134, 189, .38));
    pointer-events: none;
}

.square .number {
    position: absolute;
    top: 2px;
    left: 4px;
    font-size: .62rem;
    color: rgba(233, 227, 213, .45);
    font-variant-numeric: tabular-nums;
    text-shadow: 0 1px 2px #000;
}

/* A marked square, in the colours the board already uses for whose is whose:
   green for yours, red for theirs, as on a unit's own edge. Sat beside the
   number rather than over the art, so a marked square still reads as ground. */
.square .number .mark {
    display: inline-block;
    width: 5px;
    height: 5px;
    margin-left: 3px;
    border-radius: 50%;
    vertical-align: middle;
    box-shadow: 0 0 2px rgba(0, 0, 0, .9);
}

.square .number .mark.mine { background: var(--good); }
.square .number .mark.theirs { background: var(--danger); }
/* Ashes: ground a Wildfire has burnt over, ember-orange fading to grey. */
.square .number .mark.burnt {
    width: 7px; height: 7px; border-radius: 2px;
    background: linear-gradient(135deg, #e0782a, #6b6b6b);
    box-shadow: 0 0 3px rgba(224, 120, 42, .8);
}

.square .site-name {
    position: relative;
    font-size: .62rem;
    line-height: 1.15;
    padding: 1px 3px 2px;
    background: rgba(7, 11, 12, .82);
    text-align: center;
}

.layers {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 2px;
}

.layer { display: flex; flex-wrap: wrap; gap: 2px; }

/* One unit standing somewhere: the card's own art, cropped to its picture. */
.unit {
    position: relative;
    width: 30px;
    height: 30px;
    border-radius: 2px;
    border: 1px solid var(--edge-bright);
    background-color: var(--panel-high);
    background-size: 200%;
    background-position: center 26%;
    cursor: pointer;
    flex: 0 0 auto;
}

.unit.mine { border-color: var(--gold); }
.unit.theirs { border-color: var(--danger); }
.unit.tapped { transform: rotate(18deg); }
/* A Ward is the one mark that stops the next thing outright, so it is drawn
   as a hard white edge rather than a tint — there either is one or there is
   not, and it goes the moment it is spent. */
.unit.ward {
    border-color: #fff;
    box-shadow: 0 0 0 1px #fff;
}

.site-art.ward {
    border: 2px solid #fff;
}

.unit.stealth { opacity: .6; }

.unit .power {
    position: absolute;
    bottom: -1px;
    right: -1px;
    font-size: .58rem;
    font-weight: 700;
    line-height: 1;
    padding: 1px 2px;
    background: rgba(7, 11, 12, .9);
    border-radius: 2px;
    font-variant-numeric: tabular-nums;
}

/* Damage is its own mark rather than a second number: a player counts the
   power first and only then asks how much of it has been chipped away. */
.unit .hurt {
    position: absolute;
    top: -3px;
    right: -3px;
    min-width: 12px;
    padding: 0 2px;
    font-size: .56rem;
    font-weight: 700;
    line-height: 12px;
    text-align: center;
    color: #fff;
    background: var(--danger);
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.unit .subsurface {
    position: absolute;
    top: -1px;
    left: -1px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
}

.unit .subsurface.UNDERGROUND { background: var(--earth); }
.unit .subsurface.UNDERWATER { background: var(--water); }
.unit .subsurface.VOID { background: var(--ink-faint); }

/*
 * A unit that is not standing on the ground has to look like it.
 *
 * Most rules keep the regions apart — a nearby ally cannot cross a regional
 * boundary — so mistaking a buried minion for one on the surface makes the
 * game look broken when it is behaving correctly. The old signal was 8% of
 * opacity and a 6px dot, which is no signal at all: the chip is now visibly
 * sunk, its edge is broken rather than solid, and the dot is big enough to
 * find with a ring dark enough to hold against card art.
 */
.unit[data-region="UNDERGROUND"],
.unit[data-region="UNDERWATER"],
.unit[data-region="VOID"] {
    border-style: dashed;
    filter: brightness(.62) saturate(.75);
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, .75);
}

.unit .subsurface {
    width: 9px;
    height: 9px;
    border: 1px solid rgba(0, 0, 0, .8);
    box-shadow: 0 0 2px rgba(0, 0, 0, .9);
}

/* What is true of this one card -- where it is, whose it is -- under its
   name in the picture that pops up. */
.hover-fact {
    font-size: .78rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

/* A pile laid out to be looked through: the cemetery and the banished, which
   are face up and get read constantly. Over the table rather than beside it,
   since it is opened to answer one question and then shut. */
.pile {
    position: fixed;
    inset: 6vh 8vw;
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: .6rem;
    padding: 1rem;
    background: var(--panel);
    border: 1px solid var(--gold-dim);
    border-radius: var(--radius);
    box-shadow: 0 18px 60px rgba(0, 0, 0, .7);
}

.pile[hidden] { display: none; }

.pile-bar {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    flex: 0 0 auto;
}

.pile-bar h3 {
    font-family: var(--display);
    color: var(--gold);
    font-size: 1.05rem;
}

.pile-cards {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: .5rem;
    overflow-y: auto;
    min-height: 0;
    flex: 1;
}

/*
 * A card laid out to be looked at, wherever it is laid.
 *
 * Shared with the hand rather than restated, because restating it is how the
 * two drifted apart: a pile's cards had a height and "width: auto" and nothing
 * to give them a width -- no aspect ratio, no art, no border -- so a cemetery
 * opened onto ten cards a hundred and thirty pixels tall and none wide, and
 * looked empty. Nothing loaded their art either: it is fetched when a tile is
 * scrolled into view, and a tile with no width is never in view.
 */
.hand .card,
.pile-cards .card {
    flex: 0 0 auto;
    width: auto;
    aspect-ratio: var(--card-ratio);
    border-radius: var(--radius);
    border: 1px solid var(--edge);
    background-color: var(--panel-high);
    background-size: cover;
    background-position: center;
}

.pile-cards .card { height: 132px; }

/* A count you can open, and one that is empty and so opens nothing. */
.stat-note { opacity: .6; font-size: .85em; }
.stat.lookable { cursor: pointer; }
.stat.lookable:hover b { color: var(--gold); }
.stat.lookable.empty { cursor: default; }
.stat.lookable.empty:hover b { color: var(--ink); }

.hand-strip { display: flex; flex-direction: column; gap: .3rem; flex: 0 0 auto; }

.hand {
    display: flex;
    gap: .3rem;
    overflow-x: auto;
    padding-bottom: .25rem;
    height: 112px;
    align-items: center;
}

.hand .card {
    height: 104px;
    cursor: pointer;
}

.hand .card:hover { border-color: var(--gold); }

/* A hand some card keeps for itself sits apart from yours, under its name. */
.hand .held-by {
    flex: 0 0 auto;
    align-self: stretch;
    margin-left: 1.2rem;
    padding-left: .6rem;
    border-left: 1px solid var(--edge-bright);
    display: flex;
    align-items: center;
}
.hand .held-by span {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-size: .7rem;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-dim);
    white-space: nowrap;
}

/* ---------- the side: what you are being asked ---------- */

.side {
    display: flex;
    flex-direction: column;
    gap: .8rem;
    height: 100%;
    min-height: 0;
    overflow-y: auto;
}

.decision {
    background: var(--panel);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    padding: .9rem;
}

.decision.live { border-color: var(--gold); box-shadow: 0 0 18px rgba(201, 162, 39, .12); }
.decision .prompt { font-family: var(--display); font-size: 1rem; margin-bottom: .35rem; }

/* How long is left before the server answers for you. Quiet while there is
   plenty, and only loud when there is not. */
.countdown {
    margin-bottom: .7rem;
    font-size: .76rem;
    letter-spacing: .06em;
    color: var(--ink-faint);
    font-variant-numeric: tabular-nums;
}

.countdown.soon { color: var(--gold); }

.countdown.urgent {
    color: var(--danger);
    font-weight: 700;
}
.decision .waiting { color: var(--ink-faint); font-style: italic; }

/* Not a wait at all: the game fell over and will not ask anything again. */
.decision .waiting.broken { color: var(--danger); font-style: normal; }
.decision .waiting.broken .sub {
    margin-top: .4rem;
    font-size: .74rem;
    color: var(--ink-faint);
    word-break: break-word;
}

.options { display: flex; flex-direction: column; gap: .35rem; max-height: 46vh; overflow-y: auto; }

.option {
    display: flex;
    align-items: center;
    gap: .5rem;
    text-align: left;
    width: 100%;
}

.option .thumb {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    border-radius: 2px;
    background-color: var(--ground-deep);
    background-size: 200%;
    background-position: center 26%;
}

.option .label { flex: 1; }

/* A step with more to settle after it says so, rather than looking like a
   button that commits. */
.option .more { color: var(--gold); font-size: 1rem; line-height: 1; }
.option.deeper { border-left: 2px solid var(--gold-dim); }

.option.pass { margin-top: .3rem; border-style: dashed; width: 100%; }

/* What has been picked so far on the way to one action. */
.trail {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem;
    margin-bottom: .6rem;
}

.trail .crumb {
    padding: .1rem .45rem;
    font-size: .76rem;
    background: transparent;
    border-color: var(--edge);
    color: var(--ink-dim);
}

.trail .crumb:hover { color: var(--ink); border-color: var(--gold-dim); }
/* The way back out of a half-made choice. Always live, including while an
   answer is in flight — a player part-way in must be able to start over. */
.trail .crumb.start-over {
    color: var(--gold);
    border-color: var(--gold-dim);
    border-style: solid;
}

.trail .crumb.start-over:hover { background: var(--panel-high); }

.refused {
    margin-bottom: .6rem;
    font-size: .78rem;
    color: var(--danger);
}

/* Whatever a step is about, while it is under the pointer: the square it
   names, and the one card it names — three Humble Villages read alike in an
   option and are three different cards on the board. */
.square.pointed {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
    z-index: 1;
}

/*
 * The panel points at the board, and the board points back.
 *
 * A card with an option to its name is worth clicking, and says so; one under
 * the pointer lights the option it belongs to. The two highlights are
 * deliberately different: gold rings the thing being talked about, and this
 * softer wash marks the option being talked about -- so pointing at a card
 * and pointing at an option never look like the same thing happening.
 */
[data-card].answerable,
.square.answerable {
    cursor: default;
}

[data-card].actionable,
.square.actionable {
    cursor: pointer;
}

/* An empty square with something to answer -- somewhere to summon to, or to
   move to -- has no picture to ring, so the square itself says so. */
.square.answerable {
    border-color: var(--gold-dim);
}

.option.wanted {
    border-color: var(--gold-dim);
    background: rgba(201, 162, 39, .12);
}

.unit.pointed,
.hand .card.pointed,
.site-art.pointed {
    outline: 2px solid var(--gold);
    outline-offset: 1px;
    box-shadow: 0 0 12px rgba(201, 162, 39, .75);
    z-index: 2;
}

/* A tapped unit is drawn rotated, so its outline turns with it — which is
   right, and worth saying so nobody straightens it later. */
.unit.tapped.pointed { outline-offset: 1px; }

.preview {
    display: none;
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    overflow: hidden;
}

.preview.shown { display: block; }
.preview img { display: block; width: 100%; }

/* The log takes whatever height the side has left, and scrolls inside it —
   a board is pushed several times a turn and a log that resized with it
   would move everything else on the panel each time. */
.log-box {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 8rem;
    flex: 1;
    background: var(--panel);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    padding: .9rem;
}

.option.back { margin-top: .3rem; width: 100%; color: var(--ink-dim); border-style: dashed; }
.option.back:hover { color: var(--ink); }

.side-tabs { display: flex; gap: .25rem; border-bottom: 1px solid var(--edge); }
.side-tab {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--ink-dim);
    padding: .35rem .6rem;
    cursor: pointer;
    font: inherit;
    letter-spacing: .04em;
    text-transform: uppercase;
    font-size: .75rem;
}
.side-tab.active { color: var(--gold); border-bottom-color: var(--gold); }
.side-tab.flash { animation: tab-flash .9s ease-in-out infinite alternate; }
@keyframes tab-flash {
    from { color: var(--ink-dim); }
    to { color: var(--gold); text-shadow: 0 0 6px var(--gold-dim); }
}
@media (prefers-reduced-motion: reduce) {
    .side-tab.flash { animation: none; color: var(--gold); }
}
.side-tab:hover { color: var(--ink); }
.unread {
    display: inline-block;
    min-width: 1.2em;
    padding: 0 .3em;
    margin-left: .3em;
    border-radius: 1em;
    background: var(--gold);
    color: #1b1b1b;
    font-size: .7rem;
    text-align: center;
}
.side-pane { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.side-pane[hidden] { display: none; }
.side-pane .chat-lines { margin-top: .5rem; }
#table-chat-form { display: flex; gap: .5rem; }
#table-chat-form input { flex: 1; }
.people { padding-top: .5rem; overflow-y: auto; }
.people-group + .people-group { margin-top: .8rem; }
.people h4 { margin: 0 0 .3rem; color: var(--ink-dim); font-weight: normal; letter-spacing: .05em; text-transform: uppercase; font-size: .7rem; }
.person { padding: .15rem 0; }
.person-name { color: var(--ink); }

.log {
    list-style: none;
    margin: .5rem 0 0;
    padding: 0 .4rem 0 0;
    flex: 1;
    min-height: 0;
    /* A cap as well as a flex basis: on a narrow screen the table stops being
       a fixed-height layout and becomes a page that scrolls, and there is no
       leftover height to give the log — without this it grows to two hundred
       lines tall and takes the page with it. */
    max-height: 40vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    font-size: .8rem;
    color: var(--ink-dim);
}

.log li { padding: .12rem 0; border-bottom: 1px solid rgba(43, 58, 61, .5); }
.log li:last-child { color: var(--ink); }

/* Shown only while the reader has scrolled away from the newest line. */
.log-latest {
    position: absolute;
    right: 1.2rem;
    bottom: .9rem;
    padding: .2rem .6rem;
    font-size: .75rem;
    background: var(--panel-high);
    border-color: var(--gold-dim);
    color: var(--gold);
    box-shadow: var(--shadow);
}

/* A card held under the pointer, shown full size wherever there is room. */
.hover {
    position: fixed;
    z-index: 50;
    width: 220px;
    pointer-events: none;
    border-radius: 4px;
    box-shadow: 0 6px 26px rgba(0, 0, 0, .7);
}

.hover img { display: block; width: 100%; border-radius: 4px 4px 0 0; }

/*
 * A site, shown the way it lies in the Realm.
 *
 * The card is printed portrait and played sideways, so a picture of it stood
 * upright is a picture you have to tilt your head at. The frame is the card's
 * own shape turned a quarter: as wide as the box, and 380/531 of that tall.
 * The picture inside is sized so that turning it fills the frame exactly —
 * its pre-rotation width is 380/531 of the box, which makes its height the
 * box's width, which is its width once turned.
 */
.hover.landscape { width: 340px; }

.hover.landscape .shot {
    position: relative;
    width: 100%;
    aspect-ratio: 531 / 380;
    overflow: hidden;
    border-radius: 4px 4px 0 0;
}

.hover.landscape .shot img {
    position: absolute;
    inset: 0;
    margin: auto;
    width: 71.56%;
    height: auto;
    border-radius: 4px;
    transform: rotate(90deg);
}

/* What the card says, set to be read rather than photographed. */
.hover-text {
    background: var(--panel);
    border: 1px solid var(--edge);
    border-top: none;
    border-radius: 0 0 4px 4px;
    padding: .5rem .6rem .6rem;
    font-size: .78rem;
    line-height: 1.35;
    color: var(--ink);
}

.hover img + .hover-text { border-top: none; }
.hover-text:first-child { border-top: 1px solid var(--edge); border-radius: 4px; }

.hover-name {
    font-family: var(--display);
    font-size: .9rem;
    color: var(--gold);
}

.hover-type {
    margin-bottom: .35rem;
    font-size: .72rem;
    font-style: italic;
    color: var(--ink-faint);
}

.hover-text p { margin: .3rem 0 0; }

/* ---------- scrollbars, so the dark does not break ---------- */

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: var(--ground-deep); }
::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: var(--edge-bright); }

@media (max-width: 1100px) {
    body[data-screen="table"] { overflow: auto; }
    #table { height: auto; }
    .table-grid, .builder-grid { grid-template-columns: minmax(0, 1fr); }
    .side, .board-column { height: auto; }
    .realm { height: auto; width: 100%; }
    .realm-holder { flex: none; }
    .builder-grid { height: auto; }
}

/* ---------- the game hall ---------- */

.hall {
    display: grid;
    grid-template-rows: auto auto auto minmax(10rem, 1fr);
    gap: .75rem;
    padding: .75rem;
    flex: 1;
    min-height: 0;
}

/* A long list of games must not push the chat off the screen either. */
.hall .panel.tables { max-height: 30vh; overflow-y: auto; }

.hall .panel.tables { padding-bottom: .35rem; }

.hall-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .92rem;
}

.hall-table th {
    text-align: left;
    font-family: var(--display);
    font-weight: normal;
    font-size: .82rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-faint);
    border-bottom: 1px solid var(--edge);
    padding: .2rem .5rem .3rem 0;
}

.hall-table td {
    padding: .3rem .5rem .3rem 0;
    border-bottom: 1px solid #1c2628;
    vertical-align: middle;
}

.hall-table tr:last-child td { border-bottom: none; }
.hall-table td:last-child { text-align: right; padding-right: 0; }
.hall-table td.empty {
    color: var(--ink-faint);
    font-style: italic;
    text-align: left;
}
.hall-table button { padding: .15rem .6rem; font-size: .85rem; }

/* The row of controls that makes a table. */
.hall-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .6rem;
}

.hall-controls label {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    font-size: .78rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

.hall-controls select { min-width: 9rem; }
.hall-controls #open-deck, .hall-controls #open-opponent { min-width: 13rem; }
.hall-controls .divider {
    width: 1px;
    align-self: stretch;
    background: var(--edge);
    margin: 0 .2rem;
}

button.linkish {
    background: none;
    border: none;
    color: var(--gold);
    font-family: var(--display);
    padding: .3rem .2rem;
    text-decoration: underline;
    text-underline-offset: 3px;
}

button.linkish:hover { background: none; color: var(--ink); }

/* Chat, and who is here to read it. */
.hall-bottom {
    display: grid;
    grid-template-columns: 1fr 14rem;
    gap: .75rem;
    min-height: 0;
}

.panel.chat {
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding-bottom: .5rem;
}

.chat-lines {
    flex: 1;
    overflow-y: auto;
    margin: 0 0 .5rem;
    padding: 0 .25rem 0 0;
    list-style: none;
    font-size: .92rem;
    line-height: 1.45;
}

.chat-lines li { padding: .05rem 0; }
.chat-when { color: var(--ink-faint); font-variant-numeric: tabular-nums; margin-right: .4rem; }
.chat-who { color: var(--gold); margin-right: .35rem; }
.chat-said { color: var(--ink); }

#chat-form { display: flex; gap: .5rem; }
#chat-form input { flex: 1; }

.panel.present { min-height: 0; overflow-y: auto; }
.list.plain { list-style: none; margin: 0; padding: 0; }
.list.plain li {
    padding: .15rem 0;
    font-size: .92rem;
    color: var(--ink-dim);
    border: none;
}
.list.plain li.you { color: var(--gold); }

/* A select that sits in a toolbar rather than filling a form. */
select.compact { width: auto; max-width: 16rem; }

/* Filters that stay pressed: any of the pressed ones in a group lets a card through. */
.pool-toggles { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; margin-bottom: .8rem; }
.toggle-group { display: inline-flex; gap: .3rem; }
button.toggle {
    padding: .2rem .6rem;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    opacity: .7;
}
button.toggle.on { opacity: 1; border-color: var(--gold); color: var(--gold); }

.avatar-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.avatar-row button { padding: 0 .5rem; line-height: 1.4; }

#shift-hint { align-self: center; font-size: .78rem; }

/* ---- takeback: a list of recent actions under the header button ---- */
.takeback-holder { position: relative; }
.takebacks {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 30;
  min-width: 18rem; max-width: 28rem; max-height: 60vh; overflow-y: auto;
  background: var(--panel-high, #1a1f23); border: 1px solid var(--gold-dim, #6b5a2e);
  border-radius: .4rem; padding: .35rem; box-shadow: 0 .6rem 1.6rem rgba(0,0,0,.5);
  display: flex; flex-direction: column; gap: .2rem;
}
.takebacks[hidden] { display: none; }
.takebacks-title { font-size: .8rem; letter-spacing: .04em; text-transform: uppercase;
  opacity: .7; padding: .2rem .5rem; }
.takeback-row {
  text-align: left; padding: .4rem .6rem; border-radius: .3rem;
  background: transparent; border: 1px solid transparent; color: inherit; cursor: pointer;
  font: inherit;
}
.takeback-row:hover { border-color: var(--gold-dim, #6b5a2e); background: rgba(255,255,255,.04); }
