/* Shared lobby theme (elemental dark-green). Used by the landing, logout, lobby and in-room pages.
   Promoted to a global stylesheet so every page shares the look (scoped .razor.css would not). */

/* ── Theme tokens ─────────────────────────────────────────────────────────────
   Per-spirit skinning hangs off CSS custom properties. These are the DEFAULTS: the
   universal yellow "awaiting input" cue used by every attention blink, pending
   border, and act-here highlight. A spirit theme overrides them on a scoped wrapper
   so its panels can recolour the attention flash to something that still reads
   against the skin (e.g. an amber spirit whose panels are already yellow-ish).
     --flash-color : solid attention colour.
     --flash-rgb   : the same colour as raw "r, g, b" channels, for the
                     rgba(var(--flash-rgb), α) alpha variants. Keep it in sync with
                     --flash-color when overriding a theme. */
:root {
    --flash-color: #ffd36b;
    --flash-rgb: 255, 211, 107;

    /* A faint grayscale grain with elongated VERTICAL streaks (like aged paper / brushed woodgrain), not busy
       white-noise. baseFrequency 'x y' with x≫y stretches the noise into vertical fibres; numOctaves 1 keeps it
       smooth. Layered subtly (multiply on parchment, soft-light on the dark backdrop). Dial via the rect opacity. */
    --paper-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5 0.012' numOctaves='1' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23p)' opacity='0.28'/%3E%3C/svg%3E");

    /* The in-game panels' texture: same vertical-fibre grain but dialled way back (rect opacity 0.28 → 0.11)
       so the streaks are barely perceptible on the parchment. */
    --paper-texture-subtle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5 0.012' numOctaves='1' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23p)' opacity='0.11'/%3E%3C/svg%3E");
}

body {
    color: #efe7d6;
    font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
    background: #191410;
}

.landing {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1.5rem;
    box-sizing: border-box;
    background:
        radial-gradient(1200px 600px at 50% -10%, rgba(150, 110, 60, 0.35), transparent 60%),
        radial-gradient(900px 500px at 90% 110%, rgba(120, 85, 45, 0.30), transparent 55%),
        linear-gradient(160deg, #191410 0%, #221a12 45%, #1b1510 100%);
}

.hero {
    width: 100%;
    max-width: 34rem;
    text-align: center;
}

.hero-title {
    margin: 0;
    font-size: clamp(2.75rem, 8vw, 4.5rem);
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.05;
    background: linear-gradient(180deg, #f4f1d0 0%, #d8c49a 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-shadow: 0 2px 24px rgba(211, 194, 158, 0.15);
}

.hero-tagline {
    margin: 1.5rem auto 0;
    max-width: 30rem;
    font-size: 1.05rem;
    line-height: 1.6;
    color: #cabb9f;
}

.login,
.account {
    margin-top: 2.75rem;
    max-width: 22rem;
    margin-left: auto;
    margin-right: auto;
}

.account-welcome {
    margin: 0 0 1rem;
    font-size: 1.05rem;
    color: #e2d6bf;
}

.login-tabs {
    display: flex;
    gap: 0.25rem;
    padding: 0.25rem;
    background: rgba(0, 0, 0, 0.25);
    border-radius: 0.6rem;
    margin-bottom: 1.25rem;
}

.login-tab {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.55rem 0.75rem;
    border: none;
    border-radius: 0.45rem;
    background: transparent;
    color: #b3a486;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.login-tab.is-active {
    background: rgba(211, 194, 158, 0.16);
    color: #efe7d6;
}

.login-form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.login-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.75rem 0.9rem;
    border-radius: 0.55rem;
    border: 1px solid rgba(211, 194, 158, 0.25);
    background: rgba(255, 255, 255, 0.06);
    color: #efe7d6;
    font-size: 0.95rem;
}

.login-input::placeholder { color: #93876f; }

.login-input:focus {
    outline: none;
    border-color: rgba(211, 194, 158, 0.7);
    background: rgba(255, 255, 255, 0.09);
}

.login-input:disabled { opacity: 0.6; }

.login-error {
    margin: 0;
    font-size: 0.85rem;
    color: #ffb4ab;
    text-align: left;
}

.login-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1.5rem 0 1rem;
    color: #8a7d64;
    font-size: 0.8rem;
}

.login-divider::before,
.login-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: rgba(211, 194, 158, 0.2);
}

.login-buttons {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin: 0 auto;
}

.login-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.8rem 1.25rem;
    border-radius: 0.6rem;
    border: 1px solid transparent;
    font-size: 0.95rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: transform 0.06s ease, filter 0.15s ease, opacity 0.15s ease;
}

.login-btn:hover:not(:disabled) {
    filter: brightness(1.08);
    transform: translateY(-1px);
}

.login-btn:active:not(:disabled) { transform: translateY(0); }

.login-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.login-btn--primary {
    background: linear-gradient(180deg, #5fb878 0%, #3f8f5a 100%);
    color: #120d08;
    margin-top: 0.25rem;
}

.login-btn--google { background: #ffffff; color: #1f2937; }
.login-btn--discord { background: #5865f2; color: #ffffff; }

.login-btn--guest {
    background: transparent;
    color: #e2d6bf;
    border-color: rgba(211, 194, 158, 0.4);
}

/* ---------- Lobby ---------- */

.lobby {
    min-height: 100vh;
    box-sizing: border-box;
    padding: 1.5rem clamp(1rem, 5vw, 4rem) 4rem;
    background:
        radial-gradient(1000px 500px at 90% -10%, rgba(150, 110, 60, 0.25), transparent 60%),
        linear-gradient(160deg, #191410 0%, #221a12 50%, #1b1510 100%);
}

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    max-width: 60rem;
    margin: 0 auto 2rem;
}

.topbar-brand {
    font-size: 1.4rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: #e6d9bf;
    text-decoration: none;
}

.topbar-user {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    font-size: 0.95rem;
    color: #cabb9f;
}

.topbar-user strong { color: #efe7d6; }

.lobby-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.25rem;
    max-width: 60rem;
    margin: 0 auto;
    align-items: start;
}

/* Panels read the spirit-skin vars (set on the scoped spirit + decision panels) but fall back to the neutral
   look everywhere the theme isn't applied — so an un-themed panel is unchanged. */
.panel {
    background-color: rgba(238, 225, 195, 0.05);
    background-image: var(--paper-texture), var(--theme-panel-bg, none);
    background-blend-mode: soft-light, normal;
    background-size: 180px 180px, auto;
    border: 1px solid var(--theme-panel-border, rgba(211, 194, 158, 0.18));
    border-radius: 0.8rem;
    padding: 1.5rem;
}

.panel h2 {
    margin: 0 0 1rem;
    font-size: 1.1rem;
    color: #efe7d6;
}

.panel .muted {
    margin: 0 0 1.25rem;
    color: #a89a80;
    font-size: 0.9rem;
    line-height: 1.5;
}

.field-row {
    display: flex;
    gap: 0.6rem;
}

.field-row .login-input { flex: 1; }
.field-row .login-btn { flex: 0 0 auto; }

.room-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.room-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.75rem 0.9rem;
    border-radius: 0.55rem;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(211, 194, 158, 0.12);
}

.room-row-code {
    font-weight: 700;
    letter-spacing: 0.12em;
    color: #e6d9bf;
}

.room-row-meta {
    font-size: 0.82rem;
    color: #a89a80;
}

.room-empty {
    color: #93876f;
    font-size: 0.9rem;
    font-style: italic;
}

.badge {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 1rem;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.badge--host { background: rgba(95, 184, 120, 0.22); color: #aef0c0; }
.badge--you  { background: rgba(120, 170, 255, 0.18); color: #bcd4ff; }
.badge--live { background: rgba(255, 196, 120, 0.18); color: #ffd9a8; }
.badge--off  { background: rgba(255, 120, 120, 0.16); color: #ffb4ab; }

/* ---------- In-room ---------- */

.room {
    min-height: 100vh;
    box-sizing: border-box;
    padding: 1.5rem clamp(1rem, 5vw, 4rem) 4rem;
    background:
        radial-gradient(1000px 500px at 10% -10%, rgba(150, 110, 60, 0.22), transparent 60%),
        linear-gradient(160deg, #191410 0%, #221a12 50%, #1b1510 100%);
}

.room-head {
    max-width: 52rem;
    margin: 0 auto 1.5rem;
    text-align: center;
}

.room-code-big {
    font-size: clamp(2.5rem, 7vw, 3.75rem);
    font-weight: 800;
    letter-spacing: 0.35em;
    margin: 0.25rem 0 0;
    color: #f4f1d0;
    text-indent: 0.35em;
}

.room-code-label {
    margin: 0;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    font-size: 0.75rem;
    color: #9c8b72;
}

.room-body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.25rem;
    max-width: 52rem;
    margin: 0 auto;
    align-items: start;
}

.player-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.player {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 0.8rem;
    border-radius: 0.55rem;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(211, 194, 158, 0.12);
}

.player-name { flex: 1; color: #efe7d6; }
.player.is-off .player-name { color: #a89a80; }

/* Per-spirit sub-rows beneath a player in the roster */
.player-spirit {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    margin: 0.2rem 0 0.2rem 1.4rem;
    padding: 0.2rem 0.8rem;
    border-left: 2px solid rgba(211, 194, 158, 0.18);
    font-size: 0.85rem;
}

.player-spirit .spirit-name { color: #e6d9bf; }
.player-spirit .spirit-board { color: #a89a80; }

.seat-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.seat-list li {
    display: flex;
    justify-content: space-between;
    padding: 0.45rem 0.7rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.04);
    font-size: 0.9rem;
}

.seat-list .seat-spirit { color: #e6d9bf; }
.seat-list .seat-board  { color: #a89a80; }

.btn-row {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}

.start-note {
    margin: 0.75rem 0 0;
    font-size: 0.85rem;
    color: #ffd9a8;
}

.in-progress {
    text-align: center;
    color: #aef0c0;
    font-weight: 600;
}

/* ---------- Settings editor ---------- */

.setup-group {
    margin-bottom: 1.4rem;
}

.setup-group h3 {
    margin: 0 0 0.6rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #9c8b72;
}

.check-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.3rem 0;
    color: #e2d6bf;
    font-size: 0.92rem;
}

.check-row input[type="checkbox"] {
    accent-color: #5fb878;
    width: 1.05rem;
    height: 1.05rem;
}

.check-row input:disabled + span,
.check-row.is-readonly {
    color: #a89a80;
}

.seat-edit {
    display: flex;
    align-items: center;
    flex-wrap: wrap;   /* board + spirit + colour controls wrap to a second line rather than overflow */
    gap: 0.5rem;
    padding: 0.5rem 0.7rem;
    border-radius: 0.55rem;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(211, 194, 158, 0.1);
    margin-bottom: 0.45rem;
}

.seat-edit-name {
    flex: 1;
    color: #efe7d6;
    font-size: 0.9rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mini-select {
    background: rgba(255, 255, 255, 0.06);
    color: #efe7d6;
    border: 1px solid rgba(211, 194, 158, 0.25);
    border-radius: 0.4rem;
    padding: 0.35rem 0.5rem;
    font-size: 0.85rem;
    max-width: 8.5rem;
}

.mini-select:disabled {
    opacity: 0.55;
}

.mini-select option {
    color: #111;
}

.mini-select--color {
    max-width: 6rem;
}

/* Presence-colour preview chip next to the seat's colour picker. */
.seat-color-swatch {
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.7);
    flex: 0 0 auto;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}

.seat-block {
    margin-bottom: 0.9rem;
}

.seat-owner {
    font-size: 0.85rem;
    color: #cabb9f;
    margin-bottom: 0.35rem;
}

.seat-remove {
    background: transparent;
    border: 1px solid rgba(255, 120, 120, 0.4);
    color: #ffb4ab;
    border-radius: 0.4rem;
    width: 1.9rem;
    height: 1.9rem;
    cursor: pointer;
    font-size: 1.05rem;
    line-height: 1;
}

.seat-remove:hover {
    background: rgba(255, 120, 120, 0.12);
}

.add-seat {
    background: transparent;
    border: 1px dashed rgba(211, 194, 158, 0.4);
    color: #aef0c0;
    border-radius: 0.5rem;
    padding: 0.4rem 0.75rem;
    font-size: 0.82rem;
    cursor: pointer;
    transition: background 0.15s ease;
}

.add-seat:hover {
    background: rgba(211, 194, 158, 0.08);
}

/* ============================ In-game view ============================ */

.game {
    min-height: 100vh;
    box-sizing: border-box;
    padding: 1.25rem clamp(1rem, 4vw, 3rem) 3rem;
    background: #0d0b08;   /* fallback base; the hero art rides the ::before layer below */
    position: relative;
    isolation: isolate;    /* own stacking context so the -1 backdrop paints above this bg but below content */
}
/* The hero art envelopes the screen (cover — no aspect stretching; whatever overflows is cropped) on a fixed
   layer BEHIND the content, so it can be gently blurred without softening the panels/text on top (a subtle blur
   calms the busy art). A dark wash sits over it (the art is bright cyan) so the parchment panels keep contrast;
   the layer bleeds past the viewport (negative inset) so the blur doesn't reveal a soft edge. */
.game::before {
    content: "";
    position: fixed;
    inset: -20px;
    z-index: -1;
    background:
        linear-gradient(rgba(10, 12, 14, 0.42), rgba(10, 12, 14, 0.42)),
        url("art/SpiritIslandHeroArt.jpg") center center / cover no-repeat;
    filter: blur(20px);
    pointer-events: none;
}

.game-boards {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
    margin: 0 auto 1.5rem;
    max-width: 96rem;
}

.board-wrap {
    width: 62rem;
    /*max-width: 62rem;*/
}

.board-title {
    margin: 0 0 0.5rem;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: #9c8b72;
    text-align: center;
}

.board-svg {
    width: 100%;
    height: auto;
    max-height: 80vh;
    background: rgba(0, 0, 0, 0.25);
    border: 1px solid rgba(58, 38, 20, 0.45);
    border-radius: 2px;
    display: block;
}

.land {
    stroke: #211a13;
    stroke-width: 1.5;
    cursor: pointer;
    transition: fill-opacity 0.12s ease;
}

/* Pan/zoom island: swallow browser scroll/gesture handling so wheel-zoom and drag-pan reach board-camera.js;
   grab cursor signals the surface is draggable. Coastline strokes stay a constant width under zoom. */
.island-svg { touch-action: none; cursor: grab; }
.island-svg.is-panning { cursor: grabbing; }
.island-svg .land { vector-effect: non-scaling-stroke; }

/* "Fit" resets the camera. Floats in the board panel's top-left over the island. */
.board-fit-btn {
    position: absolute;
    top: 0.6rem;
    left: 0.6rem;
    z-index: 3;
}

.land:hover { fill-opacity: 0.78; }
.land.is-selected { stroke: #f4f1d0; stroke-width: 3.5; }

.land--jungle   { fill: #3f7a44; }
.land--wetland  { fill: #4f93a8; }
.land--sand     { fill: #cdb46a; }
.land--mountain { fill: #9a948c; }
.land--ocean    { fill: #2f5d8c; }
.land--none     { fill: #6e675c; }

/* Tile-targeting (PickTileQuestion): valid lands turn white & clickable, the rest grey out.
   Two-class selectors outrank the single-class .land--* terrain fills. */
.land.is-target-valid { fill: #f1f4ee; stroke: var(--flash-color); stroke-width: 3; cursor: pointer; }
.land.is-target-valid:hover { fill: #ffffff; fill-opacity: 1; }
.land.is-target-invalid { fill: #4a4640; fill-opacity: 0.7; cursor: default; }

.land-num {
    fill: #161009;
    fill-opacity: 0.65;
    font-weight: 800;
    font-size: 13px;
    pointer-events: none;
}

/* Defense badge (TileDefenseBadge): a steel shield + the land's Defense value, on any defended land. */
.tile-defense__shield {
    fill: #6fa8d4;
    stroke: #12283b;
    stroke-width: 1.3;
    stroke-linejoin: round;
}
.tile-defense__chevron {
    fill: none;
    stroke: #eaf3fb;
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.85;
}
.tile-defense__num {
    fill: #eaf3fb;
    stroke: #12283b;
    stroke-width: 0.7;
    paint-order: stroke;
    font-weight: 800;
    font-size: 15px;
}

/* Board tokens (BoardTokenGroup). A translucent chip sits behind every glyph so figures read on any
   terrain and give a comfortable click target; it brightens and rings on hover. */
.board-token { cursor: pointer; }
.board-token__chip {
    fill: rgba(8, 16, 12, 0.5);
    stroke: rgba(0, 0, 0, 0.25);
    stroke-width: 0.75;
    transition: fill 0.1s ease;
}
.board-token:hover .board-token__chip {
    fill: rgba(8, 16, 12, 0.82);
    stroke: #f4f1d0;
    stroke-width: 1.4;
}
/* A gather (or other token-pick) highlights its acceptable tokens with a pulsing yellow ring — click to pick. */
@keyframes gather-ring-blink {
    0%, 100% { stroke: rgba(var(--flash-rgb), 0.55); stroke-width: 2; }
    50%      { stroke: var(--flash-color); stroke-width: 3.2; }
}
.board-token__gather-ring {
    fill: none;
    stroke: var(--flash-color);
    animation: gather-ring-blink 1.2s ease-in-out infinite;
}
.board-token.is-gather-target { cursor: pointer; }
.board-token.is-gather-target:hover .board-token__chip { stroke: var(--flash-color); stroke-width: 1.8; }

/* ── Board change overlay (what changed since the previous cursor stop) ──────────
   A calm, NON-blinking visual language, deliberately distinct from the yellow "you have something to do"
   attention pulse. Changed pieces peel off into their own stack next to the untouched one and get a coloured
   border whose COLOUR reads good(green)/bad(red) FOR THE PLAYERS (a dead Invader is good, a new Invader is
   bad), while a "+"/"×" badge says added vs removed. Pieces that left are faded "ghosts" on the tile they
   vacated. So a jump across Ravage/Build/Explore shows the dead and the newly-placed instead of a silent swap. */
.land.is-changed { stroke: #ffd98a; stroke-width: 3; }

.board-token__change-ring { pointer-events: none; }

/* The "+"/"×" change badge in the piece's upper-left. */
.board-token__marker-bg { stroke: rgba(255, 255, 255, 0.92); stroke-width: 1; pointer-events: none; }
.board-token__marker { fill: #fff; font-size: 11px; font-weight: 900; pointer-events: none; }

/* Ghosts: pieces gone from a tile. Faded and non-interactive; the change ring carries the good/bad colour. */
.board-token.is-ghost { pointer-events: none; opacity: 0.4; }
.board-token.is-ghost .board-token__chip { fill: rgba(24, 18, 16, 0.4); stroke: rgba(0, 0, 0, 0.3); }

/* The overlay toggle button; lit when the overlay is on. */
.board-changes-btn { position: absolute; top: 0.6rem; left: 4.2rem; z-index: 3; }
.board-changes-btn.is-on { border-color: #ffd98a; color: #ffe6b0; }
.board-token__sym {
    font-size: 10px;
    font-weight: 800;
    pointer-events: none;
}
.board-token__count-bg {
    fill: #211a13;
    stroke: rgba(255, 255, 255, 0.85);
    stroke-width: 1;
}
.board-token__count {
    fill: #fff;
    font-size: 9px;
    font-weight: 800;
    pointer-events: none;
}

.game-side {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1.25rem;
    max-width: 75rem;
    margin: 0 auto;
    align-items: start;
}

.spirit-card {
    padding: 0.5rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(211, 194, 158, 0.12);
    margin-bottom: 0.6rem;
}

.spirit-card-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}

.spirit-card-name { color: #efe7d6; font-weight: 600; }
.spirit-card-board { color: #a89a80; font-size: 0.82rem; }

.spirit-card-stats {
    display: flex;
    gap: 1rem;
    margin-top: 0.4rem;
    color: #e6d9bf;
    font-size: 0.9rem;
}

.spirit-card-elements {
    margin-top: 0.35rem;
    font-size: 0.8rem;
    color: #b3a486;
}

.land-detail-name {
    margin: 0 0 0.25rem;
    color: #efe7d6;
    font-weight: 600;
}

.token-list {
    list-style: none;
    margin: 0.5rem 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.token-list li {
    display: flex;
    justify-content: space-between;
    padding: 0.3rem 0.5rem;
    border-radius: 0.45rem;
    background: rgba(255, 255, 255, 0.05);
    font-size: 0.88rem;
    color: #e2d6bf;
}

/* ── Timeline scrubber ─────────────────────────────────────────────── */
.game-main {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
}

.timeline {
    background: rgba(22, 14, 8, 0.72);
    border: 2px solid #573a1c;
    border-radius: 2px;
    padding: 0.5rem;
    /* The track is a slider-like drag surface; its chip/label text is never meant to be
       selected. Suppress selection so a drag (even one starting just off the track) never
       highlights timeline text. */
    user-select: none;
    -webkit-user-select: none;
}

/* One row — step-back │ track │ step-forward + Continue — so the bar costs a single row of height.
   The phase title is lifted out of the flow entirely (see .timeline-phase below). */
.timeline { position: relative; }   /* anchor for the floating phase flag */
.timeline-row { display: flex; align-items: center; gap: 0.5rem; }
.timeline-row .timeline-track { flex: 1 1 auto; min-width: 0; }

.timeline-nav { display: flex; align-items: center; gap: 0.45rem; flex: 0 0 auto; }

/* The phase title is a flag pinned above the bar's top centre, overlapping the board view a little — it's
   the "where are we" beacon, so it gets to be bigger without costing the timeline a row. pointer-events off:
   it's informational only and must never block clicks on the board/track beneath its overlap. */
.timeline-phase {
    position: absolute;
    left: 50%;
    top: -1.6rem;
    transform: translateX(-50%);
    z-index: 4;
    pointer-events: none;
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    padding: 0.25rem 1.1rem;
    background: rgba(22, 14, 8, 0.88);
    border: 2px solid #573a1c;
    border-radius: 2px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
    line-height: 1.15;
    white-space: nowrap;
    /* Plays once each time the flag remounts (TimelineBar bumps its @key on a round/phase change), pulsing to
       draw the eye to time passing. Keyframes keep the translateX(-50%) centring so it doesn't drift. */
    animation: timeline-phase-pulse 0.65s ease-out;
}
@keyframes timeline-phase-pulse {
    0%   { transform: translateX(-50%) scale(1);    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); border-color: #573a1c; }
    28%  { transform: translateX(-50%) scale(1.09); box-shadow: 0 3px 18px rgba(230, 192, 90, 0.6); border-color: #e6c05a; }
    100% { transform: translateX(-50%) scale(1);    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); border-color: #573a1c; }
}
.timeline-phase-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: #9a8d76; }
.timeline-phase-name { font-size: 1.3rem; font-weight: 700; color: #efe7d6; }

.tl-btn {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(211, 194, 158, 0.18);
    color: #e2d6bf;
    border-radius: 0.45rem;
    padding: 0.3rem 0.6rem;
    cursor: pointer;
    font-size: 0.9rem;
}
.tl-btn:hover:not(:disabled) { background: rgba(211, 194, 158, 0.16); }
.tl-btn:disabled { opacity: 0.35; cursor: default; }
.tl-btn--commit { font-weight: 600; color: #161009; background: #aec2a0; border-color: #aec2a0; transition: filter 0.15s ease; }
/* Brighten on hover (matching .login-btn) rather than swapping to a paler tint, which read as a muddy darken.
   Not while disabled — a greyed Continue must read as inert (the :disabled opacity handles the grey-out). */
.tl-btn--commit:not(:disabled):hover { background: #aec2a0; filter: brightness(1.08); }

/* History mode: the cursor has been scrubbed back into committed space; the whole timeline is read-only. */
.timeline.is-history .timeline-phase-label { color: #e6c05a; }

.timeline-track {
    display: flex;
    align-items: stretch;
    /* The live tip sits at the right; sealed history extends left. Anchor to the right so that when the
       capped window is wider than the bar the *recent* end stays visible and the oldest steps clip off the
       left (the track hides its overflow — no horizontal scrollbar, see overflow-x below). On a short
       timeline that fits, the groups simply sit flush right. */
    justify-content: flex-end;
    /* A gap between step groups so the two adjacent-but-distinct cursor stops at a phase boundary —
       end-of-step (s, count) and next-step entry (s+1, 0) — read as two separate snap points rather than
       one seam. Proposals *within* a step stay flush (see .tl-props). */
    gap: 5px;
    overflow-x: hidden;
    overflow-y: hidden;
    border-radius: 0.4rem;
    /* A lane below the cells for the caret triangle to hang in (it points up into the timeline, body below).
       overflow-y:hidden clips at the padding edge, so a triangle within this padding stays visible. */
    padding-bottom: 9px;
    /* The whole track is a drag surface (see timeline-drag.js); chips keep their own pointer cursor. */
    cursor: grab;
}
.timeline-track.is-dragging,
.timeline-track.is-dragging * { cursor: grabbing; }

.timeline-empty {
    color: #8a7d64;
    font-size: 0.82rem;
    font-style: italic;
    padding: 0.4rem 0.2rem;
}

/* One column-group per timing step: header on top, flush row of proposals beneath.
   Each group is self-contained, so the header spans exactly its proposals and the seams line up. */
.tl-group {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    min-width: 0;
    /* No overflow clipping: the active seam's caret triangle hangs below the props row into the track's
       caret lane, so the group must not clip its bottom. The gap between groups marks phase boundaries. */
}

.tl-head, .tl-prop {
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
    text-align: center;
    white-space: nowrap;
    padding: 0.22rem 0.5rem;
}

/* Top row — the timing step. Stretches to the full group width (= width of its proposals). */
.tl-head {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
    border-bottom: 1px solid rgba(0, 0, 0, 0.25);
}

/* Bottom row — proposals, sitting flush with one another. */
.tl-props {
    display: flex;
    align-items: stretch;
    gap: 0;
    flex: 1 1 auto;
}
.tl-prop { font-size: 0.8rem; flex-grow: 1.0; }
.tl-prop:hover { filter: brightness(1.18); }
.tl-prop.is-skipped { text-decoration: line-through; opacity: 0.7; }
.tl-prop.is-inactive { opacity: 0.45; }

/* My own locked-in proposal: a soft amber underline marks the ones this seat may reorder. */
.tl-prop.is-mine { box-shadow: inset 0 -2px 0 rgba(255, 211, 107, 0.75); }

/* Reorder wrapper — keeps the ‹ pill › trio behaving as one flush flex cell in the proposal row. */
.tl-prop-wrap {
    display: flex;
    align-items: stretch;
    flex-grow: 1.0;
}
.tl-prop-wrap .tl-prop { flex-grow: 1.0; }

/* Nudge arrows flanking a reorderable proposal. Faint until the row is hovered, then legible. */
.tl-reorder {
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0 0.2rem;
    opacity: 0.35;
    transition: opacity 0.12s ease;
}
.tl-prop-wrap:hover .tl-reorder { opacity: 0.8; }
.tl-reorder:hover:not(:disabled) { opacity: 1; filter: brightness(1.3); }
.tl-reorder:disabled { opacity: 0.12; cursor: default; }

/* Alternating step tones; proposals get two paired shades of their step's tone. */
.tl-group.tone-a .tl-head     { background: rgba(120, 110, 78, 0.6);  color: #efe7d6; }
.tl-group.tone-a .tl-prop.shade-0 { background: rgba(120, 110, 78, 0.22); color: #e6d9bf; }
.tl-group.tone-a .tl-prop.shade-1 { background: rgba(120, 110, 78, 0.36); color: #e2d6bf; }

.tl-group.tone-b .tl-head     { background: rgba(58, 104, 122, 0.6);  color: #eaf1f3; }
.tl-group.tone-b .tl-prop.shade-0 { background: rgba(58, 104, 122, 0.22); color: #cde4eb; }
.tl-group.tone-b .tl-prop.shade-1 { background: rgba(58, 104, 122, 0.36); color: #dceaef; }

/* Sealed (committed) steps shown in History mode — greyed to read as non-interactive. The greying is
   applied to the head/proposal cells only, so the bright cursor caret between them stays visible. */
.tl-group.is-sealed .tl-head,
.tl-group.is-sealed .tl-prop { filter: grayscale(0.6) brightness(0.72); }
.tl-group.is-sealed .tl-head { font-style: italic; }

/* Seam = one cursor stop, rendered at every position between/around proposals. Normally invisible (a thin
   spacer); the active one is the bright caret and doubles as the drag handle. */
.tl-seam {
    flex: 0 0 auto;
    width: 2px;
    align-self: stretch;
    background: transparent;
    position: relative;
}

/* While dragging the caret, reveal every seam as a faint tick so the player sees the available snap points. */
.timeline-track.is-dragging .tl-seam { background: rgba(255, 211, 107, 0.28); }

/* Active seam — where the cursor currently rests. A thin boundary line through the cells plus a triangle
   caret hanging in the lane below: fat base at the bottom, tip pointing up so it just overlaps the timeline. */
.tl-seam.is-active { background: #ffd36b; }
.tl-seam.is-active::before {
    content: "";
    position: absolute;
    left: 50%;
    top: calc(100% - 2px);          /* tip overlaps the timeline ~2px; the body hangs below in the lane */
    transform: translateX(-50%);
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-bottom: 9px solid #ffd36b;   /* upward-pointing triangle: base at bottom, tip at top */
}

/* ── Seat / spirit view switcher ───────────────────────────────────── */
.seat-switch {
    display: flex;
    gap: 0.3rem;
    margin: 0 auto 0 1rem;
}

.seat-tab {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(211, 194, 158, 0.18);
    color: #cdbfa4;
    border-radius: 0.45rem;
    padding: 0.3rem 0.5rem;
    cursor: pointer;
    font-size: 0.85rem;
}
.seat-tab:hover { background: rgba(211, 194, 158, 0.14); }
.seat-tab.is-active {
    background: rgba(211, 194, 158, 0.22);
    color: #efe7d6;
    border-color: rgba(211, 194, 158, 0.5);
    font-weight: 600;
}

.spirit-card.is-viewing {
    border-color: rgba(211, 194, 158, 0.5);
    box-shadow: inset 0 0 0 1px rgba(211, 194, 158, 0.25);
}

/* ── Question (decision) panel ──────────────────────────────────────── */
.q-stack { display: flex; flex-direction: column; gap: 0.4rem; }

/* Minimised question card — prompt on top, current answer beneath, ✕ on the right once answered.
   Click the card to open the full modal / re-arm its board flow; click the ✕ to withdraw the answer. */
.q-card {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    text-align: left;
    background: rgba(255, 255, 255, 0.25);
    border: 1px solid rgba(58, 38, 20, 0.35);
    border-left-width: 3px;
    border-radius: 2px;
    padding: 0.45rem 0.6rem;
    cursor: pointer;
    color: #190f04;
}
.q-card:hover { background: rgba(255, 255, 255, 0.4); }
/* Unanswered questions carry the universal yellow to-do blink. */
.q-card.is-pending { border-left-color: var(--flash-color); animation: attention-blink 1.3s ease-in-out infinite; }
.q-card.is-answered { border-left-color: #aec2a0; }
.q-card-main { display: flex; flex-direction: column; gap: 0.15rem; flex: 1 1 auto; min-width: 0; }
.q-card-prompt { font-size: 0.85rem; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.q-card-answer { font-size: 0.78rem; color: #54401d; }
.q-card.is-answered .q-card-answer { color: #aec2a0; }

/* Cancel-answer ✕ — withdraws the explicit answer; effects roll back (presence returns to the track). */
.q-card-cancel {
    flex: 0 0 auto;
    width: 1.3rem;
    height: 1.3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 0.35rem;
    color: #54401d;
    font-size: 0.78rem;
    line-height: 1;
}
.q-card-cancel:hover { background: rgba(207, 75, 59, 0.25); color: #f3cdc6; }

.q-confirm { width: 100%; margin-top: 0.6rem; }

/* Full-question modal. */
.q-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(6, 14, 10, 0.62);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 50;
}
.q-modal {
    background: #1e1710;
    border: 1px solid rgba(211, 194, 158, 0.28);
    border-radius: 0.5rem;
    padding: 0.5rem;
    max-width: 30rem;
    width: calc(100% - 2rem);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
.q-modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.8rem; }
/* Optional-question Skip sits at the right of the modal header, just before the ✕ close. */
.q-modal-skip { margin-left: auto; flex: 0 0 auto; }
.q-modal-prompt { font-size: 1rem; font-weight: 600; color: #efe7d6; line-height: 1.3; }
.q-modal-close {
    background: none; border: none; color: #a89a80; cursor: pointer;
    font-size: 1.1rem; line-height: 1; padding: 0.1rem 0.3rem;
}
.q-modal-close:hover { color: #efe7d6; }

/* A power-card choice modal is wider than the option modal so several cards fit side-by-side. */
.q-modal--cards { max-width: min(72rem, calc(100% - 2rem)); }
.q-cardpick-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    justify-content: center;
    max-height: 75vh;
    overflow-y: auto;
}
/* Each candidate is the full PowerCardView wrapped in a clickable frame; the selected one is ringed green. */
.q-cardpick {
    cursor: pointer;
    border-radius: 0.55rem;
    border: 2px solid transparent;
    transition: transform 0.12s ease, border-color 0.12s ease;
}
.q-cardpick:hover { transform: translateY(-4px); border-color: rgba(211, 194, 158, 0.45); }
.q-cardpick.is-selected { border-color: #aec2a0; box-shadow: 0 0 0 2px rgba(174, 194, 160, 0.4); }
/* The card inside the picker is display-only — its own (non-declarable) click is a no-op, so the frame's
   click drives the choice. */
.q-cardpick .pcard { cursor: pointer; }

/* Element picker (a "+Any" / cross-player element choice): the allowed elements as clickable icon tiles. */
.q-elementpick-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    justify-content: center;
    padding: 0.4rem 0;
}
.q-elementpick {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.4rem;
    border-radius: 0.55rem;
    border: 2px solid transparent;
    background: transparent;
    transition: transform 0.12s ease, border-color 0.12s ease;
}
.q-elementpick:hover { transform: translateY(-3px); border-color: rgba(211, 194, 158, 0.45); }
.q-elementpick.is-selected { border-color: #aec2a0; box-shadow: 0 0 0 2px rgba(174, 194, 160, 0.4); }
/* The picker draws the element via the shared {Tag} DSL (a .card-sym SVG) — upsize it from its inline default. */
.q-elementpick .card-sym { width: 44px; height: 44px; }

.q-options { display: flex; flex-direction: column; gap: 0.4rem; }
.q-option {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(211, 194, 158, 0.2);
    color: #e2d6bf;
    border-radius: 0.5rem;
    padding: 0.5rem;
    cursor: pointer;
    text-align: left;
    font-size: 0.9rem;
}
.q-option:hover { background: rgba(211, 194, 158, 0.16); }
.q-option.is-selected {
    background: #aec2a0;
    border-color: #aec2a0;
    color: #161009;
    font-weight: 600;
}

/* Card whose question the board is currently targeting. */
.q-card.is-targeting {
    border-left-color: var(--flash-color);
    box-shadow: inset 0 0 0 1px rgba(var(--flash-rgb), 0.4);
}
.q-card.is-targeting .q-card-answer { color: var(--flash-color); }

/* Banner above the boards while picking a tile. */
.targeting-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    background: rgba(var(--flash-rgb), 0.12);
    border: 1px solid rgba(var(--flash-rgb), 0.4);
    border-radius: 0.5rem;
    padding: 0.45rem 0.5rem;
    margin-bottom: 0.6rem;
    color: #f0e3c0;
    font-size: 0.9rem;
}

/* ── Hand & power cards ────────────────────────────────────────────── */
.hand-count {
    margin-left: 0.4rem;
    font-size: 0.85rem;
    font-weight: 500;
    color: #54401d;
}

/* Cards inside a pile sit in a horizontal row; the pile scrolls vertically if a card is taller than the band. */
.pile-cards {
    display: flex;
    flex-direction: row;
    gap: 0.7rem;
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
}

/* The card echoes the physical tones: a deep dark-brown border, a woody element strip down the left, and a
   light parchment body. The parchment lives on .pcard-main; .pcard itself is just the bordered, clipped
   frame so the head band and the strip run cleanly into the rounded corners. */
.pcard {
    flex: 0 0 auto;
    width: 13.5rem;
    aspect-ratio: 5 / 7;   /* portrait, matching the physical cards (and the card-back tiles) */
    display: flex;
    flex-direction: column;
    border-radius: 0.5rem;
    background: #f0e6cf;
    border: 2px solid #3a2614;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* Speed tints the head band behind the cost + title: fast = red, slow = blue (matching the physical
   cards). Lives on the head, not the border, so the yellow attention/resolving blinks never hide it. */
.pcard--fast .pcard-head { background: linear-gradient(180deg, #b6473c, #8f3429); }
.pcard--slow .pcard-head { background: linear-gradient(180deg, #3f6aa3, #2d4f80); }

/* Declarable: clickable to toggle play this turn. Declared (an In Play card): a calm green accent — the
   pile move already says "playing", so the accent just marks it toggleable-off. The gold glow is reserved
   for a RESOLVING card (its targeting question is waiting on the player). */
.pcard--declarable {
    cursor: pointer;
    transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}
.pcard--declarable:hover { transform: translateY(-4px); }

.pcard--declared { border-color: #7f9769; }
.pcard--declared .pcard-main { background: rgba(120, 138, 100, 0.16); }

/* The universal "you have something to do here" cue is a yellow BLINK (same period everywhere). For cards
   it pulses the body tint + glow — never the border, so the declared green and the speed band stay visible.
   Applied to: a resolving card (its targeting question awaits) and a hand card the player could still play
   (plays remaining + affordable). */
@keyframes pcard-attention-blink {
    0%, 100% { border-color: #3a2614; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); }
    50%      { border-color: var(--flash-color); box-shadow: 0 0 14px 3px rgba(var(--flash-rgb), 0.9); }
}
.pcard--resolving,
.pcard--attention { animation: pcard-attention-blink 1.3s ease-in-out infinite; }

/* Per-card targeting flow footer: Choose-target button, or the chosen land + cancel ✕. */
.pcard-targetflow {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: auto;
    padding-top: 0.4rem;
    border-top: 1px solid rgba(58, 38, 20, 0.25);
}
/* Solid amber action button — pops on the light parchment where the old translucent-yellow wash washed out. */
.pcard-targetflow-btn {
    flex: 1 1 auto;
    padding: 0.3rem 0.5rem;
    border-radius: 0.4rem;
    border: 1px solid #8a5212;
    background: linear-gradient(180deg, #e8b13a, #cc8a1e);
    color: #2a1808;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
}
.pcard-targetflow-btn:hover { background: linear-gradient(180deg, #f1bf4d, #d9962a); }
.pcard-targetflow-status {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pcard-targetflow-status--targeting { color: #a85d10; }
.pcard-targetflow-status--answered { color: #7d8a49; }
.pcard-targetflow-cancel {
    flex: 0 0 auto;
    width: 1.3rem;
    height: 1.3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 0.35rem;
    border: none;
    background: transparent;
    color: #9a4a3a;
    font-size: 0.78rem;
    line-height: 1;
    cursor: pointer;
}
.pcard-targetflow-cancel:hover { background: rgba(176, 52, 36, 0.18); color: #8a2c1c; }

.hand-warning {
    margin: 0 0 0.5rem;
    padding: 0.4rem 0.6rem;
    border-radius: 0.4rem;
    background: rgba(207, 75, 59, 0.16);
    border: 1px solid rgba(207, 75, 59, 0.5);
    color: #f3cdc6;
    font-size: 0.8rem;
}

/* ── Card pile containers (Hand / In Play / Discard / Forgotten) ───────── */
.card-pile-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.card-pile-head h2 { margin: 0; white-space: nowrap; }
.card-pile--collapsible .card-pile-head { cursor: pointer; user-select: none; }
.card-pile--collapsible .card-pile-head:hover h2 { color: #120b02; }

/* A collapsed pile is a slim strip — trim the side padding so the rotated label sits tight. */
.card-pile.is-collapsed { padding-left: 0.35rem; padding-right: 0.35rem; }

.card-pile-chevron {
    color: #54401d;
    font-size: 0.72rem;
    width: 0.9rem;
    text-align: center;
}

.card-pile-empty { margin: 0.2rem 0 0; }

/* The head band carries the speed colour — a solid woody base (overridden by the speed tint) so the light
   cost + title read against it, spanning the full width above the strip + parchment body. */
.pcard-head {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.45rem 0.55rem;
    background: #46301a;
    border-radius: calc(0.5rem - 2px) calc(0.5rem - 2px) 0 0;
}

.pcard-cost {
    flex: 0 0 auto;
    min-width: 1.5rem;
    height: 1.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(255, 211, 107, 0.18);
    border: 1px solid rgba(255, 211, 107, 0.45);
    color: #f0e3c0;
    font-weight: 700;
    font-size: 0.85rem;
}

.pcard-name {
    color: #f6efe0;
    font-weight: 600;
    font-size: 0.92rem;
    line-height: 1.15;
}

/* The lower region: woody element strip on the left, parchment body on the right, filling to the bottom. */
.pcard-lower {
    display: flex;
    align-items: stretch;
    flex: 1 1 auto;
    min-height: 0;
}

/* The vertical element column — a woody brown gradient strip running the full height of the lower region,
   the eight markers spaced evenly top to bottom. */
.pcard-elementstrip {
    flex: 0 0 auto;
    width: 1.95rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    padding: 0.45rem 0;
    background: linear-gradient(180deg, #7a5630 0%, #5a3c20 55%, #3f290f 100%);
    border-right: 1px solid #2e1c0d;
    border-bottom-left-radius: calc(0.5rem - 2px);
}
.pcard-elementcell { display: flex; }
/* An element the card does not offer: greyed out and faded back into the strip. */
.pcard-elementcell.is-absent { filter: grayscale(1) opacity(0.28); }

/* The parchment body — black text, holding the meta tags, rules text and thresholds. */
.pcard-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.5rem;
    background: #f0e6cf;
    color: #190f04;
    border-bottom-right-radius: calc(0.5rem - 2px);
}

.pcard-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.pcard-tag {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 0.1rem 0.4rem;
    border-radius: 0.35rem;
    background: rgba(58, 38, 20, 0.12);
    color: #452f10;
}

/* Two-tone element markers (ElementIcon / ElementArt). */
.elem-icon { display: block; }

/* A marker + its count (player's banked elements on the spirit board). */
.elem-chip { display: inline-flex; align-items: center; gap: 0.18rem; }
.elem-chip-count { font-size: 0.82rem; font-weight: 700; color: #d7e0ea; }

/* A count + marker pair in an innate's element threshold. */
.elem-req { display: inline-flex; align-items: center; gap: 0.12rem; }
.elem-req-count { font-size: 0.74rem; font-weight: 700; }

/* Inline {Token}/{Element} symbols rendered into card / innate / fear / event text (CardText / CardSymbols).
   Sized to the surrounding text and nudged onto the baseline so an icon sits naturally mid-sentence. */
.card-sym {
    display: inline-block;
    width: 1.2em;
    height: 1.2em;
    vertical-align: -0.27em;
    margin: 0 0.04em;
}

/* Labelled pills for abstract symbols with no single piece (Fear, Invaders). */
.card-sym-pill {
    display: inline-block;
    padding: 0 0.34em;
    border-radius: 0.55em;
    font-size: 0.82em;
    font-weight: 700;
    line-height: 1.3;
    white-space: nowrap;
    vertical-align: 0.02em;
}
.card-sym-pill--fear { background: #5b3f86; color: #ece3f7; }
.card-sym-pill--invader { background: #5a3030; color: #f3dede; }

/* ── Targeting band (Speed · Range · Target) ───────────────────────────────
   The printed card's three equal columns under the title, shared by power cards and innate powers
   (TargetBandView). A self-contained "printed strip": a slightly darker parchment than the card body, deep
   brown text/arrow (currentColor), and two internal vertical rules — in the same deep brown as the card
   border — bracketing the Range column. Constraint icons carry their own terrain/piece colours; the speed
   label keeps its red/blue tint. The strip reads the same on a card's parchment and an innate's dark panel. */
.target-band {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    align-items: stretch;
    background: #e3d4b2;
    color: #3a2614;
    border-radius: 0.3rem;
    overflow: hidden;
}
.band-col {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    min-height: 1.7rem;
    min-width: 0;
    padding: 0 0.2rem;
}
/* Two internal vertical separators: the Range column's left + right edges (no outer borders). */
.band-col--range {
    border-left: 1px solid #3a2614;
    border-right: 1px solid #3a2614;
}
.band-col--speed {
    justify-content: flex-start;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.band-col--fast { color: #b6473c; }
.band-col--slow { color: #3f6aa3; }

/* The range number sitting on its archer's-arrow underline. */
.range-ind {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    line-height: 1;
}
.range-num { font-size: 0.95rem; font-weight: 700; }
.range-arrow { display: block; width: 2.7rem; height: 0.85rem; margin-top: 0.06rem; }

/* Constraint chips: an icon, or a short uppercase label (e.g. ANY), with a separator between several. */
.band-chip { display: inline-flex; align-items: center; }
.band-chip--captioned { flex-direction: column; line-height: 1; gap: 0.05rem; }
.band-chip-caption { font-size: 0.55rem; font-weight: 700; letter-spacing: 0.05em; opacity: 0.7; }
.band-chip--text { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; opacity: 0.75; }
.band-sep { font-size: 0.72rem; font-weight: 700; opacity: 0.55; margin: 0 0.04rem; }
.band-sym { display: block; width: 1.3rem; height: 1.3rem; }
.target-band .card-sym { width: 1.3rem; height: 1.3rem; vertical-align: middle; margin: 0; }

/* Card type tag (Minor / Major / Unique) on its own line under the band, right-aligned. */
.pcard-typeline { display: flex; justify-content: flex-end; }

.pcard-body {
    margin-top: 0.1rem;
    padding-top: 0.45rem;
    border-top: 1px solid rgba(58, 38, 20, 0.22);
    font-size: 0.8rem;
    line-height: 1.4;
    color: #0f0a02;
    white-space: pre-wrap;   /* preserve the authored line breaks in BodyMd */
}

/* Element threshold panel — a tinted band under the body, echoing the physical card's threshold box.
   Lights green when the owning spirit currently meets it (mirrors .innate-tier.is-met). */
.pcard-threshold {
    margin-top: 0.4rem;
    padding: 0.3rem 0.45rem;
    border-radius: 0.4rem;
    border-left: 3px solid rgba(58, 38, 20, 0.3);
    background: rgba(58, 38, 20, 0.08);
}
.pcard-threshold.is-met {
    border-left-color: #7d8a49;
    background: rgba(63, 138, 79, 0.16);
}
.pcard-threshold-label {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #524012;
}
.pcard-threshold.is-met .pcard-threshold-label { color: #7d8a49; }
.pcard-threshold-reqs {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin: 0.18rem 0;
    font-weight: 700;
    color: #3a2a18;
}
.pcard-threshold.is-met .pcard-threshold-reqs { color: #7d8a49; }
.pcard-threshold-text {
    font-size: 0.76rem;
    line-height: 1.35;
    color: #0f0a02;
    white-space: pre-wrap;
}

/* ── High-level game layout ────────────────────────────────────────────
   Vertical stack: header (thin) → communal (~60%) → timeline (thin) →
   spirit (~40%). Locked to the viewport height (100dvh) with overflow
   hidden so the game NEVER scrolls the page: the thin rows take their
   content height and the two big areas partition the leftover by their
   6/4 flex ratio. This makes it fit any screen height (1080p ↔ 1440p);
   the areas clip and their inner panels scale/fan/scroll as space tightens
   (board SVG letterboxes, hand fans — see .pile-cards.is-fanned). */
.game-layout {
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
}

/* Auto-compact by viewport HEIGHT: the whole UI is rem-based, so nudging the root font-size down on shorter
   screens tightens every padding/card/chip proportionally. A 1440p monitor (usable height ≳ 1200px) keeps the
   roomy 16px default; a 1080p monitor (usable ≈ 940px) drops to 15px so everything fits the reduced height
   without a page scroll; small laptops go smaller still. Keyed on height because the vertical budget is the
   constraint here — width is handled by the flex layout. */
@media (max-height: 1200px) { html { font-size: 15px; } }
@media (max-height: 900px)  { html { font-size: 14px; } }

/* In-game panels are dense — override the roomy default .panel padding/radius (kept generous for the lobby).
   Near-square corners (2px): the in-game look is boxy, like framed parchment sheets. */
.game-layout .panel { padding: 0.5rem; border-radius: 2px; }

/* In-game panels are warm sandy parchment with dark ink — a light, high-contrast "ancient island" surface (the
   lobby keeps its darker look). The frame is the statement: a stark dark-wood border (2px — defined, but no
   wider; layout space is precious) with an inset parchment-light hairline that reads as the frame's inner
   bevel, plus a drop shadow lifting the sheet off the hero art behind. The parchment is slightly translucent
   so the art glows through faintly; the barely-there grain multiplies over it. Dark ink cascades to any panel
   text that just inherits; SVG fills / explicit light colours are flipped where they occur. */
.game-layout .panel {
    background-color: rgba(233, 206, 156, 0.92);
    background-image: var(--paper-texture-subtle);
    background-blend-mode: multiply;
    background-size: 220px 220px;
    color: #190f04;
    border: 2px solid #573a1c;
    box-shadow: inset 0 0 0 1px rgba(246, 230, 194, 0.35), 0 2px 10px rgba(0, 0, 0, 0.45);
}
.game-layout .panel h2,
.game-layout .panel h3 { color: #120b02; }
.game-layout .panel .muted { color: #54401d; }

/* Spirit-owned surfaces — the spirit board + card piles (inside the themed .spirit-area wrapper) and the
   decision panel (skinned to the focused spirit) — drop the communal paper grain entirely. Instead: a smooth,
   vibrant accent gradient over light parchment — two soft accent glows breathing in from opposite corners with
   a gentle diagonal wash between — for an ethereal, spirit-magic look, framed by the accent rather than wood. */
.game-layout .spirit-area .panel,
.game-layout .panel.threshold-panel,
.game-layout .panel.question-panel {
    background-color: rgba(240, 227, 196, 0.90);
    background-image:
        radial-gradient(120% 140% at 10% -20%, rgba(var(--theme-accent-rgb, 120, 110, 78), 0.50), transparent 60%),
        radial-gradient(110% 130% at 95% 115%, rgba(var(--theme-accent-rgb, 120, 110, 78), 0.38), transparent 55%),
        linear-gradient(160deg, rgba(var(--theme-accent-rgb, 120, 110, 78), 0.14), rgba(var(--theme-accent-rgb, 120, 110, 78), 0.03) 50%, rgba(var(--theme-accent-rgb, 120, 110, 78), 0.16));
    background-blend-mode: normal;
    background-size: auto;
    border-color: rgba(var(--theme-accent-rgb, 120, 110, 78), 0.9);
}

/* ── Dark spirit skins ──────────────────────────────────────────────────────
   Opt-in per spirit (SpiritPalette Dark:true → GameView stamps "theme-dark" on the spirit-area + decision panel).
   Instead of the light-parchment accent wash, the spirit's own surfaces become a near-black sheet lit by three
   art-drawn glows — primary/secondary/tertiary accents (--theme-accent-rgb / -accent2-rgb / -accent3-rgb) — with
   light ink. This echoes a spirit whose art is a dark forest rather than washing a single tone to parchment.
   Only the spirit's surfaces flip: the communal parchment panels above and the physical power/innate cards stay
   light and pop against the dark board. */
.game-layout .spirit-area.theme-dark .panel,
.game-layout .panel.question-panel.theme-dark {
    background-color: var(--theme-surface-base, #0a120c);
    background-image:
        radial-gradient(120% 130% at 6% -15%, rgba(var(--theme-accent-rgb, 63, 195, 92), 0.55), transparent 55%),
        radial-gradient(115% 120% at 102% 6%, rgba(var(--theme-accent2-rgb, 42, 110, 168), 0.42), transparent 55%),
        radial-gradient(135% 145% at 55% 125%, rgba(var(--theme-accent3-rgb, 90, 59, 156), 0.45), transparent 60%),
        linear-gradient(160deg, rgba(var(--theme-accent-rgb, 63, 195, 92), 0.10), rgba(0, 0, 0, 0.30) 55%, rgba(var(--theme-accent3-rgb, 90, 59, 156), 0.16));
    background-blend-mode: normal;
    background-size: auto;
    border-color: rgba(var(--theme-accent-rgb, 63, 195, 92), 0.85);
    color: #e9f2e8;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 2px 12px rgba(0, 0, 0, 0.6);
}
/* Light-ink cascade for the dark surface's own text — headings, subheads, muted notes, section dividers, and the
   glassy special-rule chips (which were dark-ink-on-light). Physical cards inside (power/innate) keep their own
   dark-on-parchment styling and are untouched. */
.game-layout .spirit-area.theme-dark .panel h2,
.game-layout .spirit-area.theme-dark .panel h3,
.game-layout .panel.question-panel.theme-dark h2,
.game-layout .panel.question-panel.theme-dark h3 { color: #f4faf2; }
.game-layout .spirit-area.theme-dark .muted,
.game-layout .panel.question-panel.theme-dark .muted { color: #9db3a4; }
.game-layout .spirit-area.theme-dark .spirit-board-subhead { color: #86d69a; }
.game-layout .spirit-area.theme-dark .spirit-board-left { border-right-color: rgba(255, 255, 255, 0.14); }
.game-layout .spirit-area.theme-dark .spirit-board-row--bottom { border-top-color: rgba(255, 255, 255, 0.14); }
.game-layout .spirit-area.theme-dark .special-rule-chip {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.22);
    color: #e9f2e8;
}
.game-layout .spirit-area.theme-dark .special-rule-chip:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.42);
}
/* Resource pool: the big energy/plays values + their labels were dark ink on parchment — flip to light so they
   read on the dark surface (their faint white-tint cell backgrounds already suit dark). */
.game-layout .spirit-area.theme-dark .res-val { color: #f4faf2; }
.game-layout .spirit-area.theme-dark .res-lbl { color: #9db3a4; }

/* Decision cards on a dark spirit skin. The minimised question chips (.q-card) and their prompt/answer/cancel
   text are dark-ink-on-white for the parchment theme — unreadable on the near-black decision panel. Flip to
   light ink on a subtly lifted translucent surface, matching the panel's own light-ink cascade and the
   already-dark option modal. The is-pending blink and is-answered green accents are hue-driven and stay. */
.game-layout .panel.question-panel.theme-dark .q-card {
    background: rgba(255, 255, 255, 0.07);
    border-color: rgba(255, 255, 255, 0.16);
    color: #eef5ec;
}
.game-layout .panel.question-panel.theme-dark .q-card:hover { background: rgba(255, 255, 255, 0.13); }
.game-layout .panel.question-panel.theme-dark .q-card-answer { color: #9db3a4; }
.game-layout .panel.question-panel.theme-dark .q-card.is-answered .q-card-answer { color: #aec2a0; }
.game-layout .panel.question-panel.theme-dark .q-card-cancel { color: #9db3a4; }
/* Header furniture: the collapse chevron and the decision-count badge are dark ink; the head-hover darkens the
   title toward black. Flip all three light so the "Decisions" header reads on the dark panel. */
.game-layout .panel.question-panel.theme-dark .card-pile-chevron { color: #9db3a4; }
.game-layout .panel.question-panel.theme-dark .hand-count { color: #9db3a4; }
.game-layout .panel.question-panel.theme-dark.card-pile--collapsible .card-pile-head:hover h2 { color: #ffffff; }

/* A dark spirit may set --theme-surface-art (its official artwork). When present, the MAIN spirit board swaps its
   multi-hue gradient for the art, enveloped as `cover` (centred, no stretch or letterbox) — while the card piles
   and decision panel keep the gradient (this targets only .spirit-board).

   The art rides a ::before layer, not the panel background, so it can be CSS-blurred (--theme-surface-blur) WITHOUT
   blurring the text on top — no pre-blurred image needed, just drop in the raw art. Blur samples transparent pixels
   at the edges and would fade the border, so the layer bleeds past the panel (negative inset) and the panel clips
   it (.spirit-board already has overflow:hidden). ::after lays a scrim + vignette (transparent centre → dark
   edges/bottom) over the art for legibility and to ground the empty lower area. Content sits above both (z-index).
   Without art the var resolves to none and the board is a flat dark sheet. */
.game-layout .spirit-area.theme-dark .panel.spirit-board {
    position: relative;
    background-color: var(--theme-surface-base, #0a120c);
}
.game-layout .spirit-area.theme-dark .panel.spirit-board::before {
    content: "";
    position: absolute;
    inset: calc(-1 * var(--theme-surface-blur, 12px) - 8px);   /* bleed ≥ blur radius so edges stay crisp */
    background-image: var(--theme-surface-art, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    filter: blur(var(--theme-surface-blur, 12px)) saturate(1.05);
    z-index: 0;
    pointer-events: none;
}
.game-layout .spirit-area.theme-dark .panel.spirit-board::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        radial-gradient(125% 100% at 50% 18%, transparent 42%, rgba(3, 8, 5, 0.62) 100%),
        linear-gradient(160deg, rgba(4, 10, 6, var(--theme-art-dim, 0.42)), rgba(4, 10, 6, calc(var(--theme-art-dim, 0.42) + 0.16)));
    z-index: 0;
    pointer-events: none;
}
/* Lift the board's real content (resource column + right stack) above the art + scrim pseudo-layers. */
.game-layout .spirit-area.theme-dark .panel.spirit-board > * { position: relative; z-index: 1; }

.game-header {
    flex: 0 0 auto;
    margin: 0;
    max-width: none;   /* override .topbar's 60rem cap so the title centres across the full width */
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
}
.game-header .topbar-brand { grid-column: 2; }                       /* centred title */
.game-header .seat-switch { grid-column: 1; justify-self: start; margin: 0; }

.communal-area,
.spirit-area {
    min-height: 0;   /* partition the leftover height by flex (6/4) — never force overflow / a page scroll */
    display: flex;
    gap: 0.75rem;
    overflow: hidden;
}
/* While an exclusive board operation (tile targeting / gather) is mid-flight, the spirit area dims to signal
   that the one multi-part action on the board has the floor. Its own suppressed highlights (play prompts,
   growth picks) already go quiet via their VM flags; this is the at-a-glance cue. */
.spirit-area.is-operating { opacity: 0.78; filter: saturate(0.85); transition: opacity 0.12s ease; }

/* A spirit the viewer doesn't control: shown for reference (board state, hand, presence at the viewer's own
   scrub position) but with no actions. A subtle desaturation plus a corner badge make "you can look, not act"
   obvious; the interactive highlights are already absent because GameView feeds it empty toggle/target data. */
.spirit-area.is-readonly { filter: saturate(0.8); }
.spirit-area-inner { position: relative; }
.spirit-readonly-badge {
    position: absolute;
    top: 0.25rem;
    right: 0.5rem;
    z-index: 2;
    padding: 0.1rem 0.5rem;
    border-radius: 0.75rem;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    background: rgba(0, 0, 0, 0.45);
    color: #e8e8e8;
    pointer-events: none;
}

/* Give the board ~60% of the vertical space and the player area ~40%. */
.communal-area { flex: 6 1 0; }
.spirit-area   { flex: 4 1 0; }

.timeline-region {
    flex: 0 0 auto;
    /* No overflow clipping here: the track hides its own horizontal overflow, and the phase flag must be able
       to poke up out of this region to overlap the board view above. */
}

/* Tab row sitting on top of the spirit panel. Fixed-height (flex 0 0 auto); since .spirit-area has a
   min-height it doesn't give up space, so this row's height comes out of the communal area above — opening
   it pushes the timeline + board up rather than squeezing the spirit panel. */
.spirit-tabs {
    flex: 0 0 auto;
    display: flex;
    gap: 0.25rem;
    align-items: flex-end;
}
/* Each tab wears its spirit's accent (--tab-accent, set inline by SpiritTabs) with the same ethereal
   gradient-over-parchment treatment as the spirit's panels — so the row reads as a set of differently
   coloured tabs and no swatch dot is needed. Unselected tabs are darkened via filter; the active one is
   full-brightness and merges into the panel below. */
.spirit-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.7rem;
    border: 1px solid color-mix(in srgb, var(--tab-accent, #786e4e) 85%, transparent);
    border-bottom: none;
    border-radius: 2px 2px 0 0;
    background-color: rgba(240, 227, 196, 0.9);
    background-image:
        radial-gradient(140% 170% at 12% -40%, color-mix(in srgb, var(--tab-accent, #786e4e) 75%, transparent), transparent 75%),
        linear-gradient(160deg, color-mix(in srgb, var(--tab-accent, #786e4e) 35%, transparent), color-mix(in srgb, var(--tab-accent, #786e4e) 60%, transparent));
    color: #120b02;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    filter: brightness(0.68) saturate(1.1);
    transition: filter 0.12s ease;
}
.spirit-tab:hover { filter: brightness(0.85) saturate(1.05); }
/* A spirit I control with an open decision I'm not currently looking at — pulses a yellow glow ring to pull me
   over to it. Note: it CANNOT use the shared background-color `attention-blink` here — the tab paints an opaque
   spirit-accent background-image gradient (--tab-accent, above) on top of its background-color, which would
   occlude that pulse entirely. So the cue rides on border-color + box-shadow (which sit above the gradient),
   the same channels the power-card blink uses. The label stays dark ink — yellow text wouldn't read on the
   light tab. */
.spirit-tab.needs-attention {
    animation: spirit-tab-attention-blink 1.3s ease-in-out infinite;
}
@keyframes spirit-tab-attention-blink {
    0%, 100% { border-color: color-mix(in srgb, var(--tab-accent, #786e4e) 85%, transparent); box-shadow: none; }
    50%      { border-color: var(--flash-color); box-shadow: 0 0 12px 3px rgba(var(--flash-rgb), 0.9); }
}
/* The active tab reads as the front sheet: full-brightness, and it visually merges into the panel below it. */
.spirit-tab.is-active {
    filter: none;
    margin-bottom: -1px;
    padding-bottom: calc(0.3rem + 1px);
}
/* Read-only (not mine) tabs are slightly dimmer still, matching the read-only panel treatment. */
.spirit-tab.is-readonly { opacity: 0.9; }
.spirit-tab-label { white-space: nowrap; }
.spirit-tab-eye { font-size: 0.72rem; opacity: 0.75; }

/* A dark-skinned spirit's tab wears the SAME near-black multi-hue gradient as its panels (built from the same
   --theme-accent-rgb / -accent2-rgb / -accent3-rgb over --theme-surface-base, passed inline via the spirit's
   ThemeVars) with a light label — so the tab reads as the front edge of its dark panel rather than a light chip.
   The unselected dim is gentler than the light-tab brightness(0.68), which on near-black would crush the gradient
   to flat black; :not(.is-active) keeps the focused tab full-brightness (its .is-active filter:none still wins). */
.spirit-tab.theme-dark {
    background-color: var(--theme-surface-base, #0a120c);
    background-image:
        radial-gradient(140% 180% at 10% -40%, rgba(var(--theme-accent-rgb, 63, 195, 92), 0.55), transparent 60%),
        radial-gradient(130% 160% at 100% 10%, rgba(var(--theme-accent2-rgb, 42, 110, 168), 0.42), transparent 60%),
        radial-gradient(150% 190% at 60% 150%, rgba(var(--theme-accent3-rgb, 90, 59, 156), 0.45), transparent 65%),
        linear-gradient(160deg, rgba(var(--theme-accent-rgb, 63, 195, 92), 0.10), rgba(0, 0, 0, 0.30) 55%, rgba(var(--theme-accent3-rgb, 90, 59, 156), 0.16));
    border-color: rgba(var(--theme-accent-rgb, 63, 195, 92), 0.75);
    color: #ecf4ec;
}
.spirit-tab.theme-dark:not(.is-active) { filter: brightness(0.78) saturate(1.02); }
.spirit-tab.theme-dark:not(.is-active):hover { filter: brightness(0.94) saturate(1.02); }

/* The board floats directly on the hero art — no parchment sheet behind it (the island svg brings its own
   frame + dark tint, and the art showing through beats parchment letterbox bars around the island). */
.game-layout .panel.board-panel {
    background: none;
    border: none;
    box-shadow: none;
    padding: 0;
}

/* Communal: board on the left, the shared island column on the right. */
.board-panel {
    flex: 1 1 62%;
    min-width: 0;
    overflow: hidden;   /* board fits to height (below), so never scroll */
    display: flex;
    flex-direction: column;
    position: relative;   /* anchor for the floating "Fit" camera-reset button */
}

.board-panel .game-boards {
    flex: 1 1 auto;
    min-height: 0;      /* let the boards column shrink below content height */
    margin: 0;
    max-width: none;    /* fill however much width the row has (e.g. when the decisions panel collapses) —
                           the 96rem cap is a lobby-era limit that stopped the board stretching on wide screens */
    gap: 1rem;
    justify-content: center;
}

.board-panel .board-wrap {
    width: 100%;
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;   /* centre the (narrower) board once it's height-constrained */
}

.board-panel .board-svg {
    /* Fill the whole panel; preserveAspectRatio="meet" keeps the island centred and letterboxed inside,
       so the extra space becomes pan/zoom room and a bigger interaction surface. */
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
}

.island-panel {
    flex: 0 0 650px;
    width: 650px;
    max-width: 100%;
    min-width: 0;
    overflow: auto;
}

/* Decisions panel: lives in the communal row, right of the board. It reuses the card-pile collapse classes
   so it folds to a slim full-height strip (with a rotated label) when there are no active questions, and pops
   back to an 18rem column when one arrives. */
.communal-area > .question-panel { margin: 0; }
.communal-area > .question-panel:not(.is-collapsed) { flex: 0 0 18rem; overflow-y: auto; overflow-x: hidden; }
.communal-area > .question-panel.is-collapsed { flex: 0 0 auto; }

/* ── Group threshold test panel ─────────────────────────────────────────────────
   The aggregate view of a live "Aided by <Element>" test: element + target header, one contribution row per
   player, running total, per-player lock-in. A table-wide moment, so it claims its own column beside the
   decision panel; it renders nothing (no column) when no test is live. */
.communal-area > .threshold-panel { margin: 0; flex: 0 0 21rem; overflow-y: auto; overflow-x: hidden; }

.threshold-test { display: flex; flex-direction: column; gap: 0.5rem; }
.threshold-test + .threshold-test { margin-top: 0.9rem; border-top: 1px solid rgba(58, 38, 20, 0.3); padding-top: 0.7rem; }

.threshold-head { display: flex; align-items: center; gap: 0.45rem; }
.threshold-head h2 { flex: 1 1 auto; margin: 0; font-size: 1rem; }
.threshold-total {
    font-size: 1.05rem;
    font-weight: 700;
    padding: 0.1rem 0.5rem;
    border-radius: 0.4rem;
    border: 1px solid rgba(58, 38, 20, 0.35);
}
/* Under target: the total carries the universal yellow to-do blink; met-but-unlocked reads calm green. */
.threshold-test.is-short .threshold-total { color: #7a1f12; animation: attention-blink 1.3s ease-in-out infinite; }
.threshold-test.is-met .threshold-total { background: rgba(174, 194, 160, 0.5); color: #24380f; }
.threshold-test.is-resolved .threshold-total { background: #aec2a0; color: #161009; }

.threshold-status { margin: 0; font-size: 0.78rem; color: #54401d; }

.threshold-row {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    background: rgba(255, 255, 255, 0.25);
    border: 1px solid rgba(58, 38, 20, 0.3);
    border-left-width: 3px;
    border-radius: 2px;
    padding: 0.4rem 0.55rem;
}
.threshold-row.is-mine { border-left-color: var(--theme-accent, #786e4e); background: rgba(255, 255, 255, 0.4); }
.threshold-row-head { display: flex; align-items: baseline; gap: 0.5rem; }
.threshold-player { flex: 1 1 auto; font-weight: 600; font-size: 0.85rem; }
.threshold-row-sum { font-size: 0.85rem; font-weight: 700; color: #24380f; }

.threshold-contribs { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.threshold-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.78rem;
    padding: 0.1rem 0.4rem;
    border-radius: 0.8rem;
    border: 1px solid rgba(58, 38, 20, 0.3);
    background: rgba(255, 255, 255, 0.35);
}
.threshold-step {
    width: 1.15rem;
    height: 1.15rem;
    line-height: 1;
    border-radius: 50%;
    border: 1px solid rgba(58, 38, 20, 0.45);
    background: rgba(255, 255, 255, 0.5);
    cursor: pointer;
    font-size: 0.8rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}
.threshold-step:hover:not(:disabled) { background: #fff; }
.threshold-step:disabled { opacity: 0.35; cursor: default; }

/* One pledgeable card: its name plus the Keep / Discard (+2) / Forget (+4) segments. */
.threshold-card {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.78rem;
    padding: 0.1rem 0.3rem 0.1rem 0.45rem;
    border-radius: 0.4rem;
    border: 1px solid rgba(58, 38, 20, 0.3);
    background: rgba(255, 255, 255, 0.3);
    flex-wrap: wrap;
}
.threshold-card-name { font-style: italic; }
.threshold-seg {
    font-size: 0.72rem;
    padding: 0.08rem 0.35rem;
    border-radius: 0.35rem;
    border: 1px solid rgba(58, 38, 20, 0.35);
    background: rgba(255, 255, 255, 0.3);
    cursor: pointer;
}
.threshold-seg:hover { background: rgba(255, 255, 255, 0.6); }
.threshold-seg.is-on { background: #aec2a0; border-color: #7d9469; color: #161009; font-weight: 600; }
.threshold-seg.is-static { cursor: default; }

.threshold-lock {
    align-self: flex-end;
    font-size: 0.78rem;
    padding: 0.18rem 0.6rem;
    border-radius: 0.4rem;
    border: 1px solid rgba(58, 38, 20, 0.45);
    background: rgba(255, 255, 255, 0.4);
    cursor: pointer;
}
.threshold-lock:hover:not(.is-static) { background: #fff; }
.threshold-lock.is-locked { background: #aec2a0; border-color: #7d9469; color: #161009; font-weight: 600; }
.threshold-lock.is-static { cursor: default; color: #54401d; }
/* The met-but-unlocked state is exactly a to-do for whoever hasn't pressed it. */
.threshold-test.is-met .threshold-lock:not(.is-locked) { animation: attention-blink 1.3s ease-in-out infinite; border-color: var(--flash-color); }

.spirit-area-inner {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    gap: 0.75rem;
    overflow: hidden;
}

/* The spirit board grows to fill the left of the player area; the card band (below) sizes to its piles.
   Grid layout: a tall resource column on the left (spanning two rows), growth (short) above the presence
   tracks (tall) on the right, and innates running full-width along the bottom under the title. */
/* Nested flow panels rather than a grid: the spirit's name + resource pool run down a fixed LEFT column, and
   everything else fills the RIGHT as a 2×2 (Special|Growth over Innate|Tracks). Each section sizes to its
   content and the tall bottom row flexes to share the leftover height, so the board shrinks to fit its area
   instead of growing a scrollbar. */
.spirit-area-inner .spirit-board {
    flex: 1 1 0;
    /* Hard floor: a huge hand must never squeeze the board below a usable width (resource column 13rem +
       a workable track/innate area). Past this point the Hand fans/overlaps instead — see .pile-cards.is-fanned. */
    min-width: 30rem;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: row;
    gap: 0.6rem;
}

/* Left: title + resources, tucked together so the title costs no vertical space on the right. */
.spirit-board-left {
    flex: 0 0 13rem;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    border-right: 1px solid rgba(58, 38, 20, 0.25);
    padding-right: 0.6rem;
}
.spirit-board-resources { flex: 0 0 auto; min-width: 0; }

/* Right: everything else, stacked as two rows. The top row sizes to content; the bottom row (innates +
   tracks) takes the remaining height. */
.spirit-board-right {
    flex: 1 1 0;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.spirit-board-row {
    display: flex;
    flex-direction: row;
    gap: 0.6rem;
    min-width: 0;
}
.spirit-board-row--top    { flex: 0 0 auto; }
.spirit-board-row--bottom { flex: 1 1 0; min-height: 0; border-top: 1px solid rgba(58, 38, 20, 0.25); padding-top: 0.4rem; }

/* In each row the left cell (special / innate) takes only the width it needs and the right cell (growth /
   tracks) absorbs the rest — so the innate cards get the room to sit side by side and the presence track
   fills whatever's left rather than being squashed into a corner. */
.spirit-board-special,
.spirit-board-innate { flex: 0 1 auto; min-width: 0; }
.spirit-board-growth,
.spirit-board-tracks { flex: 1 1 0; min-width: 0; }

/* The bottom row's cells are the tall ones: they soak up the leftover height and clip rather than scroll
   (the track SVG scales to fit; see .track-svg max-height). */
.spirit-board-innate,
.spirit-board-tracks { min-height: 0; overflow: hidden; }

.spirit-board-subhead {
    margin: 0 0 0.4rem;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: #452f10;
}

.growth-options { display: flex; flex-direction: row; flex-wrap: wrap; gap: 0.6rem; }
/* Innates sit side by side (wrapping only if they truly run out of width); the list clips rather than scrolls. */
.innate-list { display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: 0.6rem; min-height: 0; overflow: hidden; }
/* Special-rule chips flow and wrap. */
.special-list { display: flex; flex-direction: row; flex-wrap: wrap; gap: 0.4rem; }

/* A special rule shown as a compact name-only chip; its full text appears as a hover tooltip (title attr),
   so it no longer eats vertical space. */
.special-rule-chip {
    flex: 0 0 auto;
    padding: 0.3rem 0.6rem;
    border-radius: 0.5rem;
    background: rgba(58, 38, 20, 0.06);
    border: 1px solid rgba(58, 38, 20, 0.35);
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: help;
}
.special-rule-chip:hover {
    background: rgba(58, 38, 20, 0.12);
    border-color: rgba(58, 38, 20, 0.6);
}

/* An Innate Power card: a name + current-speed header, then one row per element-gated tier. Fixed width so
   several sit comfortably side by side. Shares the power card's light parchment body + black text and deep
   brown border, but has no woody element strip — the tiers carry the elements instead. */
.innate-power {
    flex: 0 0 auto;
    width: 14rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.45rem 0.55rem;
    border-radius: 0.55rem;
    background: #f0e6cf;
    color: #190f04;
    border: 2px solid #3a2614;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.innate-power-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4rem;
}
.innate-power-name { font-weight: 700; font-size: 0.86rem; }

/* The innate's targeting band sits directly under the name; nudge it in line with the tiers below. */
.innate-power .target-band { margin: 0.1rem 0; }

.innate-tiers { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
/* A tier on the parchment body, echoing the power card's threshold box (.pcard-threshold). */
.innate-tier {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.25rem 0.4rem;
    border-radius: 0.4rem;
    border-left: 3px solid rgba(58, 38, 20, 0.3);
    background: rgba(58, 38, 20, 0.08);
}
/* A tier whose element threshold is currently met lights green — the same green that reads on the card's parchment. */
.innate-tier.is-met {
    border-left-color: #7d8a49;
    background: rgba(63, 138, 79, 0.16);
}
.innate-tier-threshold { display: flex; align-items: center; flex-wrap: wrap; gap: 0.3rem; font-size: 0.7rem; font-weight: 700; color: #524012; }
.innate-tier.is-met .innate-tier-threshold { color: #7d8a49; }
/* The "no threshold" em-dash: muted brown on parchment, not the dark-theme sage grey. */
.innate-tier-threshold .muted { color: #8a7350; }
.innate-tier-text { font-size: 0.74rem; line-height: 1.2; color: #0f0a02; }

/* While the power is resolvable this turn it carries the universal yellow attention blink. Pulse the border +
   glow (not the body) — same as the power card — so the parchment and green tiers stay readable underneath. */
.innate-power.is-resolvable { animation: pcard-attention-blink 1.3s ease-in-out infinite; }

/* The innate's target flow: a "Use" button, then the chosen land + an ✕ to withdraw. Sits on the parchment
   body, so it borrows the power card's amber-on-parchment buttons and deep-brown statuses (.pcard-targetflow). */
.innate-targetflow {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.1rem;
    padding-top: 0.4rem;
    border-top: 1px solid rgba(58, 38, 20, 0.25);
}
.innate-targetflow-btn {
    flex: 0 0 auto;
    padding: 0.2rem 0.6rem;
    border-radius: 0.5rem;
    border: 1px solid #8a5212;
    background: linear-gradient(180deg, #e8b13a, #cc8a1e);
    color: #2a1808;
    font-weight: 700;
    font-size: 0.76rem;
    cursor: pointer;
}
.innate-targetflow-btn:hover { background: linear-gradient(180deg, #f1bf4d, #d9962a); }
.innate-targetflow-pass {
    flex: 0 0 auto;
    padding: 0.2rem 0.55rem;
    border-radius: 0.5rem;
    border: 1px solid rgba(58, 38, 20, 0.35);
    background: rgba(58, 38, 20, 0.06);
    color: #452f10;
    font-size: 0.74rem;
    cursor: pointer;
}
.innate-targetflow-pass:hover { background: rgba(58, 38, 20, 0.14); }
.innate-targetflow-status { font-size: 0.76rem; }
.innate-targetflow-status--targeting { color: #a85d10; font-weight: 600; }
.innate-targetflow-status--answered { color: #7d8a49; font-weight: 600; }
.innate-targetflow-status--passed { color: #524012; font-style: italic; }
.innate-targetflow-cancel {
    flex: 0 0 auto;
    border: none;
    background: none;
    color: #9a4a3a;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0 0.15rem;
}
.innate-targetflow-cancel:hover { color: #8a2c1c; }

/* A growth option card. Default is a neutral tile; when an open growth question exists it highlights yellow
   (declarable, clickable); when picked it turns green. Mirrors the power-card declarable/declared states. */
/* Auto-sizes to its icons (no fixed width, no title) — the effects are the physical-game icon shorthand. */
.growth-option {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    padding: 0.4rem 0.5rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.18);
    border: 1px solid rgba(58, 38, 20, 0.35);
}

.growth-effect-icons { display: flex; flex-direction: row; align-items: center; gap: 0.3rem; }
.growth-effect-slot { display: inline-flex; border-radius: 0.35rem; padding: 0.1rem; }
.geff-icon { display: block; width: 2.5rem; height: 2.5rem; }
/* Numbers baked into the glyphs: the "+N" on card/energy and the presence range number are bold. */
.geff-num { font-size: 12px; font-weight: 700; }
.geff-num--beside { font-size: 11px; }

/* Declarable: an open growth question — clickable, highlighted yellow with the universal to-do blink. */
.growth-option--declarable {
    cursor: pointer;
    border-color: var(--flash-color);
    transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
    animation: attention-blink 1.3s ease-in-out infinite;
}
.growth-option--declarable:hover { transform: translateY(-3px); }

/* Declared (picked): green. Overrides the yellow declarable state. */
.growth-option--declared {
    border-color: #aec2a0;
    background: rgba(174, 194, 160, 0.16);
    box-shadow: 0 6px 18px rgba(174, 194, 160, 0.24);
}

/* "You have a choice to make here" blink, shared by pending growth effects and playable hand cards. */
@keyframes attention-blink {
    0%, 100% { background-color: rgba(var(--flash-rgb), 0); }
    50%      { background-color: rgba(var(--flash-rgb), 0.22); }
}

/* An effect icon of a picked option whose resolution proposal is still awaiting answers — blinks + rings so
   the player sees which specific icon still needs a decision (answered on the board/track or Decisions panel). */
.growth-effect-slot.growth-effect--pending {
    box-shadow: 0 0 0 2px var(--flash-color);
    animation: attention-blink 1.3s ease-in-out infinite;
}

/* ── Presence track ──────────────────────────────────────────────────────────
   The track SVG: spaces are rings (dashed while still covered by presence, solid once revealed), directed
   edges flow left-to-right, labels sit under each space. The presence disc covers the bonus glyph; hovering
   a covered space fades the disc to peek at what placing it would unlock. */
/* Scales to its container's width; max-height lets it letterbox (preserveAspectRatio meet) down to fit a
   height-constrained tracks column instead of overflowing it. */
.track-svg { display: block; width: 100%; height: auto; max-height: 100%; }

.track-edge { stroke: rgba(90, 70, 40, 0.5); stroke-width: 1.6; fill: none; }

.track-ring { fill: rgba(42, 29, 14, 0.06); stroke: rgba(70, 52, 26, 0.55); stroke-width: 1.3; stroke-dasharray: 3 2.4; }
.track-node--revealed .track-ring { stroke-dasharray: none; }

.track-glyph { font-size: 14px; font-weight: 700; fill: #190f04; }
.track-coin { fill: rgba(242, 193, 78, 0.2); stroke: rgba(242, 193, 78, 0.6); stroke-width: 1.2; }
.track-cardplay { fill: rgba(42, 29, 14, 0.05); stroke: rgba(70, 52, 26, 0.55); stroke-width: 1.2; }
.track-any { fill: none; stroke: rgba(70, 52, 26, 0.6); stroke-width: 1.2; }
.track-elem-name { font-size: 7.5px; font-weight: 700; }
.track-reclaim { font-size: 17px; fill: #190f04; }
.track-label { font-size: 9px; fill: #54401d; }

/* Fill + ring come inline from the shared PresenceDisc (so track presence matches the board tokens);
   this only carries the hover/committed opacity behaviour. */
.track-presence { transition: opacity 0.12s ease; }
.track-node--covered:hover .track-presence { opacity: 0.22; }

/* Placement flow. State shows on the ring so the hover-peek of the disc still works: placeable spaces ring
   yellow (clickable), the armed space (its tile is being picked) rings green, and spaces already claimed by
   an answered placement ring green with their presence half-faded (it's departing). */
.track-node--placeable { cursor: pointer; }
/* Placeable spaces blink — the same universal yellow to-do cue as cards and decision rows. */
@keyframes track-placeable-blink {
    0%, 100% { stroke: rgba(var(--flash-rgb), 0.55); }
    50%      { stroke: var(--flash-color); stroke-width: 3; }
}
.track-node--placeable .track-ring {
    stroke: var(--flash-color);
    stroke-width: 2.2;
    stroke-dasharray: none;
    animation: track-placeable-blink 1.3s ease-in-out infinite;
}
.track-node--armed .track-ring { stroke: #aec2a0; stroke-width: 2.4; stroke-dasharray: none; }
.track-node--committed .track-ring { stroke: #aec2a0; stroke-width: 1.6; stroke-dasharray: none; }
.track-node--committed .track-presence { opacity: 0.45; }

/* A revealed "+Any" slot owed an income element choice — clickable, and blinks with the same universal
   to-do cue as placeable spaces so the player knows a decision is owed on the track itself. */
.track-node--element-choice { cursor: pointer; }
.track-node--element-choice .track-ring {
    stroke: var(--flash-color);
    stroke-width: 2.2;
    stroke-dasharray: none;
    animation: track-placeable-blink 1.3s ease-in-out infinite;
}

/* ── Presence track on a DARK spirit skin ───────────────────────────────────
   The track SVG is authored in dark ink (brown strokes, near-black labels/glyphs) for the light parchment board;
   on a dark art-backed board all of it vanishes. Re-ink the whole track light: bright labels + glyphs carry a
   dark halo (paint-order: stroke, so the outline sits *behind* the fill) so they pop over the variable blurred
   art, and the dashed rings, edges and arrowheads become light strokes. Element icons and the presence disc
   already bring their own vivid colour, so they're left alone. Placeable/armed/committed ring states already use
   the light flash colour / pale green and need no change. */
.spirit-area.theme-dark { --track-arrow-fill: rgba(228, 240, 230, 0.8); }
.spirit-area.theme-dark .track-edge { stroke: rgba(228, 240, 230, 0.5); }
.spirit-area.theme-dark .track-ring { fill: rgba(255, 255, 255, 0.05); stroke: rgba(228, 240, 230, 0.62); }
.spirit-area.theme-dark .track-cardplay { fill: rgba(255, 255, 255, 0.06); stroke: rgba(228, 240, 230, 0.6); }
.spirit-area.theme-dark .track-coin { stroke: rgba(242, 205, 120, 0.85); }
.spirit-area.theme-dark .track-glyph,
.spirit-area.theme-dark .track-reclaim,
.spirit-area.theme-dark .track-elem-name {
    fill: #f6fbf5;
    paint-order: stroke;
    stroke: rgba(2, 6, 3, 0.7);
    stroke-width: 2.2px;
    stroke-linejoin: round;
}
.spirit-area.theme-dark .track-label {
    fill: #ecf4ec;
    paint-order: stroke;
    stroke: rgba(2, 6, 3, 0.72);
    stroke-width: 2.4px;
    stroke-linejoin: round;
}

/* Generic dashed placeholder tile, used until growth / innate / special-rule renders exist. */
.placeholder-card {
    flex: 0 0 auto;
    width: 6rem;
    min-height: 4.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0.4rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.03);
    border: 1px dashed rgba(211, 194, 158, 0.18);
    font-size: 0.78rem;
}

/* One horizontal band: In Play · Hand · Discard · Forgotten, side by side, all the same height. Open piles
   size to their card row; collapsed piles shrink to a thin vertical strip. The band scrolls horizontally only
   if the open piles together outrun the width (rare — declaring a card just moves it Hand → In Play, so the
   total card count, and thus the total width, is unchanged). */
.spirit-area-inner .card-piles {
    flex: 0 1 auto;   /* size to the piles; the spirit board takes the rest of the row */
    min-width: 0;
    display: flex;
    flex-direction: row;
    align-items: stretch;
    gap: 0.6rem;
    overflow-x: auto;
    overflow-y: hidden;
}

.card-pile {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

/* Open pile: width follows its cards (don't grow/shrink); the card row fills the remaining height. */
.card-pile:not(.is-collapsed) { flex: 0 0 auto; }

.card-pile .pile-cards {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Fanned pile (the Hand): cards advance by a JS-measured step instead of the fixed gap, so they sit shoulder
   to shoulder with NO overlap while they fit and only start overlapping once the band runs out of room. The
   step is set on --card-step by cardfan.js (see CardPileView): it equals the natural gap when everything fits
   and goes negative to overlap as the hand grows, clamped so a readable spine of each card always shows. Only
   at the extreme (spine clamp reached) does the band itself scroll. Later cards paint over earlier ones, so the
   visible spine is each card's left edge (cost + name); hovering lifts a card to the front to read it in full. */
.card-pile .pile-cards.is-fanned {
    gap: 0;
    overflow-x: auto;
}
.card-pile .pile-cards.is-fanned .pcard {
    position: relative;   /* ensure z-index on hover lifts the card above its neighbours */
}
.card-pile .pile-cards.is-fanned .pcard + .pcard {
    margin-left: var(--card-step, 0px);
}
.card-pile .pile-cards.is-fanned .pcard:hover {
    z-index: 5;
}

/* Collapsed pile: a slim full-height strip with a vertical (rotated) label. Clicking it expands the pile. */
.card-pile.is-collapsed { flex: 0 0 auto; }
.card-pile.is-collapsed .card-pile-head {
    flex: 1 1 auto;
    writing-mode: vertical-rl;
    align-items: center;
    justify-content: flex-start;
    gap: 0.5rem;
    padding: 0.2rem 0;
}

/* ── Terminal-outcome banner (Island Saved / Island Overrun) ──
   A big centred label floating over the whole view when the local replica reaches a terminal state. Bound to
   _game.TerminalState, so scrubbing back un-renders it. pointer-events:none keeps the board interactive. */
.outcome-banner {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.outcome-banner span {
    font-size: clamp(2.5rem, 9vw, 7rem);
    font-weight: 800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-align: center;
    padding: 0 1rem;
    animation: outcome-pop 0.45s cubic-bezier(0.2, 1.3, 0.4, 1) both;
}
.outcome-banner--victory span {
    color: #6fe39a;
    text-shadow: 0 0 18px rgba(111, 227, 154, 0.55), 0 2px 6px rgba(0, 0, 0, 0.6);
}
.outcome-banner--defeat span {
    color: #ff5d52;
    text-shadow: 0 0 18px rgba(255, 93, 82, 0.5), 0 2px 6px rgba(0, 0, 0, 0.6);
}
@keyframes outcome-pop {
    0%   { opacity: 0; transform: scale(0.6); }
    100% { opacity: 1; transform: scale(1); }
}

/* ── Floating land-inspection panel (replaces the dedicated land panel) ── */
.land-hover {
    /* Pinned to the board panel's top-right corner (below the Fit button's row) rather than following the
       cursor, so inspecting a land never blocks the map you're navigating. Highly transparent to stay
       unobtrusive over the board. */
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 3;
    pointer-events: none;   /* never steals the hover that spawned it */
    max-width: 14rem;
    background: rgba(8, 18, 14, 0.55);
    border: 1px solid rgba(211, 194, 158, 0.22);
    border-radius: 0.55rem;
    padding: 0.5rem 0.65rem;
    backdrop-filter: blur(2px);
    font-size: 0.82rem;
}

.land-hover p { margin: 0 0 0.3rem; }
.land-hover .token-list { margin: 0.4rem 0; }

/* ── Island panel internals ────────────────────────────────────────────── */
.island-section { margin-bottom: 1rem; }

.island-section-title {
    margin: 0 0 0.5rem;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: #452f10;
}

.island-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.7rem;
}

/* Pool row: the Fear pool + its terror track grouped together, then the Blight pool (wraps if narrow). */
.island-pools { display: flex; flex-wrap: wrap; gap: 0.7rem 1rem; align-items: flex-start; }
.fear-pool-group { display: flex; gap: 0.6rem; align-items: flex-start; }

/* Fear row: three equal-height bordered .pool sub-sections — Fear Pool, Fear Deck, Earned. They stretch to
   the tallest (the deck cards), share a small EARNED-style title, and vertically centre their content so the
   bar / cards / backs line up across the boxes. */
.fear-row { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: stretch; }
.fear-row .pool { display: flex; flex-direction: column; }
.fear-row .island-section-title {
    font-size: 0.6rem;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #54401d;
}
.fear-row .pool-body { flex: 1 1 auto; align-items: center; justify-content: center; }
.fear-row .fear-backs { margin-bottom: 0; }
.fear-earned-empty { color: #54401d; font-size: 1rem; }

/* Powers & Events decks on the left, the Blight pool pushed out to the far right of the same row, with open
   space between them (space-between). They align to the bottom so the deck tiles and the blight gauge share a
   baseline; wraps if the panel gets too narrow. */
.island-decks-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.island-decks-group { flex: 0 1 auto; }
.island-blight-block { flex: 0 0 auto; }
.island-blight-block .pool { min-width: 7rem; }

/* The bottom Face-up Cards row: a strip of small thumbnails (one per face-up fear/event card this round), plus
   the expand overlay that rises up over the panel when a thumbnail is clicked. Positioned so the overlay can be
   absolutely anchored to it. */
.island-facecards-row { position: relative; min-height: 4rem; }
.island-active-card-empty {
    display: flex; align-items: center; justify-content: center;
    min-height: 3rem;
    border-radius: 2px;
    border: 1px dashed rgba(58, 38, 20, 0.4);
    background: rgba(58, 38, 20, 0.03);
    color: #54401d;
    font-size: 0.8rem;
}

/* Face-up cards are split into labelled groups (Unearned Fear / Earned Fear / Events) so an upcoming deck
   reveal isn't mistaken for an earned card. Each group is a small-caps label above its own thumbnail row. */
.island-thumb-group { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.5rem; }
.island-thumb-group:last-child { margin-bottom: 0; }
.island-thumb-group-label {
    font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: #6b5227;
}

/* Thumbnail strip: small tappable cards. Each shows a title + subtitle; the one owing this player an answer
   blinks yellow and flags a "!" ; the currently-open one is outlined. */
.island-thumb-row { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.island-thumb {
    position: relative;
    flex: 0 0 auto;
    min-width: 6.5rem; max-width: 11rem;
    display: flex; flex-direction: column; gap: 0.1rem;
    padding: 0.35rem 0.5rem;
    text-align: left;
    border-radius: 3px;
    background: rgba(58, 38, 20, 0.08);
    border: 1px solid rgba(58, 38, 20, 0.4);
    color: inherit; font: inherit;
    cursor: pointer;
}
.island-thumb:hover { background: rgba(58, 38, 20, 0.14); }
.island-thumb.is-open { border-color: #8a5212; background: rgba(138, 82, 18, 0.16); }
.island-thumb.is-attention {
    border-color: var(--flash-color);
    animation: attention-blink 1.3s ease-in-out infinite;
}
.island-thumb-title { font-weight: 600; color: #120b02; font-size: 0.78rem; line-height: 1.15; }
.island-thumb-sub { font-size: 0.6rem; color: #54401d; }
.island-thumb-flag {
    position: absolute; top: -0.4rem; right: -0.4rem;
    width: 1rem; height: 1rem;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--flash-color); color: #120b02;
    font-size: 0.66rem; font-weight: 800;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Expand overlay: rises up from just above the thumbnail strip to cover part of the panel; a near-opaque scrim
   hides the busy Fear/deck section behind so the card stays readable, and the card is centred on top. Clicking
   the scrim closes it. */
.island-card-overlay {
    position: absolute;
    left: -0.6rem; right: -0.6rem; bottom: 100%;
    z-index: 30;
    display: flex; justify-content: center; align-items: flex-end;
    padding: 0 0.6rem 0.5rem;
    max-height: 70vh;
    background: linear-gradient(to top, rgba(28, 18, 8, 0.94), rgba(28, 18, 8, 0.88));
}
.island-card-overlay-inner {
    width: 100%; max-width: 26rem;
    max-height: 68vh;
    overflow: auto;
}

/* Fear deck: a deck of cards broken into sections. Each section is a stack of overlapping face-down "Fear"
   card rectangles (top one printed "FEAR / n", like the power decks) followed by the terror divider card it
   precedes (TERROR II/III) and finally the FEAR VICTORY card. Card geometry matches .power-deck (5:7). */
.fear-deck { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; }

/* A section's overlapping rectangles: a flex row where each card after the first slides left to overlap, so
   only a sliver of the buried cards shows and the last (top) card — bearing the count — sits fully on top. */
.fd-stack { display: flex; }
.fd-card {
    flex: 0 0 auto;
    width: 3.4rem; height: 4.76rem;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.12rem;
    border-radius: 0.4rem;
    background: linear-gradient(160deg, #3a2a52, #241834);
    border: 1px solid #4a3f6e;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
    color: #e7eef0;
}
.fd-card + .fd-card { margin-left: -3.0rem; }   /* overlap: ~0.4rem of each buried card peeks out */
.fd-card--top { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); }
.fd-card-kind { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.08em; color: #b9c4cf; }
.fd-card-count { font-size: 1.35rem; font-weight: 800; line-height: 1; }

/* The terror divider card that closes a section. */
.fd-divider {
    flex: 0 0 auto;
    width: 3.4rem; height: 4.76rem;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.1rem;
    border-radius: 0.4rem;
    background: linear-gradient(160deg, #6b4486, #3f2752);
    border: 1px solid rgba(240, 227, 247, 0.35);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
    color: #f0e3f7;
}
.fd-divider--victory { background: linear-gradient(160deg, #a86a30, #6a3f18); color: #ffe9c9; border-color: rgba(255, 233, 201, 0.4); }
.fd-divider-l1 { font-size: 0.56rem; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; }
.fd-divider-l2 { font-size: 1.1rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; line-height: 1; }

.island-stub {
    padding: 0.6rem;
    border-radius: 2px;
    background: rgba(58, 38, 20, 0.04);
    border: 1px dashed rgba(58, 38, 20, 0.35);
}

.island-stub .muted { margin: 0; font-size: 0.78rem; }

/* Pool bars (Fear / Blight): a vertical track whose fill rises from the bottom to Value/Max, plus a readout. */
.pool {
    padding: 0.6rem;
    border-radius: 2px;
    background: rgba(58, 38, 20, 0.05);
    border: 1px solid rgba(58, 38, 20, 0.3);
}
.pool-body { display: flex; align-items: stretch; gap: 0.6rem; }
.pool-track {
    position: relative;
    width: 18px;
    min-height: 72px;
    border-radius: 0.3rem;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.12);
    overflow: hidden;
}
.pool-fill {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    transition: height 0.25s ease;
}
.pool-info { display: flex; flex-direction: column; justify-content: center; gap: 0.15rem; }
.pool-value { font-size: 1.05rem; font-weight: 800; color: #120b02; }
.pool-caption { font-size: 0.68rem; color: #54401d; text-transform: uppercase; letter-spacing: 0.06em; }

/* Earned fear cards stay face-down until resolved — a row of small hatched card backs, one per held card. */
.fear-backs { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.5rem; }
.fear-back {
    width: 2rem; height: 2.7rem;
    display: flex; align-items: center; justify-content: center;
    border-radius: 0.3rem;
    font-size: 0.52rem; font-weight: 700; letter-spacing: 0.03em;
    color: #d9c9ef;
    background: repeating-linear-gradient(45deg, #2a2440, #2a2440 4px, #332b4e 4px, #332b4e 8px);
    border: 1px solid #4a3f6e;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Minor & Major power decks: card-proportioned (5:7) tiles with deck + discard counts. They blink and become
   clickable while the viewing player owes a Minor-or-Major choice (PickPowerDeck question). */
.power-decks { display: flex; gap: 0.6rem; align-items: center; }
.power-deck {
    flex: 0 0 auto;
    box-sizing: border-box;   /* so the <button> minor/major tiles size identically to the <div> event tile */
    width: 3.4rem; height: 4.76rem;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.12rem;
    padding: 0.3rem;
    border-radius: 0.4rem;
    border: 1px solid rgba(211, 194, 158, 0.3);
    color: #e7eef0;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);   /* keep labels legible over the textured card-back art */
    font: inherit;
    cursor: default;
}
/* The Event deck sits next to the two power decks but is its own thing — a little extra space sets it apart. */
.power-deck--separated { margin-left: 0.9rem; }
/* Card-back art evoking the physical decks. Each is a stack of background layers (top→bottom): a couple of
   thin bright-white diagonal streaks reading as a faint magical glyph, a soft tinted glow, then the base
   colour wash. */

/* Minor: an earthy brown worn tome, warm light bleeding from the top, with hairline white glyph highlights. */
.power-deck--minor {
    background:
        linear-gradient(118deg, transparent 31%, rgba(255, 255, 255, 0.5) 32%, rgba(255, 255, 255, 0.5) 32.7%, transparent 33.7%),
        linear-gradient(47deg, transparent 60%, rgba(255, 255, 255, 0.32) 61%, transparent 62%),
        radial-gradient(120% 75% at 50% -12%, rgba(196, 154, 96, 0.55), transparent 60%),
        linear-gradient(160deg, #6b4e2d 0%, #4a361f 50%, #2d1f12 100%);
}
/* Major: a washed-out, low-saturation blue/green/yellow blend with brighter white glyph highlights. */
.power-deck--major {
    background:
        linear-gradient(114deg, transparent 37%, rgba(255, 255, 255, 0.6) 38%, rgba(255, 255, 255, 0.6) 38.8%, transparent 39.8%),
        linear-gradient(63deg, transparent 65%, rgba(255, 255, 255, 0.33) 66%, transparent 67%),
        radial-gradient(135% 95% at 24% 14%, rgba(214, 228, 205, 0.34), transparent 56%),
        linear-gradient(155deg, #7a6f5a 0%, #7d7358 44%, #867c52 74%, #464038 100%);
}
/* The Event deck is a read-only indicator (a div, never clickable) styled like the power decks — a smoky,
   dark top igniting into a red/orange fire-storm glow rising from the bottom edge. */
.power-deck--event {
    background:
        radial-gradient(70% 45% at 50% 108%, rgba(255, 226, 150, 0.95), transparent 55%),
        radial-gradient(95% 70% at 50% 120%, rgba(245, 130, 40, 0.9), transparent 62%),
        radial-gradient(125% 82% at 50% 138%, rgba(190, 42, 20, 0.85), transparent 72%),
        linear-gradient(180deg, #290a06 0%, #5e160a 40%, #94290e 70%, #c2410c 100%);
}
.power-deck-kind { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.08em; color: #b9c4cf; }
.power-deck-count { font-size: 1.35rem; font-weight: 800; line-height: 1; }
.power-deck-foot { font-size: 0.54rem; color: #54401d; }
.power-deck.is-choosing { cursor: pointer; border-color: var(--flash-color); animation: attention-blink 1.3s ease-in-out infinite; }
.power-deck.is-choosing:hover { filter: brightness(1.15); }
.power-deck-cue { margin: 0.4rem 0 0; font-size: 0.72rem; color: var(--flash-color); }

.island-empty { margin: 0; font-size: 0.78rem; }

/* Expanded (readable) fear / event card shown inside the overlay. Opaque parchment so the dimmed panel behind
   doesn't bleed through, with a shadow lifting it off the scrim. */
.island-card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.7rem 0.75rem;
    text-align: left;
    border-radius: 4px;
    background: #efe2c4;
    border: 1px solid rgba(58, 38, 20, 0.5);
    box-shadow: 0 6px 22px rgba(0, 0, 0, 0.4);
    color: inherit;
    font: inherit;
}

/* The maximised card must be fully opaque parchment — nothing behind it should ever bleed through. */
.island-card--expanded { background: #efe2c4; opacity: 1; }

/* A card with an open question for this player blinks the universal yellow to-do cue. Unlike the thumbnails —
   which sit on the light parchment panel — the expanded card floats over the near-opaque dark scrim, so it must
   keep its own opaque parchment background while blinking. attention-blink animates background-color (driving it
   transparent at the trough), which would clobber the parchment and leave near-black text on the dark scrim; use
   the ring-only pulse (border + shadow) so the parchment stays put. */
.island-card.is-attention {
    border-color: var(--flash-color);
    animation: pcard-attention-blink 1.3s ease-in-out infinite;
}

.island-card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}
.island-card-heading { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }

/* Close (×) button in the card head. */
.island-card-close {
    flex: 0 0 auto;
    width: 1.5rem; height: 1.5rem;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    border: 1px solid rgba(58, 38, 20, 0.4);
    background: rgba(58, 38, 20, 0.06);
    color: #54401d; font-size: 1rem; line-height: 1;
    cursor: pointer;
}
.island-card-close:hover { background: rgba(58, 38, 20, 0.16); color: #120b02; }

/* Answer button on a card that owes this player a decision. */
.island-card-answer {
    align-self: flex-start;
    padding: 0.35rem 0.7rem;
    border-radius: 3px;
    border: 1px solid var(--flash-color);
    background: var(--flash-color);
    color: #120b02; font-weight: 700; font-size: 0.76rem;
    cursor: pointer;
}
.island-card-answer:hover { filter: brightness(1.08); }

.island-card-title { font-weight: 600; color: #120b02; font-size: 0.92rem; }
.island-card-sub { font-size: 0.66rem; color: #54401d; white-space: nowrap; }

.island-card-sections { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }

.island-card-section {
    font-size: 0.72rem;
    line-height: 1.3;
    color: #33240c;
    padding: 0.1rem 0.3rem;
    border-left: 2px solid transparent;
    border-radius: 0.2rem;
}

/* The tier / event currently in force. */
.island-card-section.is-active {
    color: #0f0a02;
    background: rgba(58, 38, 20, 0.12);
    border-left-color: #8a5212;
}

.island-card-section-label {
    display: inline-block;
    margin-right: 0.35rem;
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #54401d;
}

.island-card-cue {
    align-self: flex-start;
    font-size: 0.66rem;
    font-weight: 600;
    color: var(--flash-color);
}

/* Invader card slots: a single pipeline row — Discard ← Ravage ← Build ← Explore ← Deck. Cards share the
   5:7 geometry of the power/fear decks. Action slots are FACE-UP (land-coloured); Deck/Discard are FACE-DOWN
   cream backs drawn as overlapping stacks (one per stage). Scrolls horizontally if the pipeline is wide. */
.invader-slots { display: flex; flex-direction: column; gap: 0.6rem; }

.inv-slot-row { display: flex; gap: 0.6rem; align-items: flex-start; justify-content: space-between; overflow-x: auto; padding-bottom: 0.2rem; }

/* A column = a label above its card(s). */
.inv-col { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 0.3rem; }
.inv-slot-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #452f10;
    white-space: nowrap;
}

/* The action slot whose timing point is currently resolving pulses RED — title + card border — to draw the
   eye to the live step. Deliberately red (not the universal yellow attention blink) so it never reads as
   "a question is posed to you". */
@keyframes inv-active-blink {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}
.inv-col.is-active .inv-slot-label {
    color: #ff6b6b;
    font-weight: 800;
    animation: inv-active-blink 1.1s ease-in-out infinite;
}
.inv-col.is-active .inv-card,
.inv-col.is-active .inv-card--empty {
    border-color: #ff6b6b;
    box-shadow: 0 0 0 1px #ff6b6b, 0 0 8px rgba(255, 107, 107, 0.55);
    animation: inv-active-blink 1.1s ease-in-out infinite;
}

/* Cards in a column sit in a row; stage stacks overlap (only a sliver of buried cards peeks out). */
.inv-row { display: flex; gap: 0.4rem; }
.inv-stack { display: flex; }
.inv-stack .inv-card + .inv-card { margin-left: -3.0rem; }

.inv-card {
    flex: 0 0 auto;
    width: 3.4rem; height: 4.76rem;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.1rem;
    border-radius: 0.4rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Face-up action cards: background colour supplied inline (land tile colour / diagonal split). */
.inv-card--up {
    border: 1px solid rgba(0, 0, 0, 0.4);
    color: #fff;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

/* Face-down deck/discard backs: cream card with dark text. */
.inv-card--down {
    background: linear-gradient(160deg, #f3ecd9, #e2d7bb);
    border: 1px solid #b9ac86;
    color: #3a2f1c;
}
.inv-card--down-top { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); }

.inv-card--empty {
    width: 3.4rem; height: 4.76rem;
    display: flex; align-items: center; justify-content: center;
    color: #54401d;
    background: rgba(58, 38, 20, 0.05);
    border: 1px dashed rgba(58, 38, 20, 0.4);
    border-radius: 0.4rem;
}

.inv-card-kind { font-size: 0.55rem; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.85; }
.inv-card-land { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.02em; line-height: 1.05; }
.inv-card-stage-label { font-size: 0.62rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; line-height: 1; }
.inv-card-stage-num { font-size: 1.3rem; font-weight: 800; line-height: 1; }

/* ── Spirit resources ──────────────────────────────────────────────────── */
.res-grid { display: flex; gap: 0.5rem; margin-bottom: 0.7rem; }

.res-cell {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    padding: 0.4rem 0.3rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.05);
}

/* The Reclaim resource becomes a real button while a reclaim is owed: strip the native button chrome so it
   still reads as a resource cell, then add the universal yellow attention blink + a ring so it's clearly
   clickable. Clicking opens the discard picker. */
.res-cell--action {
    font: inherit;
    color: inherit;
    border: none;
    cursor: pointer;
}
.res-cell--attention {
    box-shadow: 0 0 0 2px var(--flash-color);
    animation: attention-blink 1.3s ease-in-out infinite;
}

.res-icon { font-size: 1rem; }
.res-val { font-size: 1.2rem; font-weight: 700; color: #190f04; }
.res-lbl { font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em; color: #452f10; }

.res-elements {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-bottom: 0.7rem;
}

.res-piles {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.8rem;
    color: #b3a486;
}
