/* GENERATED FILE — the stylesheet source lives in web-client/styles/*.css.
   Edit those files, then `npm run sync:static` regenerates this concatenation. */

/* ==========================================================================
   Design tokens, reset, themes, scenes
   ========================================================================== */

/* ==========================================================================
   Discipline Online — v0.2 dark-fantasy design system (WP-15)
   Layout: centered stage, no page scroll, hand docked bottom, left rail.
   Cards: one poker-ratio component (5:7) everywhere.
   ========================================================================== */
:root {
    --card-w: 132px;
    --card-h: calc(var(--card-w) * 1.4);
    /* 2.5 x 3.5 poker ratio */
    --gold: #d4a94b;
    --gold-bright: #ffd98a;
    --gold-dim: #8a6d2f;
    --parchment: #e8d9b5;
    --ink: #1a1208;
    --panel: rgba(24, 18, 10, .88);
    --panel-border: #4a3c22;
    --blood: #c94f42;
    --ok: #74b06a;
    --arcane: #8ab4dd;
    --day-bg1: #241a0e;
    --day-bg2: #3a2c16;
    --night-bg1: #0c0f1e;
    --night-bg2: #1d2440;
    --fac-holy_kingdom: #d4a94b;
    --fac-desolate_throne: #7ee787;
    --fac-verdant_alliance: #7cbf6b;
    --fac-burning_covenant: #e2643c;
    --fac-neutral: #a89a7c;
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html,
body {
    height: 100%;
    overflow: hidden;
}

body {
    font-family: 'Alegreya', Georgia, serif;
    font-size: 18px;
    color: var(--parchment);
    background: radial-gradient(ellipse at top, var(--day-bg2), var(--day-bg1) 72%);
    transition: background 1.2s ease;
}

body::before {
    /* tabletop mat texture */
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: url("assets/texture.jpg") center/cover;
    opacity: .28;
    filter: saturate(.8);
}

body.day-theme {
    background: radial-gradient(ellipse at top, var(--day-bg2), var(--day-bg1) 72%);
}

body.night-theme {
    background: radial-gradient(ellipse at top, var(--night-bg2), var(--night-bg1) 72%);
}

h1,
h2,
h3,
h4,
.btn,
.brand,
.loc-card b {
    font-family: 'Cinzel', serif;
}

/* ------------------------------------------------------------- scenes */
.scene {
    display: none;
    height: 100vh;
    /* v0.6.5.9 item 6: iPad Safari's toolbars shrink the REAL viewport —
       dvh follows them, so the hand never sinks below the screen's edge */
    height: 100dvh;
}

.scene.on {
    display: flex;
    flex-direction: column;
}

#scene-setup.on {
    align-items: center;
    justify-content: center;
}

body.night-theme .hand-row {
    min-height: calc(var(--card-h) + 40px);
}

body.targeting,
body.targeting .slot {
    cursor: crosshair;
}

body.targeting .slot:not(.legal-target):not(.spell-target) .card {
    cursor: not-allowed;
}

/* the enemy ranks always fit — no stray scrollbar */
/* v0.4.1 ---------------------------------------------------------------- */
/* F3: the dead-marker pop happens only in battle; day re-renders stay still */
body.night-theme .card.dead::after {
    animation: skull-in .6s ease;
}


/* v0.6.5.7 item 8: every scrollbar in the realm is thin and theme-colored */
* {
    scrollbar-width: thin;
    scrollbar-color: #6b5a2a rgba(20, 14, 7, .4);
}
/* ==========================================================================
   Ornate panels, brand, shared text helpers
   ========================================================================== */

/* ------------------------------------------------------------- panels */
.panel-ornate {
    background: var(--panel);
    border: 2px solid var(--panel-border);
    border-radius: 14px;
    box-shadow: inset 0 0 40px rgba(0, 0, 0, .55), 0 8px 30px rgba(0, 0, 0, .5);
    position: relative;
}

.panel-ornate::before {
    content: "";
    position: absolute;
    inset: 5px;
    pointer-events: none;
    border: 1px solid rgba(212, 169, 75, .28);
    border-radius: 10px;
}

.brand-title,
.brand {
    color: var(--gold);
    letter-spacing: 3px;
    font-weight: 700;
}

.brand-title span,
.brand span {
    color: var(--parchment);
    font-weight: 500;
}

.tagline {
    color: #a89a7c;
    font-style: italic;
    margin: 4px 0 14px;
}

.error {
    color: #ff9a8a;
    min-height: 1.2em;
    font-size: 15px;
}

.error.floating {
    position: fixed;
    top: 74px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 300;
    background: rgba(60, 12, 8, .95);
    padding: 8px 22px;
    border-radius: 8px;
    display: none;
    border: 1px solid #7a3328;
}

.hint {
    color: #a89a7c;
    font-size: 15px;
}

.center {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
}

.brand.small {
    font-size: 17px;
}

.rail-title {
    color: var(--gold);
    font-size: 15px;
    letter-spacing: 1px;
}

.panel {
    display: none;
    flex: 1 1 auto;
    min-height: 0;
}

.panel.on {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.panel-title {
    color: var(--gold);
    font-size: 18px;
    letter-spacing: 1px;
}

/* the XP ۞ glyph (v0.6.8.1 item 2 — a text glyph, so it takes the gold) used in buttons, prices and totals (v0.6.4.4)
   v0.6.8.2 item 17: it must read as the equal of 💰. Gold is an EMOJI with no
   class, so it renders at the inherited size and its glyph fills more of the
   em box than a text character does. A fixed 13px therefore looked small in
   every context — the size is now relative (matching gold's inheritance) and
   nudged up to close the optical gap between an emoji and a text glyph. */
.xp-ico {
    font-size: 1.18em;
    line-height: 1;
    display: inline-block;
    vertical-align: -2px;
    color: var(--gold-bright);
}

/* ---------------------------------------------------------------- v0.6.8.4
   item 1: a run of failures turns the passing toast into a way out. It STAYS
   (no 3.2s fade) while the streak holds — a blinking message is no use to a
   player already staring at a frozen board. */
.error.floating.stuck {
    display: flex !important;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    border-color: var(--blood, #c94f42);
    box-shadow: 0 0 18px rgba(201, 79, 66, .45);
}

.error.floating.stuck .err-count {
    font-style: normal;
    font-size: 10.5px;
    opacity: .7;
}

/* review H1: the banner must not swallow taps in its band — only its own
   controls are clickable, the board behind it stays reachable. */
.error.floating.stuck {
    pointer-events: none;
}

.error.floating.stuck .err-doors {
    display: flex;
    gap: 6px;
    pointer-events: auto;
}

/* ---------------------------------------------------------------- v0.6.8.4
   item 5: the feedback letter under "Exit to menu". It takes what is left of
   the closing panel — never squeezing the scoreboard above it. */
.feedback-box {
    margin: 14px auto 4px;
    max-width: 720px;
    width: 100%;
    padding: 12px 14px 14px;
    border: 1px solid rgba(212, 169, 75, .35);
    border-radius: 12px;
    background: rgba(20, 15, 9, .55);
    text-align: left;
}

.feedback-box h4 {
    margin: 0 0 4px;
    font-family: var(--font-display, "Cinzel", serif);
    color: var(--gold, #ffd98a);
    font-size: 15px;
    letter-spacing: .5px;
}

.feedback-box .hint {
    margin: 0 0 10px;
    line-height: 1.45;
}

.fb-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 6px;
}

.fb-tools .btn {
    min-width: 30px;
    padding: 2px 7px;
}

.fb-tools .btn.on {
    border-color: var(--gold, #ffd98a);
    box-shadow: 0 0 8px rgba(255, 217, 138, .45);
}

.fb-text {
    display: block;
    width: 100%;
    box-sizing: border-box;
    min-height: 92px;
    resize: vertical;
    padding: 8px 10px;
    border: 1px solid rgba(212, 169, 75, .3);
    border-radius: 8px;
    background: rgba(8, 6, 4, .65);
    color: var(--parchment, #e8d9b5);
    font-family: inherit;
    font-size: 13px;
    line-height: 1.5;
}

.fb-text:focus {
    outline: none;
    border-color: var(--gold, #ffd98a);
}

.fb-shots {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}

.fb-shots figure {
    position: relative;
    margin: 0;
    width: 84px;
    height: 84px;
    border: 1px solid rgba(212, 169, 75, .35);
    border-radius: 8px;
    overflow: hidden;
}

.fb-shots img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.fb-drop {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 20px;
    height: 20px;
    line-height: 1;
    padding: 0;
    border: 1px solid rgba(212, 169, 75, .5);
    border-radius: 50%;
    background: rgba(10, 7, 4, .85);
    color: var(--parchment, #e8d9b5);
    cursor: pointer;
    font-size: 11px;
}

.fb-preview {
    margin-top: 8px;
    padding: 8px 10px;
    border: 1px dashed rgba(212, 169, 75, .3);
    border-radius: 8px;
    background: rgba(8, 6, 4, .4);
    font-size: 13px;
    line-height: 1.5;
    max-height: 260px;
    overflow: auto;
}

.fb-preview img {
    max-width: 100%;
    border-radius: 6px;
}

.fb-preview h4,
.fb-preview h5,
.fb-preview h6 {
    color: var(--gold, #ffd98a);
    margin: 6px 0 2px;
}

.fb-preview p {
    margin: 4px 0;
}

/* the send row keeps its slot whether or not there is anything to send */
.fb-send {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
    flex-wrap: wrap;
}

.feedback-box.sent {
    text-align: center;
    border-color: rgba(120, 200, 140, .45);
}
/* ==========================================================================
   Buttons — never moved, only disabled (AGENTS.md §4)
   ========================================================================== */

/* ------------------------------------------------------------- buttons */
.btn {
    background: linear-gradient(180deg, #3a2f1c, #241c10);
    color: var(--parchment);
    border: 1px solid var(--panel-border);
    padding: 10px 20px;
    border-radius: 8px;
    cursor: pointer;
    font-family: 'Cinzel', serif;
    font-size: 15px;
    letter-spacing: .6px;
    transition: all .15s;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .7);
}

.btn:hover:not(:disabled) {
    border-color: var(--gold);
    box-shadow: 0 0 14px rgba(212, 169, 75, .35);
    transform: translateY(-1px);
}

.btn:disabled {
    opacity: .38;
    cursor: default;
}

.btn.primary {
    background: linear-gradient(180deg, #7a5c22, #4a3510);
    border-color: var(--gold-dim);
}

.btn.primary:not(:disabled) {
    animation: btn-breathe 2.6s ease-in-out infinite;
}

.btn.big {
    font-size: 19px;
    padding: 14px 34px;
}

.btn.small {
    font-size: 12.5px;
    padding: 6px 12px;
}

.btn.danger {
    background: linear-gradient(180deg, #6e2318, #40120a);
    border-color: #8a3328;
}

@keyframes btn-breathe {
    50% {
        box-shadow: 0 0 16px rgba(212, 169, 75, .30);
    }
}

/* ==========================================================================
   The faction-pick lobby
   ========================================================================== */

/* ------------------------------------------------------------- setup */
.setup-box {
    /* v0.6.8.1 item 5: the welcome chip anchors to the box's top-right */
    position: relative;
    width: 660px;
    max-width: 94vw;
    padding: 30px 36px;
    text-align: center;
}

.setup-logo {
    width: 130px;
    margin: -6px auto 4px;
    filter: drop-shadow(0 4px 14px rgba(212, 169, 75, .4));
}

.setup-box label {
    display: block;
    text-align: left;
    margin: 12px 0 4px;
    color: #bfae88;
    font-size: 15px;
}

.setup-box input {
    background: #14100a;
    color: var(--parchment);
    border: 1px solid var(--panel-border);
    padding: 10px 12px;
    border-radius: 8px;
    width: 100%;
    font-size: 17px;
    font-family: inherit;
}

.faction-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin: 12px 0;
}

.faction-card {
    background: #17120b;
    border: 2px solid #2e2517;
    border-radius: 10px;
    padding: 14px;
    color: var(--parchment);
    cursor: pointer;
    text-align: left;
    transition: all .15s;
    display: grid;
    grid-template-columns: 40px 1fr;
    grid-template-rows: auto auto;
    column-gap: 10px;
    font-family: inherit;
    font-size: 16px;
}

.faction-card .fx-emblem {
    grid-row: span 2;
    font-size: 28px;
    align-self: center;
}

.faction-card b {
    font-family: 'Cinzel', serif;
    letter-spacing: .5px;
}

.faction-card i {
    color: #a89a7c;
    font-size: 13.5px;
}

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

.faction-card.selected {
    border-color: var(--gold);
    box-shadow: 0 0 18px rgba(212, 169, 75, .4);
}

/* the setup screen's side-by-side link row (library / release notes) */
.setup-links {
    display: flex;
    gap: 8px;
}

.btn.big {
    margin: 14px;
}

.setup-links .btn {
    flex: 1 1 0;
    text-align: center;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
/* ==========================================================================
   v0.6.6.2 items 19-21 — the epic gate: the four legends drift behind the
   title under a dark vignette, and each faction card wears its legend
   ========================================================================== */
.setup-epic {
    position: fixed;
    inset: 0;
    z-index: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    overflow: hidden;
    pointer-events: none;
}

.setup-epic i {
    background-size: cover;
    background-position: center;
    opacity: .55;
    filter: saturate(.85) brightness(.75);
    animation: epic-drift 26s ease-in-out infinite alternate;
}

.setup-epic i:nth-child(2) { animation-delay: -7s; }
.setup-epic i:nth-child(3) { animation-delay: -13s; }
.setup-epic i:nth-child(4) { animation-delay: -20s; }

@keyframes epic-drift {
    0% { transform: scale(1.05) translate(-1.5%, -1%); }
    100% { transform: scale(1.18) translate(1.5%, 1.5%); }
}

/* the vignette pools the eye toward the gate */
.setup-epic::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse at center, rgba(10, 7, 3, .55) 30%, rgba(10, 7, 3, .92) 78%),
        linear-gradient(180deg, rgba(10, 7, 3, .65), transparent 30%, transparent 65%, rgba(10, 7, 3, .8));
}

/* the box rises above the legends */
#scene-setup .setup-box {
    position: relative;
    z-index: 1;
    background: rgba(16, 11, 6, .88);
}

/* the logo breathes (item 20) */
@keyframes logo-breathe {
    0%, 100% { filter: drop-shadow(0 4px 14px rgba(212, 169, 75, .35)); transform: scale(1); }
    50% { filter: drop-shadow(0 4px 26px rgba(255, 216, 120, .8)); transform: scale(1.035); }
}

.setup-logo {
    animation: logo-breathe 4.5s ease-in-out infinite;
}

/* v0.6.6.3 item 5: faction cards wear their PAINTED PORTRAIT COVER
   (inline background image) — the scene sits high, under the left text wash */
.faction-card {
    background-size: cover;
    background-position: center 24%;
}

/* item 5: the gate's great button rises from the box art itself */
#start-btn {
    background:
        linear-gradient(180deg, rgba(56, 38, 12, .62), rgba(20, 12, 5, .86)),
        url("/assets/covers/box.jpg") center 88% / cover no-repeat;
    border-color: var(--gold-dim);
    text-shadow: 0 1px 4px rgba(0, 0, 0, .95);
}

/* ==========================================================================
   v0.6.7.0 item 1 — the war council (multiplayer lobby)
   ========================================================================== */

.lobby-room { max-width: 560px; }

.lobby-seats {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 14px 0;
}

.lobby-seat {
    display: grid;
    grid-template-columns: 34px 1fr auto 84px;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border: 1px solid rgba(184, 146, 92, .35);
    border-radius: 8px;
    background: rgba(24, 17, 10, .55);
    color: var(--ink-dim, #b7a98f);
}

.lobby-seat.claimed { color: var(--ink, #e8dcc4); border-color: rgba(184, 146, 92, .6); }

.lobby-seat.mine { box-shadow: 0 0 0 1px rgba(226, 190, 118, .55), 0 0 14px rgba(226, 190, 118, .18); }

.lobby-seat .ls-emblem { font-size: 20px; text-align: center; }

.lobby-seat .ls-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.lobby-seat .ls-faction { font-size: 12px; opacity: .8; }

.lobby-seat .ls-ready { font-size: 12px; text-align: right; opacity: .55; }

.lobby-seat .ls-ready.on { color: #8fd489; opacity: 1; }

.lobby-join { display: flex; flex-direction: column; gap: 10px; }

.lobby-factions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

/* ==========================================================================
   v0.6.7.0 item 5 — the replay viewer (Hall of Chronicles)
   ========================================================================== */

.replay-shelf, .replay-player { max-width: 640px; }

.replay-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; max-height: 46vh; overflow-y: auto; }

.replay-entry { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; text-align: left; }

.replay-entry i { font-size: 12px; opacity: .75; font-style: normal; }

.replay-controls { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }

.replay-jumps { display: flex; gap: 6px; margin-bottom: 8px; flex-wrap: wrap; }

.replay-jumps .btn { font-size: 11px; padding: 3px 8px; }

.replay-scroll { max-height: 46vh; min-height: 240px; overflow-y: auto; text-align: left; }

/* v0.6.7.0 item 7 — the rival tier row */
.bot-tier-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin: 4px 0 8px; }
.bot-tier-row .btn { font-size: 12px; padding: 6px 4px; }

/* v0.6.7.0 items 9/12 — victory paths + bloodline draft at the gate */
.mode-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 0 0 8px; }
.mode-row .btn { font-size: 12px; padding: 6px 4px; }
.draft-box { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; max-height: 30vh; overflow-y: auto; }
.draft-class { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.draft-cls-name { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; opacity: .7; min-width: 64px; text-align: left; }
.draft-class .btn { font-size: 11px; padding: 4px 8px; }

/* v0.6.7.0 item 10 — the Hall of Feats */
.hall-of-feats { max-height: 30vh; overflow-y: auto; text-align: left; margin-bottom: 8px; }
.feat-row { display: flex; align-items: baseline; gap: 8px; padding: 3px 4px; opacity: .45; font-size: 13px; }
.feat-row.earned { opacity: 1; }
.feat-row i { font-style: normal; font-size: 11px; opacity: .75; flex: 1; }
.feat-row .feat-count { color: #e2be76; font-size: 12px; }

/* v0.6.7.0 item 23 — the ambience budget: small screens carry two drifting
   quadrants instead of four, and the drift slows to spare weak GPUs */
@media (max-width: 767px) {
    .setup-epic i:nth-child(3),
    .setup-epic i:nth-child(4) { display: none; }
    .setup-epic i { animation-duration: 44s; will-change: transform; }
}

/* v0.6.7.2 item 5: the gate leads with the war council — open councils are
   listed for one-click joining, and the rare dials fold away */
.lobby-list {
    margin: 6px 0 2px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 100%;
    align-self: stretch;
    /* buttons-never-move: the poll adds/removes rows every 4s — the list owns
       a FIXED footprint (title + up to five rows) and scrolls inside it, so
       March alone / Battle settings / the hall links never shift */
    height: 148px;
    overflow-y: auto;
}

.lobby-list-title {
    text-transform: uppercase;
    letter-spacing: 2px;
    font-size: 11px;
    margin: 0;
}

.lobby-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 10px;
    border: 1px solid #3a2e1a;
    border-radius: 8px;
    background: rgba(20, 14, 8, .6);
}

.lobby-row .lobby-host {
    flex: 1;
    text-align: left;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lobby-row .lobby-claimed {
    font-size: 12px;
    opacity: .8;
    white-space: nowrap;
}

.settings-toggle {
    align-self: center;
}

.settings-fold {
    border: 1px solid #3a2e1a;
    border-radius: 10px;
    padding: 8px 10px;
    background: rgba(16, 11, 7, .55);
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* ==========================================================================
   v0.6.7.4 item 21 — the gate's three doors, the muster, the custom screen
   ========================================================================== */
.gate-doors {
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 100%;
    margin: 8px 0 10px;
}

.gate-panel {
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 100%;
}

.gate-panel-title {
    font-family: 'Cinzel', serif;
    color: var(--gold-bright, #d4a94b);
    letter-spacing: 2px;
    margin: 2px 0 4px;
}

.back-btn {
    align-self: center;
    margin-top: 6px;
}

/* the muster: crossed swords pulse while the pool gathers */
.queue-wait {
    margin: 18px auto 6px;
    width: 96px;
    height: 96px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid rgba(212, 169, 75, .5);
    border-radius: 50%;
    animation: muster-ring 2.2s ease-in-out infinite;
}

.qw-swords {
    font-size: 44px;
    animation: muster-swords 2.2s ease-in-out infinite;
}

@keyframes muster-ring {
    0%, 100% { box-shadow: 0 0 0 0 rgba(212, 169, 75, .45); }
    55% { box-shadow: 0 0 0 18px rgba(212, 169, 75, 0); }
}

@keyframes muster-swords {
    0%, 100% { transform: rotate(-6deg) scale(1); }
    50% { transform: rotate(6deg) scale(1.12); }
}

.queue-count {
    font-family: 'Cinzel', serif;
    font-size: 19px;
    color: #e8dcbc;
    margin: 4px 0 2px;
}

/* the custom door: password + visibility side by side */
.custom-door-row {
    display: flex;
    gap: 8px;
    align-items: flex-end;
    width: 100%;
}

.custom-door-row label { flex: 1; }

.lobby-mod {
    font-style: normal;
    margin-left: 5px;
    opacity: .9;
}

.lobby-pass {
    display: inline-flex;
    gap: 6px;
    align-items: center;
}

.lobby-pass input { width: 90px; }

/* the single-player wing: four doors in a 2x2 */
.sp-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    width: 100%;
}

.sp-grid .btn i { display: block; font-style: normal; font-size: 10.5px; opacity: .7; }

/* v0.6.7.4 item 21.8: the gate must FIT a phone — the scene scrolls and
   the box centers by margin, so neither edge ever clips out of reach */
#scene-setup.on {
    overflow-y: auto;
    justify-content: flex-start;
}

#scene-setup .setup-box {
    margin: auto;
}

/* v0.6.7.4 item 21.10: the release notes framed INSIDE the PWA */
.notes-scene {
    display: flex;
    align-items: center;
    justify-content: center;
}

.notes-frame {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    width: min(980px, 96vw);
    height: min(92vh, 92dvh);
    background: rgba(16, 11, 6, .92);
    /* v0.6.8.2 item 4: the frame had NO padding, so the "Release Notes"
       caption and the "Back to the gate" button sat flush against the
       panel edge and collided with the ornate hairline `.panel-ornate::before`
       draws at inset:5px. Clear the hairline, then breathe. */
    padding: 12px 14px;
    gap: 10px;
}

.notes-page {
    flex: 1;
    border: 0;
    border-radius: 8px;
    background: #0d0a06;
}

/* ---- v0.6.8.0 item 1: the account panel at the gate -------------------- */
.gate-panel .account-title {
    margin-top: 14px;
    border-top: 1px solid rgba(255, 255, 255, .12);
    padding-top: 12px;
}

.account-box {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.account-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.account-social {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.account-social .btn:disabled {
    opacity: .45;
}

/* v0.6.8.1 item 5: who stands at the gate — pinned to the title screen's
   top-right corner, with the door to the account/settings */
.gate-account-chip {
    position: absolute;
    top: 10px;
    right: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
    z-index: 5;
}

.gate-welcome {
    font-size: 12.5px;
    color: var(--parchment);
    opacity: .85;
    white-space: nowrap;
}

.gate-welcome b {
    color: var(--gold-bright);
}

/* On a narrow screen the two corner chips would collide (a 280px foldable
   leaves ~235px between them and the pair wants ~263px). The GREETING is the
   one that yields: the name is on the settings screen anyway, while the
   version print and the Log In door both have to stay reachable. */
@media (max-width: 420px) {
    .gate-welcome {
        display: none;
    }
}

/* v0.6.8.2 item 1: the build, in small print, mirroring the welcome chip */
.gate-version {
    position: absolute;
    top: 10px;
    left: 12px;
    z-index: 5;
    font-size: 11px;
    letter-spacing: .04em;
    color: var(--parchment);
    opacity: .55;
    white-space: nowrap;
    pointer-events: none;
}

/* v0.6.8.2 item 3: the label sits ABOVE its own row so the three paces share
   one line. The old markup put the label INSIDE .anim-speeds — a 3-column
   grid — making it a fourth cell that pushed a button onto a second row. */
.pace-field {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}

.pace-field > b {
    font-size: 13px;
}

.pace-row {
    display: flex;
    justify-content: center;
    gap: 8px;
    width: 100%;
}

.pace-row .btn {
    flex: 1 1 0;
    min-width: 0;
}

/* item 15: the name leads the settings screen, so it needs the breathing
   room the account title used to give it */
.gate-name-field {
    margin-bottom: 4px;
}

/* item 14: four Single Player doors must fit a phone. Grid items default to
   min-width:auto, so a long label (".btn.big" keeps 19px text and 14px
   margins) forces each 1fr track past its share and the row overflows. */
.sp-grid > .btn {
    min-width: 0;
    margin: 4px 0;
    padding: 10px 8px;
    font-size: 15px;
    overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- v0.6.8.4
   item 7: the owner's line, quiet at the foot of the gate and the settings */
.gate-legal {
    margin-top: 16px;
    font-size: 11px;
    letter-spacing: .04em;
    color: var(--parchment);
    opacity: .45;
}

/* item 1: the crash screen the ErrorBoundary renders when a scene throws */
.crash-scene {
    display: flex;
    align-items: center;
    justify-content: center;
}

.crash-box {
    text-align: center;
    max-width: 560px;
}

.crash-detail {
    margin: 10px 0 4px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11.5px;
    opacity: .7;
    word-break: break-word;
}

.crash-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    margin-top: 14px;
}
/* ==========================================================================
   Topbar: players strip, era badge, army & era popovers
   ========================================================================== */

/* ------------------------------------------------------------- topbar */
.topbar {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 8px 18px;
    background: linear-gradient(180deg, rgba(14, 10, 5, .92), rgba(14, 10, 5, .65));
    border-bottom: 1px solid #3a2f1c;
    /* review F2: the era popup lives in THIS stacking context — it must
       outrank the hand dock (z60) and the mobile overlay dock (z85) */
    z-index: 120;
    flex: 0 0 auto;
}

.shift-badge {
    padding: 5px 16px;
    border-radius: 20px;
    font-family: 'Cinzel', serif;
    font-size: 15px;
}

.shift-badge.day {
    background: rgba(212, 169, 75, .18);
    color: var(--gold-bright);
    border: 1px solid var(--gold-dim);
}

.shift-badge.night {
    background: rgba(90, 110, 200, .18);
    color: #aab8ff;
    border: 1px solid #4a5691;
}

.players-strip {
    display: flex;
    gap: 8px;
    margin-left: auto;
    flex-wrap: wrap;
}

.player-chip {
    background: var(--panel);
    border: 1px solid #33291a;
    border-radius: 10px;
    padding: 4px 10px;
    font-size: 13.5px;
    display: flex;
    gap: 8px;
    align-items: center;
}

/* v0.6.6.0 item 21: no ✓ — a finished player's chip GLOWS green in place */
.player-chip.done {
    border-color: #74b06a;
    box-shadow: 0 0 8px rgba(116, 224, 106, .35);
}

.player-chip.active {
    border-color: var(--gold);
    box-shadow: 0 0 10px rgba(212, 169, 75, .35);
}

.player-chip .crown {
    font-size: 15px;
}

.player-chip .fac-ico {
    font-size: 14px;
}

.player-chip .gold-val {
    cursor: help;
}

.player-chip .prod {
    color: #a5f08c;
    font-size: 10.5px;
    font-style: normal;
    margin-left: 1px;
}

/* era badge (req 12): always visible top right */
.era-badge {
    position: relative;
    width: 54px;
    height: 54px;
    cursor: pointer;
    flex: 0 0 auto;
    filter: drop-shadow(0 0 6px rgba(212, 169, 75, .55));
    animation: era-glow 3s ease-in-out infinite;
}

.era-badge img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.era-badge span {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Cinzel Decorative', 'Cinzel', serif;
    font-weight: 700;
    font-size: 20px;
    color: #fff;
    text-shadow: 0 0 8px #000, 0 0 3px #000;
}

@keyframes era-glow {
    50% {
        filter: drop-shadow(0 0 14px rgba(212, 169, 75, .85));
    }
}

.era-badge.pulsing {
    animation: era-pulse .9s ease-in-out infinite;
}

@keyframes era-pulse {
    50% {
        transform: scale(1.18);
        filter: drop-shadow(0 0 26px rgba(255, 217, 138, 1));
    }
}

/* item 15: the army preview under a hovered player chip */
.player-chip {
    position: relative;
    cursor: pointer;
}

.army-pop {
    /* fixed + centered: never clips, whatever chip spawned it */
    position: fixed;
    top: 52px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 90;
    --card-w: 96px;
    --card-h: calc(96px * 1.4);
    padding: 8px 10px;
    width: max-content;
    max-width: min(680px, 96vw);
    max-height: calc(100vh - 70px);
    max-height: calc(100dvh - 70px);
    overflow-y: auto;
    scrollbar-width: thin;
    cursor: default;
}

/* item 1 (v0.6.5.9): the rival's economy rides the army popup */
/* v0.6.7.4 item 17: the rival's activated bolsters under the economy line */
.army-pop .army-bolsters {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    justify-content: center;
    margin: 2px 0 6px;
    font-size: 12.5px;
    color: #cdbf9d;
}

.army-pop .army-bolsters span {
    background: rgba(212, 169, 75, .12);
    border: 1px solid rgba(212, 169, 75, .3);
    border-radius: 8px;
    padding: 1px 8px;
}

.army-pop .army-economy {
    font-size: 13.5px;
    color: var(--gold-bright);
    margin: 2px 0 8px;
    white-space: nowrap;
}

.army-pop .army-row {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

/* item 14: era conditions popover under the era badge */
.era-pop {
    position: absolute;
    top: 64px;
    right: 12px;
    /* v0.6.5.7 item 10: above the hand, always */
    z-index: 400;
    width: 320px;
    max-height: calc(100vh - 90px);
    max-height: calc(100dvh - 90px);
    /* v0.6.5.6 item 6: the buttons pin — only the TEXT scrolls */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 10px 14px;
}

.era-pop-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: thin;
}

.era-pop-actions {
    flex: 0 0 auto;
}

.era-cond-block {
    margin: 8px 0;
}

.era-cond-block b {
    font-family: 'Cinzel', serif;
    color: var(--gold-bright);
    font-size: 13.5px;
}

.era-cond-row {
    font-size: 13px;
    margin: 2px 0;
}

.era-pop-actions {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    margin-top: 10px;
}


/* ==========================================================================
   v0.6.5.5 — the top row never wraps, and it travels light on small screens
   ========================================================================== */

/* item 1: the brand stands in two lines everywhere */
.brand.stacked {
    line-height: 1.05;
    white-space: nowrap;
}

.brand.stacked span {
    display: block;
    font-size: .78em;
    letter-spacing: 4px;
}

/* item 5: one line, always — the strip absorbs the squeeze */
.topbar {
    flex-wrap: nowrap;
    min-width: 0;
}

.players-strip {
    /* review F2: nowrap is the whole point — white-space alone never
       stopped flex line-breaking */
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
    white-space: nowrap;
    min-width: 0;
    cursor: grab;
    touch-action: pan-x;
}

.players-strip::-webkit-scrollbar {
    display: none;
}

.players-strip .player-chip {
    flex: 0 0 auto;
}

/* items 2 & 4: on small screens the shift chip keeps only its icon */
@media (max-height: 500px), (max-width: 980px) and (max-height: 760px), (max-width: 700px) and (orientation: portrait) {
    .shift-badge .shift-label {
        display: none;
    }

    .topbar {
        gap: 8px;
        padding: 4px 8px;
    }
}

/* item 4 (review F5): the names drop wherever the row gets tight — a WIDTH
   band, so it fires on narrow desktops and big foldables where the strip is
   actually visible (the phone media hides the whole strip) */
@media (max-width: 1240px) {
    .players-strip .p-name {
        display: none;
    }
}

/* v0.6.5.6 item 5: the small-screen corner cluster — four identical squares,
   right to left: ERA, players, chronicle, day/night */
@media (max-height: 500px), (max-width: 980px) and (max-height: 760px), (max-width: 700px) and (orientation: portrait) {
    .topbar .era-badge,
    .topbar .shift-badge {
        position: fixed;
        top: 4px;
        z-index: 96;
        width: 32px;
        height: 32px;
        margin: 0;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 8px;
        border: 1px solid var(--gold-dim);
        background: rgba(18, 13, 7, .92);
        font-size: 15px;
    }

    .topbar .era-badge {
        right: 6px;
    }

    .topbar .era-badge img {
        width: 24px;
        height: 24px;
    }

    .topbar .shift-badge {
        right: 120px;
    }
}
/* ==========================================================================
   Layout shell: stage, left rail, logs, chronicle
   ========================================================================== */

/* ------------------------------------------------------------- layout */
.main-grid {
    flex: 1 1 auto;
    display: grid;

    /* v0.6.5.5 item 11: the rail column follows its content — folding the
       Chronicle hands its width to the stage.
       v0.6.5.8 item 1: the grid runs FULL BLEED so the Chronicle hugs the
       SCREEN's left edge; the stage re-centers itself in its own track. */
    grid-template-columns: auto 1fr;
    gap: 12px;
    padding: 12px 16px 4px 0;
    min-height: 0;
    width: 100%;
}

/* the stage keeps the centered 1500px stance on wide screens */
#center-stage {
    width: 100%;
    max-width: 1500px;
    margin-inline: auto;
}

#left-rail {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
    width: 250px;
    position: relative;
}

/* v0.6.5.5 item 11: one pinned toggle — the rail folds to a strip */
/* v0.6.5.8 item 1: the pull-tab is PART of the panel — glued to its right
   edge, it folds and unfolds as one piece with the Chronicle */
#left-rail .rail-fold-btn {
    position: absolute;
    left: 100%;
    top: 64px;
    z-index: 60;
    width: 20px;
    height: 72px;
    border-radius: 0 10px 10px 0;
    border: 1px solid var(--gold-dim);
    border-left: none;
    background: rgba(18, 13, 7, .9);
    color: var(--gold-bright);
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
}

/* v0.6.5.6 item 8: the folded rail vanishes — its tab stays at the screen
   edge (still one piece), and the stage takes everything */
#left-rail.folded {
    width: 0;
    overflow: visible;
}

.main-grid:has(#left-rail.folded) {
    column-gap: 0;
}

#left-rail.folded .rail-body {
    display: none;
}

#left-rail .rail-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
    flex: 1 1 auto;
}

.rail-box {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    /* item 1 (v0.6.5.8): docked flush to the screen edge — square left */
    border-left: none;
    border-radius: 0 12px 12px 0;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.rail-box.grow {
    flex: 1 1 auto;
    min-height: 0;
}

.log-scroll {
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
    font-size: 13.5px;
    line-height: 1.5;
    color: #cdbf9d;
    scrollbar-width: thin;
}

.log-scroll p {
    margin-bottom: 2px;
}

.log-scroll .lg-round {
    color: var(--gold);
    font-family: 'Cinzel', serif;
    margin-top: 6px;
}

.chron-entry {
    border: 1px solid #33291a;
    border-radius: 8px;
    padding: 6px 8px;
    margin-bottom: 6px;
    cursor: pointer;
}

.chron-entry:hover {
    border-color: var(--gold-dim);
}

.chron-entry b {
    color: var(--gold-bright);
    font-size: 13px;
}

/* v0.6.6.1 item 2: the round pips and End-round button left the rail —
   the round counter and End casting ride the hand dock's tab row now
   (.round-dock in 45-hand.css), one spot on every layout */

#center-stage {
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

#stage-board {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    min-height: 0;
}

#stage-board .panel-title {
    margin: 0;
}

#stage-right {
    display: flex;
    min-height: 0;
}

.rpane {
    display: none;
    flex: 1 1 auto;
    min-height: 0;
    padding: 14px 16px;
    /* v0.6.5.4 item 1: the PANEL never scrolls — its ornate ring would ride
       away with the content; the inner .rpane-scroll does the scrolling */
    overflow: hidden;
    flex-direction: column;
    gap: 8px;
}

.rpane-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: thin;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.rpane.on {
    display: flex;
}

#end-day-btn {
    width: 100%;
}

#board-actions {
    min-height: 34px;
    margin-top: 6px;
}

/* v0.4 stage refinements (late in the cascade on purpose) */
.rpane.on {
    justify-content: flex-start;
}

#board-actions {
    min-height: 30px;
}


/* v0.6.5.1 item 19: nothing on the game screen is selectable — a drag must
   never turn into a text selection (the setup scene keeps its inputs) */
#scene-game {
    user-select: none;
    -webkit-user-select: none;
}

/* v0.6.6.2 item 14: the chronicle's day entries — quieter than the battles */
.chron-day {
    font-size: 12px;
    color: #b9ab8d;
    margin: 1px 0;
}

.chron-day b {
    color: var(--gold-bright);
    font-weight: 600;
}

/* v0.6.6.2 item 21: the realm wears YOUR faction's legend — a blurred,
   quiet backdrop under every panel, keyed off #scene-game[data-faction] */
#scene-game {
    position: relative;
    isolation: isolate;
}

#scene-game::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--faction-bg, none) center 28% / cover no-repeat;
    opacity: .14;
    filter: blur(6px) saturate(.8);
    transform: scale(1.08);
    pointer-events: none;
}

/* v0.6.6.3 item 5: the realm's backdrop is the faction's painted box cover */
#scene-game[data-faction="holy_kingdom"] { --faction-bg: url("/assets/covers/hk.jpg"); }
#scene-game[data-faction="desolate_throne"] { --faction-bg: url("/assets/covers/dt.jpg"); }
#scene-game[data-faction="verdant_alliance"] { --faction-bg: url("/assets/covers/va.jpg"); }
#scene-game[data-faction="burning_covenant"] { --faction-bg: url("/assets/covers/bc.jpg"); }

/* ==========================================================================
   v0.6.7.0 item 4 — spectator mode: the table is glass
   ========================================================================== */

#scene-game[data-spectator] #day-panel,
#scene-game[data-spectator] #hand-dock,
#scene-game[data-spectator] #stage-grid,
#scene-game[data-spectator] #stage-right {
    pointer-events: none;
}

/* reading is allowed — the chronicle rail keeps its scroll */
#scene-game[data-spectator] #left-rail { pointer-events: auto; }

.spectator-chip {
    position: fixed;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 300;
    padding: 4px 14px;
    border: 1px solid rgba(226, 190, 118, .5);
    border-radius: 999px;
    background: rgba(20, 14, 8, .85);
    color: #e2be76;
    font-size: 13px;
    letter-spacing: .04em;
    pointer-events: none;
}

/* v0.6.7.0 item 13 — the sound toggle keeps its chair in the top row */
.sound-toggle { padding: 2px 8px; font-size: 14px; line-height: 1.4; }

/* v0.6.7.0 items 15/16/20 — animation dial, gem shapes, card inspector */
.anim-popover {
    position: fixed;
    top: 52px;
    right: 12px;
    z-index: 320;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    background: rgba(24, 17, 10, .96);
}

.anim-speeds { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }

/* reduced motion stills the ambient drift, the sparkler and the pulses */
:root[data-reduced-motion] .setup-epic i,
:root[data-reduced-motion] .setup-logo,
:root[data-reduced-motion] .evo-crown,
:root[data-reduced-motion] .death-fx .death-line,
:root[data-reduced-motion] .round-dock .btn {
    animation: none !important;
}

:root[data-reduced-motion] .death-fx .death-line {
    opacity: 0;
}

/* under reduced motion the wipe stands finished: full veil, skull shown */
:root[data-reduced-motion] .death-fx .death-veil {
    animation: none !important;
    clip-path: inset(0 0 0 0);
}

:root[data-reduced-motion] .death-fx .death-skull {
    opacity: 1;
    animation: none !important;
}


.inspector-veil {
    position: fixed;
    inset: 0;
    z-index: 400;
    background: rgba(8, 5, 2, .78);
    display: flex;
    align-items: center;
    justify-content: center;
}

.inspector-body {
    display: flex;
    gap: 18px;
    align-items: center;
    max-width: min(92vw, 640px);
}

.inspector-card .card {
    width: min(46vw, 260px) !important;
    height: auto !important;
    aspect-ratio: 5 / 7;
}

.inspector-lore {
    display: flex;
    flex-direction: column;
    gap: 8px;
    color: var(--ink, #e8dcc4);
    max-width: 280px;
}

.inspector-lore .insp-text { white-space: pre-line; font-size: 13px; }

.inspector-lore .insp-flavor { font-style: italic; font-size: 12px; opacity: .75; }

/* v0.6.7.0 item 14 — the guided first game */
.tutorial-banner {
    position: fixed;
    left: 50%;
    bottom: 148px;
    transform: translateX(-50%);
    z-index: 290;
    display: flex;
    align-items: center;
    gap: 12px;
    max-width: min(92vw, 620px);
    padding: 10px 14px;
    border: 1px solid rgba(226, 190, 118, .55);
    border-radius: 10px;
    background: rgba(24, 17, 10, .94);
    color: #e8dcc4;
    font-size: 13px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, .5);
}

.tutorial-banner .tut-skip { flex-shrink: 0; font-size: 11px; padding: 4px 8px; }

/* v0.6.8.2 item 10: the guide closes the way cards do — a round corner
   button, not a text bar that claimed a third of a phone's banner. It rides
   the banner's top-right CORNER (hence the negative offsets and the
   padding-right that keeps text clear of it). */
.tutorial-banner {
    padding-right: 34px;
}

.tut-close {
    position: absolute;
    top: -12px;
    right: -12px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 1px solid var(--gold-dim);
    background: rgba(24, 17, 10, .97);
    color: var(--parchment);
    font-size: 15px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 2;
    transition: box-shadow .15s ease, transform .15s ease;
}

.tut-close:hover {
    box-shadow: 0 0 12px rgba(212, 169, 75, .75);
    transform: scale(1.12);
}

/* the second tap confirms — the button says so, and glows to be noticed */
.tut-close.asking {
    border-color: #d8a03a;
    color: #f4d68a;
    box-shadow: 0 0 12px rgba(212, 169, 75, .8);
}

.tutorial-glow {
    box-shadow: 0 0 0 2px rgba(226, 190, 118, .55), 0 0 22px rgba(226, 190, 118, .25) !important;
}

/* v0.6.7.0 item 19 — the chronicle export sits inside the title, immobile */
.chron-export { float: right; padding: 0 6px; font-size: 12px; line-height: 1.5; }

/* ---------------------------------------------------------------- v0.6.8.4
   item 2: the settings popover's escape hatch and the owner's line */
.anim-popover .gear-leave {
    margin-top: 4px;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, .12);
    display: flex;
}

/* review L11: arming the confirm swaps in a much longer label ("⚠ Leave —
   are you sure?"). In a width-less fixed popover that widened the whole
   panel and reshuffled the pace buttons above it. A fixed width means the
   label changes in place — which is exactly what the law asks for. */
.anim-popover .gear-leave .btn {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.anim-popover {
    min-width: 190px;
}

.anim-popover .gear-legal {
    margin-top: 6px;
    font-size: 10px;
    text-align: center;
    color: var(--parchment);
    opacity: .4;
}
/* ==========================================================================
   Day panel, locations, temple rites
   ========================================================================== */

.location-menu {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.loc-card {
    background: #17120b;
    border: 2px solid #2e2517;
    border-radius: 10px;
    padding: 9px 10px;
    color: var(--parchment);
    cursor: pointer;
    text-align: left;
    transition: all .15s;
    display: grid;
    grid-template-columns: 30px 1fr;
    column-gap: 8px;
    font-family: inherit;
}

.loc-card span {
    grid-row: span 2;
    font-size: 22px;
    align-self: center;
}

.loc-card b {
    font-family: 'Cinzel', serif;
    font-size: 14px;
}

.loc-card i {
    color: #a89a7c;
    font-size: 12px;
}

.loc-card:hover:not(:disabled) {
    border-color: var(--gold-dim);
}

.loc-card.selected {
    border-color: var(--gold);
    box-shadow: 0 0 12px rgba(212, 169, 75, .35);
}

.loc-card:disabled {
    opacity: .4;
    cursor: default;
}


.loc-card.chosen:not(.selected) {
    border-color: var(--gold-dim);
}

/* the exhausted gold mine (v0.3.1 item 2) */
.loc-card.capped i {
    color: #d4a94b;
    font-style: normal;
}

.loc-card.capped span {
    filter: grayscale(.6);
}


/* issue 9: temple rites in two grid sections — revives first, then heals */
.temple-section {
    margin: 6px 0 2px;
}

.temple-section .rail-title {
    margin: 0 0 4px;
}

.temple-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 5px;
}

.temple-grid .btn {
    width: 100%;
}

.temple-pair {
    display: flex;
    gap: 4px;
}

.temple-pair .btn:first-child {
    flex: 1 1 auto;
}

/* v0.6.6.1 item 9: every rite button stands the SAME height — an auto grid
   resolves its fr rows to the tallest, so long names wrap inside a shared
   row instead of stretching their own button */
.temple-grid {
    grid-auto-rows: 1fr;
    align-items: stretch;
}

.temple-grid > .btn,
.temple-grid .temple-pair,
.temple-pair .btn {
    height: 100%;
}

.temple-grid .btn {
    min-height: 40px;
}

/* issue 3: the all-rites temple buttons stand apart from the single rites */
.temple-grid .btn.rite-all {
    grid-column: 1 / -1;
    font-family: 'Cinzel', serif;
    letter-spacing: .5px;
    border-color: var(--gold-bright, #f0c96b);
    background: linear-gradient(180deg, rgba(212, 169, 75, .32), rgba(212, 169, 75, .12));
    box-shadow: 0 0 8px rgba(212, 169, 75, .25);
}

/* item 39: picks-left counter rides the right edge of the "Your Day" row */
.day-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.day-actions-left {
    flex: 0 0 auto;
    /* a changing digit must not nudge its neighbors (item 3) */
    font-variant-numeric: tabular-nums;
    width: 104px;
    min-width: 26px;
    height: 26px;
    padding: 0 10px;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(212, 169, 75, .18);
    border: 1px solid var(--gold-dim);
    border-radius: 13px;
    font-size: 15px;
    color: var(--gold-bright);
}

/* item 10: the End Day button is pinned to the panel's bottom edge — the
   content above it grows and shrinks, the button never moves */
#panel-day {
    display: flex;
    flex-direction: column;
}

/* v0.6.5.8 items 2 & 6: the centering experiment rolls back — End Day flows
   BESIDE the "Your Day" title on the left, never wrapping, never overlapping
   the Hide/Actions controls, never sliding out from under the pointer */
.day-title-row .day-end-btn {
    flex: 0 0 auto;
    white-space: nowrap;
    /* review m1: one FIXED box for both labels — arming must not grow the
       button and nudge its neighbours */
    min-width: 168px;
    height: 36px;
    line-height: 22px;
    padding: 6px 12px;
    font-size: 15px;
}

.day-title-name {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* item 13: every action spent — the day is done, the button glows */
@keyframes end-day-call {

    0%,
    100% {
        box-shadow: 0 0 4px rgba(110, 220, 130, .35);
    }

    50% {
        box-shadow: 0 0 16px rgba(110, 220, 130, .8);
    }
}

.day-end-btn.all-spent:not(:disabled) {
    border-color: #6edc82;
    animation: end-day-call 1.8s ease-in-out infinite;
}

/* item 20 (v0.6.5.8): the armed question wears the warning color — same
   button, same spot, only the words and the border change */
.day-end-btn.armed {
    border-color: var(--blood);
    box-shadow: 0 0 10px rgba(201, 79, 66, .5);
}

/* v0.6.5.4 item 3: Hide and Reopen share ONE fixed slot in the day header */
.day-title-controls {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    flex: 1 1 0;
}

.shop-toggle-btn {
    width: 108px;
    padding-left: 4px;
    padding-right: 4px;
    white-space: nowrap;
    text-align: center;
}

#panel-day .bolster-panel {
    /* v0.6.5.5 review F1: the hall SHRINKS under pressure (0 1 auto) so its
       list hits the 58px floor and scrolls — flex-shrink 0 had locked the
       list at full height and the phone clipped six unreachable bolsters */
    flex: 0 1 auto;
    min-height: 96px;
}

#panel-day .day-end-btn {
    flex: 0 0 auto;
}

/* ==========================================================================
   v0.6.5.0 — the redesigned day: checkmarks, the wide lair, the Bolsters
   ========================================================================== */

/* item 5 (v0.6.5.1 item 13): the visited checkbox is BIG and unmissable */
.loc-card {
    position: relative;
}

.loc-card .loc-check {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: 10px;
    grid-row: auto;
    font-size: 22px;
    line-height: 1;
    filter: grayscale(1) opacity(.35);
    align-self: start;
}

.loc-card.chosen .loc-check {
    filter: none;
    text-shadow: 0 0 8px rgba(120, 220, 120, .5);
}

/* v0.6.5.1 item 14: every location explains itself on hover — since
   v0.6.8.1 item 1.6 the explanation rides the body-level action-tip layer
   (data-tip is its content source), so it can never be clipped by the
   panel's overflow or hide under the top bar.
   BUT a DISABLED <button> fires no pointer events (the JS layer never
   sees it), while CSS :hover still matches — so a locked location keeps
   a pure-CSS tooltip to explain WHY it is barred. It lives inside the day
   panel, far from the top bar, so clipping is not a concern here. */
.loc-card:disabled[data-tip]:hover::after {
    content: attr(data-tip);
    position: absolute;
    left: 8px;
    right: 8px;
    top: calc(100% + 4px);
    z-index: 45;
    background: rgba(18, 13, 7, .98);
    border: 1px solid var(--gold-dim);
    border-radius: 8px;
    padding: 7px 10px;
    font-size: 12px;
    font-style: normal;
    color: #9a8d74;
    pointer-events: none;
    white-space: normal;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .6);
}

/* the disabled shop price keeps its caption too (same reason) — a
   DISABLED button fires no pointer events, so the CSS :hover cap stands in */
.price-btn:disabled:hover .cap {
    display: block;
    position: absolute;
    bottom: calc(100% + 5px);
    left: 50%;
    transform: translateX(-50%);
    background: rgba(14, 10, 6, .97);
    border: 1px solid #4a3c22;
    border-radius: 6px;
    padding: 3px 8px;
    font-size: 11px;
    font-style: normal;
    white-space: nowrap;
    color: var(--parchment);
    pointer-events: none;
    z-index: 12;
}

/* v0.6.5.1 item 15: the mine's dig is precious — it pulses while possible */
@keyframes mine-call {

    0%,
    100% {
        box-shadow: 0 0 0 rgba(212, 169, 75, 0);
        border-color: #5a4a26;
    }

    50% {
        box-shadow: 0 0 14px rgba(212, 169, 75, .55);
        border-color: var(--gold);
    }
}

.loc-card.mine-pulse {
    animation: mine-call 1.6s ease-in-out infinite;
}

/* item 10: yesterday's grounds rest today */
.loc-card.resting {
    filter: saturate(.4);
}

/* item 8.8: THE BOSS LAIR spans both columns */
.loc-card.loc-wide {
    grid-column: 1 / -1;
    background: linear-gradient(160deg, rgba(80, 24, 18, .5), rgba(30, 12, 10, .75));
    border-color: #5a2c22;
}

.loc-card.loc-wide:not(:disabled):hover {
    border-color: #c96a4b;
    box-shadow: 0 0 12px rgba(201, 106, 75, .35);
}

.loc-card.loc-wide b {
    letter-spacing: 1.5px;
}

/* items 1.7/1.8: Bolster the Kingdom — the bottom half of Your Day, visibly
   split from the actions above, with room for entries yet to come */
.bolster-panel {
    margin-top: 10px;
    padding-top: 8px;
    border-top: 2px solid #3a2e1a;
    flex: 0 0 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* the deeds pop opens UPWARD past the hall's top edge — the clip must yield
   while the badge is hovered or tapped open, or the pop is invisible */
/* v0.6.6.3 item 3: the Rights hover no longer flips these containers to
   overflow:visible — that dropped the scrollbar and shifted the whole panel.
   The deeds ride the FIXED tooltip layer and need no escape; only the
   floating confirm (absolute, above the panel) still does. */
.bolster-panel:has(.bolster-confirm.floating),
.rpane:has(.bolster-confirm.floating),
.day-h-scroll:has(.bolster-confirm.floating) {
    overflow: visible;
}

.bolster-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin: 0 0 2px;
}

.bolster-rights {
    min-width: 26px;
    height: 22px;
    padding: 0 9px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(212, 169, 75, .18);
    border: 1px solid var(--gold-dim);
    border-radius: 11px;
    font-size: 13px;
    color: var(--gold-bright);
}

.bolster-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    /* v0.6.6.0 item 2: the SAME grid as the locations — one gap, and no
       private horizontal overflow (the day column's scroller is the only one) */
    gap: 8px;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    min-height: 0;
    align-content: start;
}

.bolster-row {
    display: flex;
    align-items: center;
    gap: 8px;
    background: #17120b;
    border: 2px solid #2e2517;
    border-radius: 8px;
    /* v0.6.5.1 item 3: bigger rows — real drop targets for a tucked card */
    padding: 10px 10px;
    min-height: 52px;
}

.bolster-row.tuckable {
    border-style: dashed;
    border-color: #57431f;
}

.bolster-row.drag-over {
    border-color: var(--gold);
    box-shadow: 0 0 12px rgba(212, 169, 75, .5);
    background: rgba(212, 169, 75, .08);
}

/* v0.6.5.2 item 3: the deeds live in a rich tooltip on the Rights badge */
.bolster-rights {
    position: relative;
    /* hover opens the deeds on desktop; a tap toggles them on touch */
    cursor: pointer;
}

/* v0.6.6.0 item 8: the deeds ride the FIXED tooltip layer now — nothing
   absolute in the panel, nothing to shift or jump with a scroll */
.milestone-list {
    display: flex;
    flex-direction: column;
    gap: 5px;
    max-width: 340px;
    text-align: left;
    text-transform: none;
    letter-spacing: 0;
}

.milestone-list > b {
    color: var(--gold-bright);
    font-size: 12.5px;
    margin-bottom: 3px;
}

.milestone {
    font-size: 13px;
    color: #7d6f55;
}

.milestone.done {
    color: var(--gold-bright);
}

/* v0.6.5.1 item 3: the tuck is forever — the confirmation stands guard */
.bolster-confirm {
    background: rgba(80, 24, 18, .25);
    border: 1px solid #6b3b2a;
    border-radius: 8px;
    padding: 8px 10px;
}

.bolster-row.done {
    border-color: var(--gold-dim);
    background: linear-gradient(160deg, rgba(60, 48, 20, .5), rgba(30, 24, 12, .7));
}

.bolster-row.armed {
    border-color: var(--gold);
    box-shadow: 0 0 10px rgba(212, 169, 75, .35);
}

.bolster-icon {
    font-size: 18px;
    flex: 0 0 auto;
}

.bolster-text {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

.bolster-text b {
    font-family: 'Cinzel', serif;
    font-size: 12.5px;
}

.bolster-text i {
    color: #a89a7c;
    font-size: 11px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* v0.6.6.0 item 3: the two rewards tell apart — the steady grant in
   parchment, the one-time burst in green */
.bolster-rewards {
    display: block;
    min-width: 0;
}

.bolster-rewards i {
    display: inline;
}

.bolster-rewards .bolster-boost::before {
    content: " · ";
    color: #7d6f55;
    font-style: normal;
}

/* v0.6.6.2 item 13: the colors SWAP — the permanent grant is the green
   one (it lasts), the one-time burst wears the parchment */
.bolster-text .bolster-perm {
    color: #8fd489;
}

.bolster-text .bolster-boost {
    color: #b8a67e;
}

.bolster-text .bolster-tucked {
    color: var(--gold-dim);
}

.bolster-btn {
    flex: 0 0 auto;
}

.bolster-hint {
    margin: 4px 0 0;
    color: var(--gold-bright);
}



/* ---- the inline shop (v0.6.5.1 items 4 & 8) ------------------------------
   A chosen location renders in the day panel's top section, replacing the
   location grid until Hide. The five-card row sizes itself off the panel
   width; buttons sit in an always-rendered equal-size grid on the left,
   piles with quantities on the right. */
.inline-shop {
    container-type: inline-size;
    display: flex;
    flex-direction: column;
    gap: 8px;
    background: rgba(14, 10, 6, .55);
    border: 1px solid #3a2f1c;
    border-radius: 10px;
    padding: 10px;
}

.ishop-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.ishop-head>b {
    font-family: var(--display-font, serif);
    color: var(--gold-bright);
    font-size: 15px;
}

/* item 4: FIVE cards in a row, always — the cards shrink to fit the panel */
.ishop-shelf {
    --card-w: min(150px, calc((100cqw - 52px) / 5));
    --card-h: calc(var(--card-w) * 1.4);
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 8px;
    justify-items: center;
}

/* the Marketplace hand mirror: 5 visible, the rest scroll sideways */
.ishop-shelf.mirror {
    display: flex;
    overflow-x: auto;
    scrollbar-width: thin;
    padding-bottom: 4px;
}

.ishop-shelf.mirror .shop-item {
    flex: 0 0 var(--card-w);
}

.ishop-lower {
    display: flex;
    gap: 10px;
    align-items: stretch;
}

/* buttons ALWAYS in a grid, same size, max space — left half, two columns */
.ishop-btn-grid {
    flex: 1 1 55%;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    align-content: start;
}

.ishop-btn-grid .btn {
    width: 100%;
    height: 100%;
    min-height: 42px;
    white-space: normal;
    line-height: 1.25;
}

.ishop-btn-grid.two {
    flex: none;
}

.ishop-piles {
    flex: 1 1 45%;
    display: flex;
    gap: 10px;
    justify-content: flex-end;
}

.ishop-aside {
    flex: 1 1 45%;
    align-self: center;
    text-align: right;
}

/* tokens left, the six-slot unit grid right — same pattern as the board */
.ishop-split {
    display: flex;
    gap: 10px;
    align-items: stretch;
}

.ishop-tokens {
    flex: 0 0 42%;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    align-content: start;
}

.ishop-tokens .hint {
    grid-column: 1 / -1;
}

.ishop-token {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 8px 6px;
    background: #17120b;
    border: 2px dashed #57431f;
    border-radius: 8px;
    color: var(--parchment);
    font-size: 13px;
    cursor: grab;
    touch-action: none;
}

.ishop-token i {
    font-size: 11px;
    color: #9c8b68;
    font-style: normal;
    text-align: center;
}

.ishop-token.primary {
    border-color: var(--gold);
    border-style: solid;
    box-shadow: 0 0 10px rgba(212, 169, 75, .5);
}

.ishop-token:disabled {
    opacity: .45;
    cursor: default;
}

.ishop-token.big {
    grid-column: 1 / -1;
    padding: 14px 8px;
}

.ishop-token.big .tok-face {
    font-size: 34px;
    line-height: 1;
}

.ishop-units {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-auto-rows: 1fr;
    gap: 6px;
}

.ishop-unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    min-height: 46px;
    white-space: normal;
}

.ishop-unit b {
    font-size: 12.5px;
}

.ishop-unit i {
    font-size: 10.5px;
    color: #9c8b68;
    font-style: normal;
}

.ishop-temple {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 300px;
    overflow-y: auto;
    scrollbar-width: thin;
}

/* ---- card shops v2 (v0.6.5.2 item 2) ------------------------------------ */
/* the card IS the control: it lifts like the hand and arms on click */
.ishop-shelf .shop-item {
    position: relative;
}

.ishop-shelf .shop-item .card {
    cursor: pointer;
    transition: transform .12s ease, box-shadow .12s ease;
}

.ishop-shelf .shop-item:hover .card {
    transform: translateY(-10px) scale(1.04);
    z-index: 6;
    box-shadow: 0 10px 26px rgba(0, 0, 0, .8);
}

.ishop-shelf .shop-item.armed .card {
    outline: 2px solid var(--gold);
    box-shadow: 0 0 16px rgba(212, 169, 75, .65);
}

/* two-line grid buttons (item 2.6) and the full-width Refresh Shelf (2.5) */
.ishop-btn-grid .btn .sub {
    display: block;
    font-size: 11px;
    font-style: normal;
    color: #b8a67e;
    margin-top: 1px;
}

.ishop-btn-grid .btn.wide {
    grid-column: 1 / -1;
    min-height: 50px;
    font-size: 15px;
    letter-spacing: .5px;
}

/* compact piles with the name over the stack (items 2.4, 2.7) */
.ishop-piles {
    --card-w: 92px;
    --card-h: calc(92px * 1.4);
    align-items: flex-start;
}

.ishop-piles .pile>i {
    position: absolute;
    left: 3px;
    right: 3px;
    bottom: 3px;
    z-index: 3;
    background: rgba(10, 7, 3, .62);
    border-radius: 4px;
    padding: 1px 0;
    font-size: 11px;
}

/* v0.6.5.2 item 5: the armed undig — the mine button asks in place */
.loc-card.undig-armed {
    border-color: var(--blood);
    box-shadow: 0 0 10px rgba(201, 79, 66, .5);
}

.loc-card.undig-armed i {
    color: #e8a292;
}

/* v0.6.8.1 item 1.5: the cost rides the BOTTOM-MIDDLE of the card — the
   one spot with no permanent design element — as a slightly bigger circle
   of the same corner-button family; the same spot arms the ⚠ confirmation */
.price-btn {
    position: absolute;
    bottom: -6px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 8;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    border: 1px solid #6b5a2a;
    background: rgba(40, 32, 10, .94);
    color: var(--gold-bright);
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* item 1.2: the shop circles glow like every other button */
.price-btn:hover:not(:disabled) {
    box-shadow: 0 0 12px rgba(212, 169, 75, .75);
    transform: translateX(-50%) scale(1.1);
}

.price-btn.armed {
    background: rgba(120, 80, 10, .96);
    border-color: var(--gold);
    box-shadow: 0 0 12px rgba(212, 169, 75, .8);
    font-size: 18px;
}

.price-btn.channel {
    background: rgba(20, 12, 40, .94);
    border-color: #4a3f6b;
    color: #cdd8f0;
    font-size: 15px;
}

.price-btn.channel.armed {
    background: rgba(50, 30, 100, .95);
    border-color: #8ab4dd;
    box-shadow: 0 0 12px rgba(138, 180, 221, .9);
}

.price-btn:disabled {
    opacity: .5;
    cursor: default;
}

.price-btn .cap {
    display: none;
    position: absolute;
    bottom: calc(100% + 5px);
    right: -4px;
    background: rgba(14, 10, 6, .96);
    border: 1px solid #4a3c22;
    border-radius: 6px;
    padding: 3px 8px;
    font-size: 11px;
    font-style: normal;
    white-space: nowrap;
    color: var(--parchment);
    pointer-events: none;
}

/* v0.6.5.4 item 9: the tavern's hire price shows bottom-middle on hover */
.ishop-shelf.hire .card {
    cursor: grab;
    touch-action: none;
}

.ishop-shelf.hire .card .badge-cost {
    opacity: 0;
    transition: opacity .15s;
}

.ishop-shelf.hire .card:hover .badge-cost {
    opacity: 1;
}

/* v0.6.8.1 item 1.6: captions render via the body-level action-tip layer
   now — no overflow container can clip them, so the mirror flip retired */
.price-btn .cap {
    display: none;
}

/* ==========================================================================
   v0.6.5.5 items 9 & 10 — the short-screen day splits fair
   ========================================================================== */

/* the actions region never shrinks below one row of location buttons */
#panel-day .rpane-scroll {
    min-height: 64px;
}

/* the hall keeps its bottom seat but its LIST does the scrolling */
.bolster-panel .bolster-list {
    overflow-y: auto;
    scrollbar-width: thin;
    min-height: 58px;
}

/* End Day Turn stands between them, always visible */
#panel-day .day-end-btn {
    flex: 0 0 auto;
}

/* v0.6.5.6 item 7.2: the day panel compresses, then SCROLLS sideways —
   and the grid column must let it shrink (the >660px-tall squeeze bug) */
#stage-right {
    min-width: 0;
}

/* item 7 (v0.6.5.7): the day column scrolls sideways as ONE */
.day-h-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-x: auto;
    overflow-y: hidden;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* review F1: the CHILDREN hold the 430px floor so the wrapper overflows and
   actually scrolls — without it they absorbed the squeeze themselves and
   the hall clipped its right column unreachable */
#panel-day .rpane-scroll,
#panel-day .bolster-panel {
    min-width: 430px;
}

#panel-day .location-menu,
#panel-day .inline-shop {
    min-width: 430px;
}


.bolster-list {
    min-width: 430px;
}

/* item 7.1: the row is the button */
button.bolster-row {
    font-family: inherit;
    text-align: left;
    color: var(--parchment);
    cursor: pointer;
}

button.bolster-row:disabled {
    cursor: default;
}

button.bolster-row.armed {
    border-color: var(--gold);
    box-shadow: 0 0 10px rgba(212, 169, 75, .5);
}

.bolster-card-ico {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--gold-dim);
    margin-left: auto;
    flex: 0 0 auto;
}

/* item 7 (v0.6.5.8): before the tuck the medallion is an EMPTY ring — the
   same circle the artwork will fill, like the locations' unchecked box */
span.bolster-card-ico.empty {
    display: block;
    border-style: dashed;
    background: rgba(0, 0, 0, .3);
}

/* item 8: the Fate line rides the bolstered card's tooltip */
.bolster-fate-line {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px solid rgba(212, 169, 75, .35);
    color: var(--gold-bright);
    font-size: 12px;
}

/* item 16: confirm and diamond pick share ONE floating popup — the buttons
   wrap and center so small screens read it whole */
.bolster-confirm .center {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
}

/* item 16: the confirmation floats ABOVE the hall, rows stay beneath */
.bolster-panel {
    position: relative;
}

.bolster-confirm.floating {
    position: absolute;
    left: 6px;
    right: 6px;
    bottom: calc(100% + 4px);
    z-index: 70;
    background: rgba(24, 12, 9, .97);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .7);
}

/* item 21: a resting location still explains itself — SOLID and on top */
.loc-card:disabled {
    opacity: 1;
}

.loc-card:disabled>* {
    opacity: .4;
}

.loc-card[data-tip]:hover {
    z-index: 45;
}

/* v0.6.5.7 item 5: overlapping shelves stack left-to-right; the hovered
   card rises over its right neighbour's price circle */
.ishop-shelf .shop-item {
    position: relative;
}

.ishop-shelf .shop-item:hover {
    z-index: 26;
}

/* ==========================================================================
   v0.6.5.9 items 8 & 9 — wide screens read each location and bolster as
   ONE line: the name and its italic hint side by side, shorter rows; the
   narrow two-line stack survives below the threshold
   ========================================================================== */
@media (min-width: 1200px) {
    .loc-card:not(.loc-wide) {
        grid-template-columns: 30px auto 1fr;
        align-items: center;
        padding: 5px 36px 5px 10px;
    }

    .loc-card:not(.loc-wide) span {
        grid-row: auto;
    }

    .loc-card:not(.loc-wide) b {
        white-space: nowrap;
    }

    .loc-card:not(.loc-wide) i {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* v0.6.6.0 item 1: the Overlord's long name always reads as TWO lines —
       the wide one-liner passes it by */
    .loc-card.loc-wide b {
        white-space: normal;
    }

    .bolster-row {
        padding: 5px 10px;
        min-height: 44px;
    }

    /* v0.6.6.0 item 3: the name beside a TWO-LINE reward stack —
       the steady grant above, the one-time burst beneath */
    .bolster-text {
        flex-direction: row;
        align-items: center;
        gap: 10px;
    }

    .bolster-text b {
        white-space: nowrap;
    }

    .bolster-rewards {
        display: flex;
        flex-direction: column;
        min-width: 0;
    }

    .bolster-rewards i {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .bolster-rewards .bolster-boost::before {
        content: "";
    }
}
/* v0.6.6.1 item 8: the tiniest screens keep only the number — the word
   "Actions" rides the tooltip instead of breaking the title row */
@media (max-height: 500px), (max-width: 480px) {
    .day-actions-left .axn-word {
        display: none;
    }

    /* one fixed, digit-sized pill — same box for 0..9, nothing shifts */
    .day-actions-left {
        width: 44px;
        padding: 0 6px;
    }
}
/* ==========================================================================
   Battle boards and slots
   ========================================================================== */

/* ------------------------------------------------------------- day */
/* v0.4 stage: my board pinned LEFT, the world (day panel / enemy ranks) RIGHT */
#stage-grid {
    /* v0.6.5.1 items 7/18: the board fills the stage's REAL height — cards
       are sized off the measured container, not a viewport guess, so the
       top rank can never be squeezed out of view.
       v0.6.6.2 item 12: the cq experiment is OVER — a size container with
       an unresolved height collapses to nothing on iPad Safari, and the
       battle board vanished with it. The JS fitter (useStageFit) is the
       one authority on every layout now; these vars are only the
       first-paint fallback before it measures. */
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 16px;
    min-height: 0;
    flex: 1 1 auto;
    align-items: stretch;
    --card-w: 140px;
    --card-h: 196px;
}

/* ------------------------------------------------------------- board */
.board {
    display: flex;
    flex-direction: row;
    gap: 10px;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, .28);
    border: 1px solid #2c2314;
    border-radius: 16px;
    padding: 12px;
}

.board .board-col {
    display: grid;
    grid-template-rows: repeat(3, var(--card-h));
    gap: 8px;
}

/* the card currently in play rides ABOVE its neighbors, always (item 7) */
.slot.acting {
    z-index: 6;
}

.slot {
    width: var(--card-w);
    height: var(--card-h);
    border: 2px dashed #3a2f1c;
    border-radius: 10px;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: box-shadow .15s, border-color .15s;
}

.slot .slot-label {
    color: #52452c;
    font-size: 12px;
    font-family: 'Cinzel', serif;
}

.slot.blocked {
    border-style: solid;
    border-color: #4a3020;
    background: rgba(80, 40, 20, .18);
}

.slot.droppable {
    border-color: var(--ok);
    box-shadow: 0 0 14px rgba(116, 176, 106, .5);
    cursor: pointer;
}

.slot.evolvable {
    border-color: #b98add;
    box-shadow: 0 0 14px rgba(185, 138, 221, .5);
    cursor: pointer;
    position: relative;
}

/* v0.6.5.8 item 10: the upgrade target outlines itself ON TOP of the card
   standing in the slot — a dotted ring as loud as the empty-slot glow.
   Review m3: its OWN keyframe — the shared pulse flashed the enemy red */
@keyframes evolve-ring {

    0%,
    100% {
        box-shadow: inset 0 0 8px rgba(185, 138, 221, .35);
    }

    50% {
        box-shadow: inset 0 0 18px rgba(185, 138, 221, .75);
    }
}

.slot.evolvable::after {
    content: "";
    position: absolute;
    inset: 2px;
    border: 2px dashed #b98add;
    border-radius: 10px;
    z-index: 5;
    pointer-events: none;
    animation: evolve-ring 1.2s infinite;
}

.slot.legal-target {
    border-color: var(--blood);
    border-style: solid;
    box-shadow: 0 0 16px rgba(201, 79, 66, .65);
    animation: pulse 1s infinite;
}

.slot.spell-target {
    border-color: var(--arcane);
    border-style: solid;
    box-shadow: 0 0 16px rgba(120, 160, 220, .6);
}

.slot.acting {
    border-color: var(--gold);
    border-style: solid;
    box-shadow: 0 0 18px rgba(212, 169, 75, .7);
}

.slot.drag-over {
    transform: scale(1.04);
}

/* ==================================================== v0.6.1 battle UX */
/* issue 3: the acting unit's card pulses so the eye finds it instantly */
.slot.acting .card {
    outline: 2px solid rgba(255, 216, 120, .85);
    outline-offset: 1px;
    animation: acting-glow 1.1s ease-in-out infinite;
}

@keyframes acting-glow {

    0%,
    100% {
        box-shadow: 0 0 10px 2px rgba(255, 205, 96, .5), 0 3px 10px rgba(0, 0, 0, .6);
    }

    50% {
        box-shadow: 0 0 26px 9px rgba(255, 216, 120, .95), 0 3px 10px rgba(0, 0, 0, .6);
    }
}


/* v0.6.5.0 item 1.4: the spot exists, the unit limit refuses it */
.slot.locked {
    border-color: #6b3b2a;
    background: rgba(60, 20, 12, .25);
}

.slot.locked .slot-label {
    color: #c98a6a;
    font-size: 12px;
    text-align: center;
}

/* v0.6.5.5 item 13: the board runs to the hand — the contextual move/cancel
   buttons OVERLAY its bottom edge instead of reserving a strip */
#stage-board {
    position: relative;
}

#board-actions {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 6px;
    z-index: 6;
    pointer-events: none;
}

#board-actions .btn {
    pointer-events: auto;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .6);
}

/* v0.6.5.8 item 12: the board caption is gone everywhere — phase messages
   ride the top toast now, and the two boards mirror each other exactly */

/* v0.6.5.6 item 4: touch drags — the browser must not claim the gesture.
   v0.6.5.9 item 5: nor may Android's long-press summon its context menu —
   a held card is a DRAG, never a save-image prompt */
.hand-row .card,
#my-board .slot .card,
.ishop-shelf .card,
.ishop-token {
    touch-action: none;
    -webkit-user-drag: none;
    -webkit-touch-callout: none;
}

.card img {
    -webkit-user-drag: none;
    user-select: none;
}

/* v0.6.5.7 item 18: when the open hand hides the board's foot, the stage
   itself scrolls — thin and quiet */
#stage-board {
    overflow-y: auto;
    min-height: 0;
    scrollbar-width: thin;
}

/* v0.6.5.9 items 3/16: in combat the two boards MIRROR — equal columns,
   each board centered in its half, and nothing stands between the header
   and the hand (the controls strip is gone; hints ride the top toast) */
#stage-grid.combat {
    grid-template-columns: 1fr 1fr;
}

#stage-grid.combat #my-board,
#stage-grid.combat #enemy-board {
    margin: auto;
}

/* v0.6.6.1 item 6: the board/day divider exists only on the mobile band
   (70-mobile.css shows it) — display:none keeps it out of the grid tracks
   everywhere else, so .divided stays a plain two-column day grid here */
#stage-divider {
    display: none;
}

/* v0.6.6.1 item 7: on wide screens the day panel (and every non-combat
   right pane) claims at most HALF the stage — the board takes the rest */
@media (min-width: 1200px) {
    #stage-grid:not(.combat) {
        grid-template-columns: 1fr minmax(0, 50%);
    }
}

/* v0.6.6.2 item 3: a red health bar rides the LEFT edge of every living
   unit's card on the battle boards — day and night alike. Thin border,
   fill = current health percentage, anchored to the card's foot. */
.hp-side {
    position: absolute;
    left: -4px;
    /* v0.6.6.3 item 15: the bar measures the WHOLE card height */
    top: 2%;
    bottom: 2.1%;
    width: 6px;
    border: 1px solid rgba(0, 0, 0, .75);
    border-radius: 3px;
    background: rgba(10, 6, 3, .78);
    overflow: hidden;
    pointer-events: none;
    z-index: 4;
}

.hp-side i {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(180deg, #e4574a, #a2261c);
    border-radius: 2px;
}
/* ==========================================================================
   The hand dock
   ========================================================================== */

/* ------------------------------------------------------------- hand dock */
#hand-dock {
    flex: 0 0 auto;
    padding: 6px 16px 10px;
    background: linear-gradient(0deg, rgba(10, 7, 3, .95), rgba(10, 7, 3, .55) 82%, transparent);
    /* v0.6.6.3 item 4: no border line — the gradient alone marks the dock */
    z-index: 60;
}

.dock-tabs {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 5px;
    max-width: 1500px;
    margin-inline: auto;
}

.dock-tab {
    background: none;
    border: none;
    color: #a89a7c;
    font-family: 'Cinzel', serif;
    font-size: 14.5px;
    cursor: pointer;
    padding: 2px 10px;
    border-bottom: 2px solid transparent;
    letter-spacing: .5px;
}

.dock-tab.active {
    color: var(--gold-bright);
    border-bottom-color: var(--gold);
}


.hand-row {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    justify-content: safe center;
    /* top padding = headroom for the raised card + its channel button (item 4):
       overflow-x:auto forces vertical clipping, so the lift must stay inside */
    padding: 36px 8px 2px;
    max-width: 1500px;
    margin: 0 auto;
    min-height: calc(var(--card-h) * .58 + 44px);
    scrollbar-width: thin;
}

/* v0.6.5.9 item 6: the hand keeps its DAY size in combat — the old
   night-theme shrink to 92px is gone */

.hand-row .card {
    flex: 0 0 var(--card-w);
    transition: transform .18s, box-shadow .18s;
    cursor: grab;
}

.hand-row .card:hover {
    /* items 2/3 (v0.6.5.7): the hover pops the card ABOVE the dock border —
       the headroom lives INSIDE the scrollport (padding-top + neg margin) */
    transform: translateY(-40px) scale(1.05);
    z-index: 30;
    box-shadow: 0 10px 26px rgba(0, 0, 0, .8);
}

#hand-dock.folded .hand-row .card:hover {
    /* folded: the card climbs out to full view */
    transform: translateY(calc(var(--card-h) * -0.72));
}

.hand-row .card.selected {
    transform: translateY(-26px) scale(1.06);
    outline: 2px solid var(--gold);
    box-shadow: 0 0 22px rgba(212, 169, 75, .65);
}

.hand-row .card.unaffordable {
    filter: brightness(.55) saturate(.6);
}

.hand-row .card.dragging {
    opacity: .35;
}

/* v0.6.8.1 item 1.5: the price rides the BOTTOM-MIDDLE of the card — no
   permanent design element lives there, so it covers no stat. Mercs show
   gold AND XP, so their pill is double-width (the "twice" size). */
.badge-cost {
    position: absolute;
    bottom: -6px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 8;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-width: 76px;
    height: 30px;
    background: rgba(40, 32, 10, .94);
    border: 1px solid #6b5a2a;
    border-radius: 15px;
    font-size: 13px;
    padding: 2px 12px;
    color: var(--gold-bright);
    white-space: nowrap;
    pointer-events: none;
}

/* discard-for-channel affordance (v0.6.4.4 item 13): arm -> confirm, and
   the button grows past the card edge (same center as before).
   v0.6.8.1 item 1.3 — THE CORNER LAW: 🗑 discard/clear lives TOP-LEFT
   everywhere; 🔮 channel and 🩸 blood live TOP-RIGHT. Wait/Block mirror
   the same circles on the BOTTOM corners. */
.channel-btn {
    position: absolute;
    top: -3px;
    right: -3px;
    z-index: 8;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: rgba(20, 12, 40, .92);
    border: 1px solid var(--arcane);
    color: #cfe0ff;
    font-size: 19px;
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
}

.channel-btn.armed {
    display: flex;
    background: rgba(50, 30, 100, .95);
    border-color: #8ab4dd;
    box-shadow: 0 0 12px rgba(138, 180, 221, .9);
}

/* v0.6.8.1 item 1.1: the hand's FREE discard wears .discard-btn now — it
   was riding .channel-btn's arcane blues under a 🗑 glyph */
.hand-row .card:hover .channel-btn,
.hand-row .card:hover .discard-btn {
    display: flex;
}

/* item 1.2: one hover glow for every corner circle */
.channel-btn:hover,
.discard-btn:hover,
.blood-btn:hover,
.turn-btn:hover:not(:disabled) {
    box-shadow: 0 0 12px rgba(212, 169, 75, .75);
    transform: scale(1.12);
}

.channel-btn:hover {
    box-shadow: 0 0 12px rgba(138, 180, 221, .9);
}

/* v0.4 interactions */
.hand-row .card.banish-pick {
    outline: 2px dashed var(--blood);
    cursor: crosshair;
    animation: pulse 1.2s infinite;
}

/* F2/F6: on-card action buttons with hover captions — grown past the card
   edge, same center (v0.6.4.4 item 13). Item 1.3: discard = TOP-LEFT. */
.discard-btn,
.blood-btn {
    position: absolute;
    top: -3px;
    right: -3px;
    z-index: 8;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid #6b3020;
    background: rgba(40, 12, 8, .92);
    color: #f0d8c8;
    font-size: 19px;
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
}

.discard-btn {
    right: auto;
    left: -3px;
}

.discard-btn.armed {
    display: flex;
    background: rgba(120, 20, 10, .95);
    border-color: var(--blood);
    box-shadow: 0 0 10px rgba(201, 79, 66, .8);
}

/* v0.6.8.1 item 1.6: .cap is DATA now — the body-level action-tip layer
   (fx/actionTip.ts, z620) reads it on hover and renders above every
   stacking context, so no caption ever hides under the top bar. The
   in-card caption never displays for ENABLED buttons. */
.discard-btn .cap,
.blood-btn .cap,
.channel-btn .cap,
.turn-btn .cap {
    display: none;
}

/* …but a DISABLED <button> fires no pointer events, so the JS tip layer
   never sees it — the CSS :hover cap explains the disabled Wait/Block
   ("the strike already began", "a unit waits once per round"). The combat
   card sits well below the top bar, so this pseudo-cap is never clipped. */
.turn-btn:disabled:hover .cap {
    display: block;
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    background: rgba(14, 10, 6, .97);
    border: 1px solid #4a3c22;
    border-radius: 6px;
    padding: 3px 8px;
    font-size: 11px;
    font-style: normal;
    white-space: nowrap;
    color: var(--parchment);
    pointer-events: none;
    z-index: 12;
}

/* item 26: on wide screens the hand uses the full window width */
.dock-tabs,
.hand-row {
    max-width: none;
}


/* v0.6.5.1 item 10: while a bolster waits for its card, the WHOLE hand lifts
   like a play — every card is tuckable, playable or not */
.card.banish-pick {
    transform: translateY(-12px);
}

/* ==========================================================================
   v0.6.5.5 — the compressed, foldable hand (items 6, 7, 8)
   ========================================================================== */

/* item 6: no air between the tab row and the cards */
#hand-dock {
    padding-top: 0;
    gap: 0;
}

#hand-dock .dock-tabs {
    margin-bottom: 0;
    padding-bottom: 0;
}

/* the tab is a BUTTON now — same look, plus the folding eye */
.dock-tab {
    border: none;
    cursor: pointer;
    position: relative;
}

.dock-tab .fold-eye {
    opacity: .75;
    margin-left: 4px;
}

/* v0.6.5.6 item 11: the fold goes PARTIAL — the top quarter of every card
   stays visible, draggable and hoverable */
#hand-dock.folded .hand-row {
    height: calc(var(--card-h) * 0.25 + 6px);
    min-height: 0;
    overflow: hidden;
}

/* item 7: the help rides the tab's tooltip — served by the body-level
   action-tip layer since v0.6.8.1 item 1.6 (data-tip is the source) */

/* v0.6.5.6 items 1 & 2: no dead air, and the tab is a PULL-TAB fused to
   the cards */
#hand-dock {
    padding-bottom: 0;
}

.dock-tabs {
    justify-content: flex-start;
    padding: 0;
    margin: 0;
}

.dock-tab {
    border: 1px solid var(--gold-dim);
    border-bottom: none;
    border-radius: 10px 10px 0 0;
    background: linear-gradient(180deg, rgba(40, 32, 14, .95), rgba(24, 18, 9, .9));
    padding: 3px 14px 2px;
    margin: 0 0 0 12px;
}

.hand-row {
    padding-top: 0;
    margin-top: 0;
    border-top: 1px solid var(--gold-dim);
}

/* item 3: the 128px readability floor lives where each context sets
   --card-w (desktop default 132; the mobile block pins 128) */

/* v0.6.5.7 items 2, 3, 4: the pop headroom + always-horizontal scroll */
#hand-dock {
    overflow: visible;
}

.hand-row {
    overflow-x: auto;
    overflow-y: hidden;
    flex-wrap: nowrap;
    padding-top: calc(var(--card-h) * 0.75);
    margin-top: calc(var(--card-h) * -0.75);
    pointer-events: none;
    border-top: none;
}

.hand-row > * {
    pointer-events: auto;
}

/* the divider moves to the tab row — cards jump OVER it.
   v0.6.5.8 item 4: the FRAME yields to the cards — the row outranks the
   divider line, so a selected card can never wear it as a scar */
.dock-tabs {
    border-bottom: 1px solid var(--gold-dim);
    position: relative;
    z-index: 2;
}

.hand-row {
    position: relative;
    z-index: 3;
}

.hand-row .card.selected {
    z-index: 30;
}

/* item 19: the armed spell says what the next tap does */
.cast-arm-cap {
    position: absolute;
    top: -26px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9;
    background: rgba(20, 12, 40, .95);
    border: 1px solid var(--arcane);
    border-radius: 6px;
    padding: 3px 8px;
    font-size: 11px;
    font-style: normal;
    white-space: nowrap;
    color: #cfe0ff;
    pointer-events: none;
}

#hand-dock.folded .hand-row {
    overflow-x: auto;
    height: calc(var(--card-h) * 0.25 + 6px + var(--card-h) * 0.75);
    margin-top: calc(var(--card-h) * -0.75);
}

/* ==========================================================================
   v0.6.5.9 item 7 — the overflowing hand thins itself
   ========================================================================== */

/* the cards the "Yes" would let go wear a mark while the hand overflows */
.hand-row .card.overflow-pick {
    outline: 2px dashed var(--blood);
    outline-offset: 2px;
    box-shadow: 0 0 14px rgba(201, 79, 66, .45);
}

/* the question floats above the dock, centered — buttons never move */
.thin-hand-pop {
    position: fixed;
    left: 50%;
    bottom: calc(var(--card-h) * 1.05 + 60px);
    transform: translateX(-50%);
    z-index: 320;
    background: rgba(24, 14, 8, .97);
    border: 1px solid var(--blood);
    border-radius: 12px;
    padding: 12px 16px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .8);
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: min(440px, 94vw);
    text-align: center;
}

.thin-hand-pop .hint {
    font-size: 13px;
}

.thin-hand-pop .row {
    display: flex;
    gap: 10px;
    justify-content: center;
}

/* ==========================================================================
   v0.6.6.0 items 7 & 17 — the hand keeps its height; the bar takes the hand
   ========================================================================== */

/* item 7: an EMPTY hand keeps its full height — the panel never collapses
   when the last card leaves; only the fold tab shrinks it. The box carries
   the pop headroom (0.75 card-h) canceled by the negative margin, so the
   VISIBLE band stays one card tall. Outranks the mobile band's min-height:0 */
#hand-dock:not(.folded) .hand-row {
    min-height: calc(var(--card-h) * 1.75 + 2px);
}

/* item 17: the native scrollbar sat inside a pointer-events:none scrollport
   and never took the mouse — hidden for good; the proxy bar below scrubs */
.hand-row {
    scrollbar-width: none;
}

.hand-row::-webkit-scrollbar {
    display: none;
}

.hand-scrollbar {
    height: 10px;
    margin: 0 10px 3px;
    border-radius: 5px;
    background: rgba(74, 60, 34, .35);
    position: relative;
    cursor: pointer;
    touch-action: none;
}

.hand-scrollbar .thumb {
    position: absolute;
    top: 1px;
    bottom: 1px;
    border-radius: 4px;
    background: var(--gold-dim);
    opacity: .85;
    pointer-events: none;
}

.hand-scrollbar:hover .thumb,
.hand-scrollbar.dragging .thumb {
    background: var(--gold);
}

/* nothing to scroll: the bar stays (same spot, same size) but goes quiet */
.hand-scrollbar.idle {
    opacity: .3;
    cursor: default;
}

/* ==========================================================================
   v0.6.6.1 item 2: the combat round-dock — the round counter and the
   End casting / End round button live on the hand dock's tab row, the one
   strip every layout keeps on screen. Right-aligned so the pull-tab on the
   left never moves; the label swaps in place, the button holds its spot.
   ========================================================================== */
.round-dock {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
    padding-right: 8px;
}

.round-dock .round-ctr {
    font-family: 'Cinzel', serif;
    font-size: 13px;
    color: var(--gold-bright);
    white-space: nowrap;
    cursor: help;
}

/* v0.6.6.2 item 1: End casting stands TALL — bigger, serifed, and it
   breathes with an animated glow while the window is yours */
.round-dock .btn {
    padding: 6px 20px;
    font-size: 15px;
    font-family: 'Cinzel', serif;
    letter-spacing: .5px;
    white-space: nowrap;
    border-width: 2px;
}

@keyframes endcast-call {
    0%, 100% {
        box-shadow: 0 0 8px rgba(212, 169, 75, .45);
        border-color: var(--gold-dim);
    }
    50% {
        box-shadow: 0 0 22px rgba(255, 216, 120, .9);
        border-color: var(--gold-bright);
    }
}

.round-dock .btn:not(:disabled) {
    animation: endcast-call 1.6s ease-in-out infinite;
}

/* v0.6.6.2 item 2: the round's progress spans the hand's top edge — five
   notches, filled to the current round, the last one red */
.round-progress {
    display: flex;
    gap: 3px;
    height: 7px;
    margin: 0 12px 3px;
}

.rp-seg {
    flex: 1 1 0;
    border-radius: 3px;
    background: rgba(74, 60, 34, .35);
    border: 1px solid rgba(0, 0, 0, .45);
}

.rp-seg.on {
    background: var(--gold);
    box-shadow: 0 0 6px rgba(212, 169, 75, .55);
}

.rp-seg.last {
    background: rgba(120, 30, 25, .4);
}

.rp-seg.last.on {
    background: var(--blood);
    box-shadow: 0 0 8px rgba(201, 79, 66, .75);
}

/* v0.6.6.2 item 9: while a shelf card is dragged, the hand dock says
   "drop here to buy" with a golden edge */
#hand-dock.buy-target {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
    box-shadow: 0 -6px 18px rgba(212, 169, 75, .35);
}

/* v0.6.7.2 item 13: the acting unit's Wait / Block pair — two fixed slots
   pinned to the card's bottom corners; they render only on the ONE card that
   is aiming, and Wait dims (never vanishes) once spent this round */
/* v0.6.8.1 item 1.4: Wait/Block join the corner-circle family — the same
   40px rounds as discard/channel/blood, only on the BOTTOM corners of the
   acting card: ⏳ Wait bottom-left, 🛡 Block bottom-right. */
.turn-actions {
    position: absolute;
    inset: 0;
    z-index: 9;
    pointer-events: none;
}

.turn-btn {
    pointer-events: auto;
    position: absolute;
    bottom: -3px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid #4a3c22;
    background: rgba(14, 10, 6, .95);
    color: var(--parchment);
    font-size: 19px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.turn-btn.wait {
    left: -3px;
    border-color: #8a6b2f;
}

.turn-btn.block {
    right: -3px;
    border-color: #3d5f9e;
    color: #cfe0ff;
}

.turn-btn.block:hover:not(:disabled) {
    box-shadow: 0 0 12px rgba(90, 140, 230, .8);
}

.turn-btn:disabled {
    opacity: .45;
    cursor: default;
}

/* v0.6.7.2 item 12: SHATTERED armor wears a translucent ban sign — the
   tokens still chip away, but stop no damage */
.c-armor {
    position: relative;
}

.c-armor .armor-ban {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-style: normal;
    opacity: .72;
    pointer-events: none;
}

/* v0.6.7.2 item 9: an unresolved on-card action PULSES until it is spent.
   The blood rite no longer hides behind a hover — while the rite is open
   the drop beats; the armed state beats harder */
.blood-btn {
    display: flex;
    animation: action-pulse 1.6s ease-in-out infinite;
}

.blood-btn.armed {
    animation: action-pulse-armed 1.1s ease-in-out infinite;
}

@keyframes action-pulse {
    0%, 100% { box-shadow: 0 0 4px rgba(201, 79, 66, .35); }
    50% { box-shadow: 0 0 14px rgba(201, 79, 66, .9); }
}

@keyframes action-pulse-armed {
    0%, 100% { box-shadow: 0 0 8px rgba(201, 79, 66, .6); }
    50% { box-shadow: 0 0 18px rgba(220, 90, 70, 1); }
}

/* item 9: a summon (or spell) awaiting its spot beats on the glowing slots
   until the player resolves it */
.slot.spell-target {
    animation: slot-pulse 1.6s ease-in-out infinite;
}

@keyframes slot-pulse {
    0%, 100% { box-shadow: 0 0 8px rgba(120, 160, 220, .4); }
    50% { box-shadow: 0 0 22px rgba(140, 180, 240, .85); }
}
/* ==========================================================================
   Combat arena: dice, payments, night flow, final scores
   ========================================================================== */

#panel-assign.on,
#panel-night-wait.on,
#panel-final.on {
    justify-content: flex-start;
    padding: 22px;
    margin: auto 0;
}

/* v0.6.5.8 item 12: the enemy ranks stand BARE in the right pane — no
   panel, no title (the toast announces the opponent); centered, so the
   two boards mirror each other exactly */
#stage-right > #enemy-board {
    margin: auto;
}

/* dice chip (req 3.4): next to the ACC corner */
.c-die {
    position: absolute;
    top: 4px;
    right: 44px;
    z-index: 5;
    /* LEFT of the ACC corner (item 17); the stone face art is the chip
       itself (v0.6.4.8 item 8) */
    width: 21px;
    height: 21px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 1px 4px #000;
    animation: die-drop 1s cubic-bezier(.2, 2.4, .4, 1);
}

/* v0.6.5.0 item 2: the armed CRIT rides beside the ATTACK plate, the mirror
   of the die beside ACC — same size, same band */
.c-crit {
    position: absolute;
    top: 4px;
    left: 44px;
    z-index: 5;
    width: 21px;
    height: 21px;
    display: flex;
    align-items: center;
    justify-content: center;
    filter: drop-shadow(0 1px 4px #000);
    animation: die-drop .6s cubic-bezier(.2, 2.4, .4, 1);
}

.c-crit img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.c-die img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.c-die.pass {
    outline: 2.5px solid #74e06a;
    box-shadow: 0 0 10px rgba(116, 224, 106, .8);
}

.c-die.fail {
    outline: 2.5px solid #e05a4a;
    box-shadow: 0 0 10px rgba(224, 90, 74, .8);
    filter: saturate(.7);
}

.c-die.flipped {
    animation: die-flip 1.1s ease;
}

/* ------------------------------------------------------------- arena */
/* v0.6.5.9 item 3: the controls strip is GONE — phase hints ride the top
   toast and the blood gems live on the offering card itself */

.duel-grid {
    display: flex;
    gap: 10px;
    justify-content: center;
    margin: 16px 0;
    flex-wrap: wrap;
}

.duel-card {
    background: #17120b;
    border: 2px solid #2e2517;
    border-radius: 10px;
    padding: 14px 18px;
    color: var(--parchment);
    cursor: pointer;
    font-family: 'Cinzel', serif;
}

.duel-card.selected {
    border-color: var(--blood);
    box-shadow: 0 0 14px rgba(201, 79, 66, .5);
}

.night-results {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 12px;
}

.final-scores {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 14px 0;
}

.score-row {
    display: flex;
    gap: 12px;
    align-items: center;
    background: rgba(0, 0, 0, .3);
    border: 1px solid #33291a;
    border-radius: 10px;
    padding: 10px 16px;
    font-size: 17px;
}

.score-row.winner {
    border-color: var(--gold);
    box-shadow: 0 0 16px rgba(212, 169, 75, .4);
}

.score-row .fate {
    margin-left: auto;
    color: var(--gold-bright);
    font-family: 'Cinzel', serif;
    font-size: 20px;
}

#panel-assign.on,
#panel-night-wait.on,
#panel-final.on {
    margin: 0;
}

.blood-btn {
    border-color: #7a1622;
    background: rgba(52, 8, 14, .94);
}

.blood-btn.armed {
    display: flex;
    background: rgba(120, 20, 25, .95);
    border-color: var(--blood);
    box-shadow: 0 0 12px rgba(201, 79, 66, .9);
}

/* v0.6.5.9 item 2: the four blood gems land ON the bleeding card — a 2×2
   grid over the art; each gem names its gift */
.blood-gems {
    position: absolute;
    top: 14%;
    left: 5%;
    right: 5%;
    z-index: 9;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 5px;
    padding: 6px;
    background: rgba(24, 6, 9, .93);
    border: 1px solid #7a1622;
    border-radius: 10px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .75);
}

.blood-gem {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 5px 2px;
    border-radius: 8px;
    border: 1px solid #6b3020;
    background: rgba(52, 8, 14, .94);
    color: #f0d8c8;
    cursor: pointer;
}

.blood-gem span {
    font-size: 16px;
    line-height: 1.1;
}

.blood-gem b {
    font-size: 9px;
    font-family: 'Cinzel', serif;
    letter-spacing: .4px;
}

.blood-gem:hover {
    border-color: var(--blood);
    box-shadow: 0 0 10px rgba(201, 79, 66, .8);
}

.mob-btn {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    font-size: 17px;
    background: rgba(24, 18, 10, .92);
    border: 1px solid #4a3c22;
    color: var(--parchment);
}

/* ================================================== v0.6.4.1 client pass */
/* item 8: the final screen's per-source Fate breakdown */
.score-row {
    flex-wrap: wrap;
}

.fate-breakdown {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    padding: 2px 0 4px 30px;
}

.fate-src {
    font-size: 13px;
    color: #b8a67e;
}

.fate-src b {
    color: var(--gold-bright);
    font-family: 'Cinzel', serif;
}

/* ==========================================================================
   Shop, library and viewer popovers
   ========================================================================== */

/* ------------------------------------------------------------- popovers */
.popover {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.popover .popover-frame {
    pointer-events: auto;
    background: rgba(24, 18, 10, .97);
}

.popover-frame {
    width: min(920px, 94vw);
    max-height: 78vh;
    padding: 16px 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.popover-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.popover-actions {
    display: flex;
    gap: 8px;
}

.shop-body {
    display: flex;
    gap: 18px;
    align-items: flex-start;
}

.shop-shelf {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    flex: 1 1 auto;
    justify-content: center;
}

.shop-shelf.scroll {
    overflow-y: auto;
    max-height: 62vh;
    scrollbar-width: thin;
}

.shop-item {
    position: relative;
}

.shop-item .btn {
    width: var(--card-w);
    margin-top: 5px;
    font-size: 12px;
    padding: 6px 4px;
}

.shop-piles {
    display: flex;
    flex-direction: column;
    gap: 14px;
    flex: 0 0 auto;
    border-left: 1px dashed #4a3c22;
    padding-left: 18px;
    padding-right: 5px;

}

.pile {
    position: relative;
    width: calc(var(--card-w) * 1.15);
    text-align: center;
}

.pile>img {
    width: 100%;
    border-radius: 8px;
    box-shadow: 2px 2px 0 #17120b, 4px 4px 0 #100c07, 0 6px 14px rgba(0, 0, 0, .6);
}

/* v0.6.4.5 item 19: the discard's top card is the NORMAL card scaled to
   the pile width — a pixel-exact miniature, so no font or offset can ever
   clip differently than the full-size face does. The negative margins
   collapse the layout box to the scaled size. */
.pile .card {
    transform: scale(1.15);
    transform-origin: top left;
    margin-right: calc(var(--card-w) * -0.18);
    margin-bottom: calc(var(--card-h) * 0.2);
}

.pile .discard-face {
    width: 100%;
    aspect-ratio: 5/7;
    border-radius: 8px;
    border: 2px dashed #4a3c22;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 40px;
    color: #6a5a38;
    margin-bottom: 5px;
}

.pile.clickable {
    cursor: pointer;
}

.pile.clickable:hover .discard-face {
    border-color: var(--gold-dim);
    color: var(--gold-dim);
}

.pile-count {
    position: absolute;
    top: -8px;
    right: -8px;
    background: var(--gold);
    color: var(--ink);
    font-family: 'Cinzel', serif;
    font-weight: 700;
    border-radius: 50%;
    min-width: 26px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    box-shadow: 0 2px 6px #000;
}

.pile i {
    color: #a89a7c;
    font-size: 12.5px;
}

.shop-footer {
    display: flex;
    gap: 8px;
    justify-content: center;
}/* ==========================================================================
   Full-screen popups: era bursts, battle results
   ========================================================================== */

/* ------------------------------------------------------------- popups */
.fullscreen-popup {
    position: fixed;
    inset: 0;
    /* v0.6.6.3 item 9: the verdict overlay sits ABOVE every battle effect
       (skulls 460, drag ghost 500, tooltip 600) — animations play under it */
    z-index: 650;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(5, 3, 1, .78);
}

.era-burst,
.result-burst {
    text-align: center;
    animation: burst-in .5s cubic-bezier(.2, 1.6, .4, 1);
}

.era-burst img {
    width: 190px;
    filter: drop-shadow(0 0 40px rgba(212, 169, 75, .9));
    animation: era-glow 2.2s infinite;
}

.era-burst h1 {
    font-family: 'Cinzel Decorative', serif;
    font-size: 56px;
    color: var(--gold-bright);
    text-shadow: 0 0 30px rgba(212, 169, 75, .8);
    letter-spacing: 8px;
    margin: 8px 0;
}

.era-burst p {
    color: #cdbf9d;
    font-size: 19px;
    margin-bottom: 22px;
}

.result-burst h1 {
    font-family: 'Cinzel Decorative', serif;
    font-size: 74px;
    letter-spacing: 10px;
    margin-bottom: 6px;
}

.result-burst.victory h1 {
    color: #ffd98a;
    text-shadow: 0 0 40px rgba(255, 200, 80, .95), 0 0 90px rgba(255, 160, 40, .6);
    animation: victory-shine 1.6s ease-in-out infinite;
}

.result-burst.victory::before {
    content: "";
    position: fixed;
    inset: -120vmax;
    z-index: -1;
    background: conic-gradient(from 0deg, transparent, rgba(255, 200, 80, .16) 20deg, transparent 40deg,
            rgba(255, 200, 80, .16) 60deg, transparent 80deg, rgba(255, 200, 80, .16) 100deg, transparent 120deg,
            rgba(255, 200, 80, .16) 140deg, transparent 160deg, rgba(255, 200, 80, .16) 180deg, transparent 200deg,
            rgba(255, 200, 80, .16) 220deg, transparent 240deg, rgba(255, 200, 80, .16) 260deg, transparent 280deg,
            rgba(255, 200, 80, .16) 300deg, transparent 320deg, rgba(255, 200, 80, .16) 340deg, transparent);
    animation: rays-spin 14s linear infinite;
}

.result-burst.defeat h1 {
    color: #9db3e8;
    text-shadow: 0 0 30px rgba(90, 110, 200, .8);
}

.result-burst.draw h1 {
    color: #e0a86a;
    text-shadow: 0 0 30px rgba(224, 130, 60, .7);
}

.result-burst p {
    color: #cdbf9d;
    font-size: 20px;
    margin-bottom: 10px;
}

/* v0.6.7.4 item 19: the verdict redesigned — ONE panel below the title
   holds headline, ledger and Continue. No nested boxes: sections divide by
   hairlines, every line centers, the ledger scrolls so Continue never
   leaves the screen. */
.result-burst {
    display: flex;
    flex-direction: column;
    align-items: center;
    max-height: min(88vh, 88dvh);
}

.rp-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: min(480px, 92vw);
    min-height: 0;
    padding: 16px 18px 14px;
    border: 1px solid rgba(212, 169, 75, .4);
    border-radius: 12px;
    background: rgba(14, 9, 4, .92);
    text-align: center;
}

.result-spoils {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 22px;
    justify-content: center;
    align-items: baseline;
    margin-bottom: 8px;
    font-size: 19px;
    color: #e8dcbc;
}

.xp-tick {
    color: #a5f08c;
    font-family: 'Cinzel', serif;
    font-size: 26px;
}

.rp-ledger {
    width: 100%;
    margin: 4px 0 12px;
    overflow-y: auto;
    text-align: center;
}

.rp-sec {
    border-top: 1px solid rgba(212, 169, 75, .22);
}

.rp-sec:first-child {
    border-top: 0;
}

.rp-sec-head {
    display: flex;
    width: 100%;
    justify-content: center;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    background: none;
    border: 0;
    color: #e8dcbc;
    font-family: 'Cinzel', serif;
    font-size: 15px;
    cursor: pointer;
}

/* v0.6.8.1 item 1.2: every popup button answers the pointer with a glow */
.rp-sec-head:hover:not(:disabled) {
    text-shadow: 0 0 10px rgba(212, 169, 75, .8);
    color: var(--gold-bright);
}

.rp-sec-head:disabled {
    opacity: .45;
    cursor: default;
}

.rp-sec-head b {
    color: var(--gold-bright, #d4a94b);
    font-size: 13px;
}

.rp-sec-body {
    padding: 2px 12px 10px;
    color: #cdbf9d;
    font-size: 14px;
}

.rp-roll {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.rp-roll b {
    display: block;
    font-size: 12px;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: #9b8c68;
    margin-bottom: 4px;
}

.rp-roll ul,
.rp-spoils-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.rp-roll li,
.rp-spoils-list li {
    padding: 2px 0;
    line-height: 1.35;
}

.rp-roll li i {
    color: #d98a7a;
    font-style: normal;
}

.rp-none {
    opacity: .5;
}

.rp-spoils-list li {
    display: flex;
    justify-content: center;
    gap: 12px;
}

.rp-spoils-list b {
    color: var(--gold-bright, #d4a94b);
}

/* the verdict on a phone: the title breathes in, the panel takes the width */
@media (max-width: 520px) {
    .result-burst h1 {
        font-size: 46px;
        letter-spacing: 4px;
    }

    .rp-panel {
        width: 94vw;
        padding: 12px 10px;
    }
}

/* item 7: the one-visit-left choice */
#default-visit-popup .choice-box {
    max-width: 460px;
    padding: 22px 26px;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

#default-visit-popup .center {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* ==========================================================================
   Arrows, cursors, tooltips, scrollbars
   ========================================================================== */

@keyframes pulse {
    50% {
        box-shadow: 0 0 26px rgba(201, 79, 66, .9);
    }
}

@keyframes die-drop {
    0% {
        transform: translateY(-16px) rotate(-140deg);
        opacity: 0;
    }
}

@keyframes die-flip {

    0%,
    40% {
        transform: scale(1.55);
    }
}

@keyframes hit-shake {
    20% {
        transform: translateX(-4px) rotate(-2deg);
        filter: brightness(1.9) saturate(2);
    }

    50% {
        transform: translateX(4px) rotate(2deg);
    }
}

@keyframes heal-glow {
    40% {
        filter: brightness(1.5) hue-rotate(70deg);
    }
}

/* floating damage numbers */
.float-num {
    position: absolute;
    z-index: 40;
    font-family: 'Cinzel', serif;
    font-weight: 900;
    font-size: 24px;
    pointer-events: none;
    animation: float-up 2s ease-out forwards;
    text-shadow: 0 2px 4px #000, 0 0 8px #000;
}

.float-num.dmg {
    color: #ff7a5c;
}

.float-num.heal {
    color: #8ce68c;
}

@keyframes float-up {
    to {
        transform: translateY(-46px);
        opacity: 0;
    }
}

/* card ghost while dragging */
#drag-ghost {
    position: fixed;
    z-index: 500;
    pointer-events: none;
    opacity: .92;
    transform: translate(-50%, -60%) scale(1.02);
    filter: drop-shadow(0 14px 22px rgba(0, 0, 0, .7));
}

/* v0.6.7.4 item 8: the death wipe — the evolve reveal (.evo-cover /
   .evo-line below) inverted: the veil DESCENDS top->bottom behind a
   dark-grey sparkler line, revealing a blacked-out face with the skull
   upon it, then fades to the corpse render beneath. No glow anywhere —
   death is the ascension's opposite — and slightly faster (1.25s vs 1.6s). */
.death-fx {
    position: fixed;
    z-index: 460;
    pointer-events: none;
    border-radius: 10px;
    overflow: hidden;
    /* v0.6.8.1 item 12: the sparklers mourn in a DARKER grey */
    --death-c: #565b60;
}

.death-fx .death-veil {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(180deg, #101014, #1a1a20 55%, #0d0d11);
    clip-path: inset(0 0 100% 0);
    animation:
        death-veil-reveal 1.25s ease-out forwards,
        death-veil-fade .35s ease 1.35s forwards;
}

@keyframes death-veil-reveal {
    to { clip-path: inset(0 0 0 0); }
}

@keyframes death-veil-fade {
    to { opacity: 0; }
}

.death-fx .death-skull {
    position: absolute;
    left: 50%;
    top: 50%;
    margin: -26px 0 0 -26px;
    font-size: 52px;
    /* v0.6.8.1 item 12: the skull span lives inside an <i> veil — without
       this reset it inherits the UA's italic and renders slanted */
    font-style: normal;
    filter: grayscale(1) drop-shadow(0 4px 10px #000);
    opacity: 0;
    animation: death-skull-in .45s ease .5s forwards;
}

@keyframes death-skull-in {
    from {
        transform: scale(.55);
        opacity: 0;
    }

    to {
        transform: scale(1);
        opacity: 1;
    }
}

.death-fx .death-line {
    position: absolute;
    left: -2%;
    right: -2%;
    height: 7px;
    top: -7px;
    border-radius: 4px;
    background: linear-gradient(90deg, transparent, var(--death-c) 25%, #8a9096 50%, var(--death-c) 75%, transparent);
    animation: death-line-sweep 1.25s ease-out forwards, death-flicker .12s infinite;
}

.death-fx .death-line::after {
    content: "\2726 \2727 \2726";
    position: absolute;
    left: 0;
    right: 0;
    top: -13px;
    text-align: center;
    font-size: 10px;
    letter-spacing: 12px;
    color: var(--death-c);
}

@keyframes death-line-sweep {
    0% { top: -7px; opacity: 1; }
    90% { opacity: 1; }
    100% { top: calc(100% - 7px); opacity: 0; }
}

@keyframes death-flicker {
    50% { filter: brightness(1.35); }
}

@keyframes burst-in {
    0% {
        transform: scale(.6);
        opacity: 0;
    }
}

@keyframes victory-shine {
    50% {
        text-shadow: 0 0 70px rgba(255, 220, 120, 1), 0 0 130px rgba(255, 160, 40, .9);
    }
}

@keyframes rays-spin {
    to {
        transform: rotate(360deg);
    }
}

/* ------------------------------------------------------------- arrows & cursor */
#arrow-overlay {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    z-index: 180;
    pointer-events: none;
}

#arrow-path {
    fill: none;
    stroke-width: 5;
    stroke-linecap: round;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .7));
}

#arrow-overlay.red #arrow-path {
    stroke: url(#arrow-red);
}

#arrow-overlay.green #arrow-path {
    stroke: url(#arrow-green);
}

#arrow-overlay.red #arrow-head {
    fill: #e2372c;
}

#arrow-overlay.green #arrow-head {
    fill: #3fae4a;
}

@keyframes deny {
    25% {
        transform: translateX(-5px);
    }

    75% {
        transform: translateX(5px);
    }
}

/* ------------------------------------------------------------- tooltip */
/* v0.6.8.1 item 1.6: the ACTION TIP — one body-level caption for every
   corner button (.cap) and [data-tip] element. position:fixed at z620
   beats the top bar (z120) and escapes every overflow container, so no
   caption is ever visually truncated again. */
.action-tip {
    position: fixed;
    z-index: 620;
    max-width: 320px;
    background: rgba(18, 13, 7, .97);
    border: 1px solid var(--gold-dim);
    border-radius: 8px;
    padding: 6px 10px;
    font-size: 12px;
    color: var(--parchment);
    pointer-events: none;
    white-space: normal;
    text-align: left;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .6);
    line-height: 1.35;
}

/* v0.6.4.4 item 4: the tooltip is a twin panel — the pristine base card on
   the left (portaled GameCard, full printed HP), keywords & statuses right */
.kw-tooltip {
    position: fixed;
    z-index: 600;
    background: rgba(14, 10, 5, .97);
    border: 1px solid var(--gold-dim);
    border-radius: 10px;
    padding: 10px 12px;
    font-size: 13.5px;
    color: var(--parchment);
    box-shadow: 0 10px 28px rgba(0, 0, 0, .8);
    pointer-events: none;
    line-height: 1.4;
    gap: 12px;
    align-items: flex-start;
}

.kw-tooltip .tip-card {
    --card-w: 132px;
    --card-h: calc(132px * 1.4);
    flex: 0 0 auto;
}

.kw-tooltip .tip-card:empty {
    display: none;
}

.kw-tooltip .tip-body {
    max-width: 300px;
    min-width: 180px;
}

.kw-tooltip b {
    color: var(--gold-bright);
    font-family: 'Cinzel', serif;
}

.kw-tooltip .kw-line {
    margin-top: 5px;
}

.kw-tooltip .kw-line b {
    font-size: 12px;
    letter-spacing: .5px;
}

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-thumb {
    background: #4a3c22;
    border-radius: 4px;
}

::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, .25);
}

/* issue 5: the tooltip's "current condition" block */
.kw-tooltip .tip-statuses {
    display: block;
    margin: 5px 0;
    padding: 5px 7px;
    background: rgba(212, 169, 75, .1);
    border-left: 2px solid var(--gold);
    border-radius: 3px;
}

.kw-tooltip .tip-statuses b {
    font-size: 12px;
    letter-spacing: .5px;
}


/* v0.6.5.1 item 6: a transient in-world notice — no popup, just a whisper */
.game-toast {
    position: fixed;
    top: 64px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 320;
    background: rgba(18, 13, 7, .96);
    border: 1px solid var(--gold-dim);
    border-radius: 10px;
    padding: 10px 18px;
    color: var(--gold-bright);
    font-size: 15px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .6);
    transition: opacity .5s ease, transform .5s ease;
}

.game-toast.gone {
    opacity: 0;
    transform: translateX(-50%) translateY(-8px);
}

/* ==========================================================================
   v0.6.6.1 items 3, 4, 5 — combat & evolution flourishes
   ========================================================================== */

/* item 3: the round opens with a rolling die, center stage, self-dismissing */
.round-roll {
    position: fixed;
    left: 50%;
    top: 38%;
    transform: translate(-50%, -50%);
    z-index: 310;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 26px;
    background: rgba(14, 10, 5, .93);
    border: 1px solid var(--gold-dim);
    border-radius: 14px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .6);
    pointer-events: none;
    animation: rr-life 1.5s ease forwards;
}

.round-roll .rr-die {
    font-size: 42px;
    line-height: 1;
    color: var(--gold-bright);
    animation: rr-tumble .14s linear infinite;
}

.round-roll.settled .rr-die {
    animation: none;
    transform: none;
    text-shadow: 0 0 14px rgba(255, 217, 138, .9);
}

.round-roll b {
    font-family: 'Cinzel', serif;
    font-size: 21px;
    letter-spacing: 1px;
    color: var(--gold-bright);
}

@keyframes rr-life {
    0% { opacity: 0; }
    10%, 78% { opacity: 1; }
    100% { opacity: 0; }
}

@keyframes rr-tumble {
    0% { transform: rotate(-14deg) scale(.94); }
    100% { transform: rotate(14deg) scale(1.06); }
}

/* item 4: the damage-source icon travels attacker -> target */
.strike-fly {
    position: fixed;
    z-index: 305;
    width: 34px;
    height: 34px;
    pointer-events: none;
    font-size: 26px;
    text-align: center;
    line-height: 34px;
    transition: transform .3s cubic-bezier(.45, .05, .85, .5);
    filter: drop-shadow(0 0 8px rgba(255, 180, 80, .9));
}

.strike-fly img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* …and the landing blow rattles its victim */
@keyframes card-jiggle {
    0%, 100% { transform: translate(0, 0) rotate(0); }
    20% { transform: translate(-4px, 1px) rotate(-2deg); }
    40% { transform: translate(4px, -1px) rotate(2deg); }
    60% { transform: translate(-3px, 0) rotate(-1.5deg); }
    80% { transform: translate(2px, 1px) rotate(1deg); }
}

.card.jiggle {
    animation: card-jiggle .45s ease;
}

/* item 5: the sparkler reveal — a glowing line in the new form's rarity
   color climbs the evolved card, unveiling the face beneath it */
.evo-fx {
    position: absolute;
    inset: 0;
    z-index: 8;
    border-radius: 12px;
    overflow: hidden;
    pointer-events: none;
}

.evo-fx .evo-cover {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 100%;
    background: linear-gradient(180deg, rgba(8, 5, 2, .96), rgba(8, 5, 2, .88));
    animation: evo-cover 1.6s ease-out forwards;
}

.evo-fx .evo-line {
    position: absolute;
    left: -6%;
    right: -6%;
    height: 7px;
    top: calc(100% - 7px);
    border-radius: 4px;
    background: linear-gradient(90deg, transparent, var(--evo-c, #ffc850) 22%, #fff 50%, var(--evo-c, #ffc850) 78%, transparent);
    box-shadow: 0 0 14px var(--evo-c, #ffc850), 0 0 30px var(--evo-c, #ffc850);
    animation: evo-line 1.6s ease-out forwards, evo-flicker .12s linear infinite;
}

/* a few sparks ride above the line, in the same rarity color */
.evo-fx .evo-line::after {
    content: "✦ ✧ ✦";
    position: absolute;
    left: 0;
    right: 0;
    top: -14px;
    text-align: center;
    font-size: 11px;
    letter-spacing: 14px;
    color: var(--evo-c, #ffc850);
    text-shadow: 0 0 6px #fff;
}

@keyframes evo-cover {
    0% { height: 100%; opacity: 1; }
    92% { opacity: 1; }
    100% { height: 0; opacity: 0; }
}

@keyframes evo-line {
    0% { top: calc(100% - 7px); opacity: 1; }
    92% { opacity: 1; }
    100% { top: -7px; opacity: 0; }
}

@keyframes evo-flicker {
    0% { filter: brightness(1); }
    50% { filter: brightness(1.45); }
    100% { filter: brightness(.9); }
}

/* v0.6.6.2 item 5: the sweep ends in a glow around the LEVEL icon — the
   crown of the new form flares once the line reaches the top */
.evo-fx .evo-crown {
    position: absolute;
    top: -12px;
    left: 50%;
    width: 46px;
    height: 46px;
    margin-left: -23px;
    border-radius: 50%;
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(circle, var(--evo-c, #ffc850) 0%, rgba(255, 255, 255, .55) 30%, transparent 70%);
    animation: evo-crown 1s ease-out 1.55s forwards;
}

@keyframes evo-crown {
    0% { opacity: 0; transform: scale(.4); }
    35% { opacity: .95; transform: scale(1.25); }
    100% { opacity: 0; transform: scale(1.7); }
}
/* ==========================================================================
   v0.5 mobile — landscape phones (media-scoped)
   ========================================================================== */

/* ==================================================================== v0.5
   MOBILE — landscape phones. Nothing scales wholesale: the board, the right
   pane and the hand stay; the rail and the players strip hide behind buttons. */
#mobile-toggles {
    display: none;
    position: fixed;
    top: 4px;
    right: 6px;
    z-index: 320;
    gap: 6px;
}

/* v0.6.5.8 item 5: the rotate veil is gone — portrait PLAYS now */

.drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(340px, 80vw);
    z-index: 340;
    background: rgba(18, 13, 7, .98);
    border-left: 1px solid #4a3c22;
    padding: 12px 14px;
    flex-direction: column;
    gap: 8px;
    overflow-y: auto;
}

.drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.drawer-row {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 10px;
    background: rgba(0, 0, 0, .3);
    border: 1px solid #33291a;
    border-radius: 8px;
    margin-bottom: 6px;
}

.drawer-row i {
    color: #7fe08a;
    font-style: normal;
    font-size: 11px;
}

/* v0.6.5.9 item 16: portrait PAGES the stage sideways — the paging block
   lives at the END of this file so it outranks the shared band rules */

@media (max-height: 500px),
(max-width: 980px) and (max-height: 760px),
(max-width: 700px) and (orientation: portrait) {

    /* v0.6.5.7 item 19: the open hand rides OVER the stage; picking or
       dragging a card folds it (HandDock does the folding).
       v0.6.5.8 item 15: the FOLDED dock pins too — in-flow it slid under
       the screen's bottom edge with no way to pull the hand back */
    #hand-dock {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 85;
        background: rgba(10, 7, 4, .97);
    }


    /* v0.6.5.6 item 5: the drawer buttons take their cluster seats — the
       corner reads ERA · players · chronicle · day/night, all 32px squares */
    #mobile-toggles {
        position: fixed;
        top: 4px;
        right: 44px;
        left: auto;
        display: flex;
        gap: 6px;
        z-index: 96;
    }

    #mobile-toggles .mob-btn {
        width: 32px;
        height: 32px;
        padding: 0;
        margin: 0;
        border-radius: 8px;
        border: 1px solid var(--gold-dim);
        background: rgba(18, 13, 7, .92);
        font-size: 15px;
        display: flex;
        align-items: center;
        justify-content: center;
    }



    /* v0.6.5.5 review F4: drawer mode ignores the fold — a folded rail
       opened as a drawer was a 30px sliver */
    #left-rail.folded {
        width: auto;
    }

    #left-rail.folded .rail-body {
        display: flex;
    }

    #left-rail .rail-fold-btn,
    #left-rail .rail-fold-label {
        display: none;
    }


    /* v0.6.5.8 item 2: the small screen drops the "Your Day" words — the
       End Day button and the controls keep the whole row */
    .day-title-name {
        display: none;
    }

    /* v0.6.5.8 item 11: foldables and squarish screens may exceed the
       stage — the play area scrolls vertically over min-height sections */
    #center-stage {
        overflow-y: auto;
        scrollbar-width: thin;
    }


    /* v0.6.5.4 review F1: on phones the JS fitter (useStageFit) rules card
       size — the desktop cq formula on the grid's children yields to the
       inherited inline vars here */
    #stage-grid>* {
        --card-w: inherit;
        --card-h: inherit;
    }

    /* v0.6.5.5 items 9/10: the desktop split rules mobile too — the scroll
       regions now carry min-height floors, so End Day stays pinned and
       visible even at 375px tall */

    #mobile-toggles {
        display: flex;
    }

    /* the rail becomes a slide-over drawer (display-toggled: reliable everywhere) */
    #left-rail {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 330;
        width: min(320px, 78vw);
        background: rgba(18, 13, 7, .98);
        border-right: 1px solid #4a3c22;
        padding: 10px;
    }

    body.rail-open #left-rail {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }

    .main-grid {
        grid-template-columns: 1fr !important;
    }

    /* v0.6.5.7 items 20/21: the header FITS its buttons, the brand stays
       (one line), and the players return as sigils with a done-check */
    .topbar {
        padding: 2px 150px 2px 8px;
        min-height: 40px;
        gap: 8px;
    }

    .brand.stacked {
        font-size: 11px;
        letter-spacing: 1px;
    }

    .brand.stacked span {
        display: inline;
        font-size: 1em;
        letter-spacing: 1px;
    }

    .players-strip {
        display: flex;
        gap: 4px;
    }

    .players-strip .player-chip {
        padding: 1px 5px;
        font-size: 12px;
        gap: 2px;
    }

    /* v0.6.5.9 item 1: MY economy stays in the header at every size —
       only my name yields; rivals stay sigils (their popup carries it) */
    .players-strip .player-chip:not(.mine)>span:not(.fac-ico):not(.p-fate):not(.p-crown) {
        display: none;
    }

    .players-strip .player-chip.mine>.p-name {
        display: none;
    }

    .era-badge img {
        width: 26px;
        height: 26px;
    }

    .shift-badge {
        font-size: 12px;
        padding: 2px 8px;
    }

    /* stage: tighter gaps; the measured fitStage() sizes the cards */
    #stage-grid {
        gap: 8px;
    }

    /* v0.6.6.1 item 6: the small draggable divider between the battle board
       and the day panel — landscape/foldable only (portrait pages instead,
       its block at the file's end hides the handle). Dragging writes
       --split; until then max-content keeps today's content-sized board. */
    #stage-grid.divided {
        grid-template-columns: minmax(110px, var(--split, max-content)) 14px minmax(0, 1fr);
    }

    #stage-grid.divided #stage-divider {
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: col-resize;
        touch-action: none;
        color: #8a7550;
        border-radius: 7px;
        background: rgba(0, 0, 0, .25);
        border: 1px solid rgba(74, 60, 34, .55);
    }

    #stage-grid.divided #stage-divider span {
        font-size: 15px;
        line-height: 1;
        pointer-events: none;
    }

    .board {
        padding: 6px;
        gap: 5px;
    }

    .board .board-col {
        gap: 5px;
    }

    .rpane {
        padding: 8px 10px;
    }

    .loc-card {
        padding: 6px 8px;
    }

    .loc-card span {
        font-size: 15px;
    }

    .loc-card b {
        font-size: 12px;
    }

    .loc-card i {
        font-size: 10px;
    }

    .panel-title {
        font-size: 14px;
    }

    .hint {
        font-size: 10.5px;
    }

    .btn {
        padding: 7px 10px;
        font-size: 12px;
    }

    /* the hand: one always-visible row of touchable cards */
    /* v0.6.5.6 item 3: 128px is the floor of readability, phones included */
    #hand-dock {
        --card-w: 128px;
        --card-h: calc(128px * 1.4);
        padding: 0 8px 0;
    }

    /* v0.6.5.9 item 6: day and night share ONE hand size — the night
       override is gone (45-hand.css dropped the desktop shrink too) */

    /* v0.6.5.5 item 8: the tab row survives — it carries the folding eye */
    .dock-tabs {
        display: flex;
    }

    .dock-tab {
        font-size: 11px;
        padding: 2px 8px;
    }

    /* review F3: the base headroom pair (padding-top + equal negative
       margin) must survive here — only the sides are trimmed */
    .hand-row {
        padding-left: 8px;
        padding-right: 8px;
        padding-bottom: 2px;
    }

    /* v0.6.5.7 item 2: small screens pop hovered cards too (rule removed) */

    /* item 15: the stage reserves the folded strip's height so the fixed
       dock never buries the phase controls at the panel's foot */
    .main-grid {
        padding: 2px 6px 78px !important;
        gap: 6px !important;
    }

    #board-actions {
        min-height: 0;
    }

    #stage-board {
        gap: 3px;
    }

    /* Corner-button SIZE on touch — separate from visibility, so a button
       that is shown is always the right size.
       v0.6.8.1 item 1.3 corner law — discard LEFT, channel/blood RIGHT */
    .channel-btn,
    .discard-btn,
    .blood-btn {
        width: 32px;
        height: 32px;
        font-size: 15px;
        top: -1px;
        right: -1px;
        left: auto;
    }

    .discard-btn {
        right: auto;
        left: -1px;
    }

    /* v0.6.8.3: corner buttons follow the ACTIVE card, not every card.
       Touch has no hover, so v0.5 revealed them unconditionally — which
       meant a phone showed a 🗑 on every card in hand and on the board at
       once. The tapped card is touch's answer to hover: hand cards mark
       themselves `.selected`, board units `.slot.acting`. */
    .hand-row .card.selected .channel-btn,
    .hand-row .card.selected .discard-btn,
    .slot.acting .card .channel-btn,
    .slot.acting .card .discard-btn,
    /* a corpse is never the "acting" slot, and clearing it is the only thing
       its card can do — scoping it to selection would strand dead mercs and
       summons on the field with no way to sweep them */
    .card.fallen .discard-btn,
    /* an armed confirmation must never vanish mid-question */
    .channel-btn.armed,
    .discard-btn.armed {
        display: flex;
    }

    .turn-btn {
        width: 34px;
        height: 34px;
        font-size: 16px;
    }

    /* popovers go near-fullscreen (the combat strip is gone — item 3) */
    .popover-frame {
        width: 96vw;
        max-height: 92vh;
        padding: 10px 12px;
    }
}

#stage-grid.compact-cards .c-die {
    transform: scale(.6);
    transform-origin: top right;
}

#stage-grid.compact-cards .slot-label {
    font-size: 9px;
}

@media (max-height: 500px),
(max-width: 980px) and (max-height: 760px),
(max-width: 700px) and (orientation: portrait) {

    /* review F5: sizing lives in the items-20/21 block above — only the
       nowrap survives here */
    .topbar {
        flex-wrap: nowrap;
    }

    .era-badge img {
        width: 22px;
        height: 22px;
        display: block;
    }

    #hand-dock {
        --card-w: 128px;
        --card-h: calc(128px * 1.4);
    }

    /* review F3: the headroom padding-top must survive — sides only */
    .hand-row {
        padding-left: 8px;
        padding-right: 8px;
        padding-bottom: 1px;
    }
}

@media (max-height: 500px),
(max-width: 980px) and (max-height: 760px),
(max-width: 700px) and (orientation: portrait) {
    .era-badge {
        width: 26px;
        height: 26px;
        overflow: hidden;
    }

    .hand-row {
        min-height: 0;
    }
}

/* ==========================================================================
   v0.6.5.9 item 16: portrait PAGES the stage sideways — the battle board
   and the day panel (or enemy ranks) are full-height columns; the player
   swipes between them. Last in the file: these rules must outrank the
   shared small-screen band above.
   ========================================================================== */
@media (max-width: 700px) and (orientation: portrait) {
    #stage-grid {
        display: flex;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        /* the cq experiment yields to the JS fitter on phones */
        container-type: normal;
        flex: 1 1 auto;
        min-height: 0;
        height: auto;
        gap: 10px;
        scrollbar-width: none;
    }

    #stage-grid>* {
        flex: 0 0 88vw;
        scroll-snap-align: center;
        min-height: 0;
    }

    /* the page never scrolls vertically — each column owns its own scroll */
    #center-stage {
        overflow-y: hidden;
    }

    #stage-right {
        overflow-y: auto;
        scrollbar-width: thin;
    }

    /* the boards center in their pages so day and combat sit identically */
    #my-board,
    #enemy-board {
        margin: auto;
    }

    /* v0.6.6.1 item 6: portrait PAGES the stage — there is no split to
       drag, so the divider handle yields its flex page entirely */
    #stage-grid > #stage-divider {
        display: none;
        flex: 0 0 0;
    }
}

/* ---------------------------------------------------------------- v0.6.8.2
   item 10: the guide, sized for a phone. It had NO mobile rule at all, so a
   desktop `bottom: 148px` dropped it mid-board over a 179px hand dock and the
   full-sentence steps wrapped to six lines across 92vw. */
@media (max-width: 700px) and (orientation: portrait) {
    .tutorial-banner {
        bottom: calc(var(--hand-h, 240px) + 8px);
        max-width: calc(100vw - 24px);
        padding: 8px 30px 8px 10px;
        font-size: 12px;
        gap: 6px;
    }

    .tut-close {
        top: -10px;
        right: -10px;
        width: 28px;
        height: 28px;
        font-size: 13px;
    }
}

/* ---------------------------------------------------------------- v0.6.8.2
   item 7: PHONE PORTRAIT keeps ONE corner control.
   The cluster used to read ERA · players · chronicle · day/night — four
   targets in the width of a thumb. Portrait now hides the separate shift
   badge (the menu button wears the sun/moon) and the standalone chronicle
   button (the menu holds that door), leaving the era badge and the menu. */
/* Review finding: MobileChrome renders ONE menu button wearing the shift in
   EVERY mobile band, but this used to hide the separate .shift-badge in
   portrait alone — so a landscape phone or a foldable showed two suns with a
   hole between them. The badge and the tint follow the button's own scope. */
@media (max-height: 500px),
(max-width: 980px) and (max-height: 760px),
(max-width: 700px) and (orientation: portrait) {
    .topbar .shift-badge {
        display: none;
    }

    #mobile-toggles .mob-menu {
        width: 34px;
        height: 34px;
        font-size: 17px;
    }

    /* the sun/moon reads at a glance: the shift tints its own button */
    #mobile-toggles .mob-menu.night {
        border-color: #4a5f8a;
        background: rgba(14, 18, 30, .94);
    }

    #mobile-toggles .mob-menu.day {
        border-color: #8a6b2f;
        background: rgba(28, 20, 8, .94);
    }
}

@media (max-width: 700px) and (orientation: portrait) {
    /* the menu's doors sit under the table, thumb-sized */
    #players-drawer .drawer-actions {
        display: flex;
        gap: 8px;
        margin-top: 10px;
        padding-top: 10px;
        border-top: 1px solid rgba(255, 255, 255, .12);
    }

    #players-drawer .drawer-actions .btn {
        flex: 1 1 0;
        min-width: 0;
        padding: 9px 6px;
    }
}

/* ---------------------------------------------------------------- v0.6.8.4
   item 3: the phone's ONE menu moves INTO the top bar.

   It used to float over the board in #mobile-toggles, which forced the bar to
   reserve 150px of right padding for a button that was not even its own. Now
   the button is a flex child that ends the row against the era badge, so the
   bar only reserves the badge's own corner (42px) and every pixel it gives
   back goes to the player chips. */
.topbar-menu {
    display: none;
}

@media (max-height: 500px),
(max-width: 980px) and (max-height: 760px),
(max-width: 700px) and (orientation: portrait) {
    .topbar {
        padding: 2px 42px 2px 8px;
        gap: 3px;
    }

    /* the gear yields its seat — one door, not two */
    .topbar .sound-toggle {
        display: none;
    }

    .topbar .topbar-menu {
        display: flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        width: 32px;
        height: 32px;
        padding: 0;
        margin: 0;
        border-radius: 8px;
        font-size: 16px;
        --card-w: inherit;
        --card-h: inherit;
    }

    /* the shift tints the button that opens the menu naming it */
    .topbar .topbar-menu.night {
        border-color: #4a5f8a;
        background: rgba(14, 18, 30, .94);
    }

    .topbar .topbar-menu.day {
        border-color: #8a6b2f;
        background: rgba(28, 20, 8, .94);
    }

    /* the realm menu itself: shift · players · settings, one column */
    .realm-menu {
        max-width: min(300px, calc(100vw - 16px));
        max-height: calc(100dvh - 56px);
        overflow-y: auto;
    }

    .realm-menu .menu-shift {
        font-size: 14px;
        padding: 4px 6px;
        border-radius: 8px;
        border: 1px solid var(--gold-dim);
        text-align: center;
    }

    .realm-menu .menu-shift.night {
        background: rgba(14, 18, 30, .8);
    }

    .realm-menu .menu-shift.day {
        background: rgba(28, 20, 8, .8);
    }

    .realm-menu .menu-players {
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    .realm-menu .menu-player {
        display: flex;
        flex-direction: column;
        gap: 1px;
        padding: 4px 6px;
        border-radius: 6px;
        background: rgba(255, 255, 255, .05);
        font-size: 12px;
        line-height: 1.35;
    }

    .realm-menu .menu-player .prod {
        font-size: .82em;
        opacity: .75;
        font-style: normal;
    }

    .realm-menu .menu-rule {
        width: 100%;
        border: 0;
        border-top: 1px solid rgba(255, 255, 255, .14);
        margin: 2px 0;
    }
}
/* ==========================================================================
   Library & popover rules that must outrank the mobile block
   ========================================================================== */

/* item 38: the card library on the main screen */
.library-frame {
    width: min(1180px, 94vw);
    max-height: 88vh;
    display: flex;
    flex-direction: column;
}

.library-tabs {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    margin: 6px 0 10px;
}

/* v0.6.6.3 item 17: every tab wears its faction's box-side banner under a
   dark wash; the open tab lifts the wash and takes the gold frame */
.lib-tab {
    position: relative;
    background-size: cover;
    background-position: center 35%;
    box-shadow: inset 0 0 0 999px rgba(12, 8, 4, .68);
    text-shadow: 0 1px 3px rgba(0, 0, 0, .95), 0 0 6px rgba(0, 0, 0, .8);
    color: #e8dcbd;
}

.lib-tab.primary {
    box-shadow: inset 0 0 0 999px rgba(12, 8, 4, .34), 0 0 10px rgba(212, 169, 75, .35);
    color: #fff;
}

.lib-tab[data-libtab="holy_kingdom"] { background-image: url("/assets/covers/strip_hk.jpg"); }
.lib-tab[data-libtab="desolate_throne"] { background-image: url("/assets/covers/strip_dt.jpg"); }
.lib-tab[data-libtab="verdant_alliance"] { background-image: url("/assets/covers/strip_va.jpg"); }
.lib-tab[data-libtab="burning_covenant"] { background-image: url("/assets/covers/strip_bc.jpg"); }
.lib-tab[data-libtab="spells"],
.lib-tab[data-libtab="wilds"],
.lib-tab[data-libtab="legends"] { background-image: url("/assets/covers/box.jpg"); }

/* v0.6.5.1 item 21: one scroll for the whole tab; sections inside */
.library-scroll {
    overflow-y: auto;
    scrollbar-width: thin;
    padding: 4px;
    min-height: 200px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    /* the library gives every face a little extra parchment — 150px is the
       tier-system floor; smaller clips the wordiest saga faces */
    --card-w: 150px;
    --card-h: calc(150px * 1.4);
}

.library-section > .rail-title {
    margin-bottom: 6px;
    border-bottom: 1px solid #33291a;
    padding-bottom: 3px;
}

.library-grid.flat {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* the evolution forest: each root grows a left-to-right tree; a vertical
   fork in .evo-children is a CHOICE of evolution (item 21) */
.evo-forest {
    display: flex;
    flex-direction: column;
    gap: 14px;
    overflow-x: auto;
    scrollbar-width: thin;
    padding-bottom: 4px;
}

.evo-node {
    display: flex;
    align-items: center;
    gap: 4px;
}

.evo-node > .card {
    flex: 0 0 auto;
}

.evo-arrow {
    color: var(--gold-dim);
    font-size: 20px;
    flex: 0 0 auto;
}

.evo-children {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding-left: 2px;
    border-left: 1px dashed #3a2f1c;
}


/* v0.6.7.4 item 21.11: the library wears the OPEN faction's realm — the
   cover art breathes behind the shelves under a heavy readable wash */
.library-frame {
    background-size: cover;
    background-position: center 20%;
}

.library-frame[data-libtab="holy_kingdom"] {
    background-image: linear-gradient(rgba(14, 10, 5, .93), rgba(14, 10, 5, .96)), url("assets/covers/hk.jpg");
}

.library-frame[data-libtab="desolate_throne"] {
    background-image: linear-gradient(rgba(10, 8, 12, .93), rgba(10, 8, 12, .96)), url("assets/covers/dt.jpg");
}

.library-frame[data-libtab="verdant_alliance"] {
    background-image: linear-gradient(rgba(8, 12, 8, .93), rgba(8, 12, 8, .96)), url("assets/covers/va.jpg");
}

.library-frame[data-libtab="burning_covenant"] {
    background-image: linear-gradient(rgba(14, 7, 4, .93), rgba(14, 7, 4, .96)), url("assets/covers/bc.jpg");
}

.library-frame[data-libtab="spells"],
.library-frame[data-libtab="wilds"],
.library-frame[data-libtab="legends"] {
    background-image: linear-gradient(rgba(12, 9, 5, .93), rgba(12, 9, 5, .96)), url("assets/covers/box.jpg");
}
/* ==========================================================================
   THE CARD — one component, one face, everywhere.
   Flattened from the v0.2 base + v0.6.1/2/3/4.x layers (WP-89): every rule
   below is the FINAL computed layer; there are no override chains left.
   Text-length tiers: base (<= 26 chars) / .wordy (27-120) / .saga (> 120).
   Placed AFTER 70-mobile.css on purpose — plain-selector face rules must
   outrank the mobile block's like-for-like rules, as they always have.
   ========================================================================== */
/* ==========================================================================
   THE CARD — one component, one size, everywhere (req 4)
   ========================================================================== */
.card {
    /* v0.6.5.6 items 14/15: the face is LAID OUT at its 132px design size
       and zoomed to the slot — level, type, targeting, immunity icons and
       every line of text scale in lockstep, at ANY viewport */
    /* shrinking only: below 132px the face lays out at design size and
       zooms down whole; at 132px and above the classic growth path rules
       (zoom 1 — text wrapping and the tier fits stay byte-identical) */
    --cwo: var(--card-w);
    /* typed division (length/length -> number) makes min() unit-consistent —
       review F1: the untyped /132 kept a LENGTH and the min() was invalid,
       silently pinning zoom to 1 */
    --cs: min(1, calc(var(--card-w) / 132px));
    zoom: var(--cs);
    width: max(132px, var(--card-w));
    /* v0.6.5.7 item 14: the RATIO is law — width can't be squeezed (flex
       none) and height is pinned to it outright; aspect-ratio guards any
       context that overrides one dimension */
    height: calc(max(132px, var(--card-w)) * 1.4);
    aspect-ratio: 132 / 184.8;
    flex: none;
    position: relative;
    border-radius: 9px;
    /* v0.6.4.4 item 13: the card itself no longer clips — corner action
       buttons overflow its edge; the face clips inside .c-clip instead */
    user-select: none;
    background: #221a0f;
    color: var(--parchment);
    box-shadow: 0 3px 10px rgba(0, 0, 0, .6);
    display: flex;
    flex-direction: column;
}

/* inside the face every measurement speaks the LAID-OUT size — the slot's
   raw width stays outside, read only by the zoom above. ONLY the two vars
   live here (review F3: anything else overrides every child's own styles) */
.card > * {
    --card-w: max(132px, var(--cwo));
    --card-h: calc(max(132px, var(--cwo)) * 1.4);
}

.card .c-clip {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: 9px;
}

.card .c-frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: fill;
    opacity: .96;
    pointer-events: none;
}

.card .c-inner {
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: 5px 6px;
}

.card.dead {
    filter: grayscale(1) brightness(.45);
}

/* v0.6.7.4 item 5: a unit the Ancient Boon cast out — the fallen's layout,
   but paler than death and marked by the dove, never the skull */
.card.boon-removed {
    filter: grayscale(.85) brightness(.7);
}

.card.boon-removed::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 6;
    background: url("assets/icons/spell/boon.png") center 40% / 46px no-repeat;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .85));
    pointer-events: none;
}

.card.dead::after {
    content: "\1F480";
    position: absolute;
    inset: 0;
    z-index: 6;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 52px;
    filter: drop-shadow(0 3px 8px #000);
    animation: none;
}

@keyframes skull-in {
    0% {
        transform: scale(2.2);
        opacity: 0;
    }
}

.card.merc .c-clip::after {
    content: "MERC";
    position: absolute;
    top: 38%;
    right: -14px;
    transform: rotate(35deg);
    background: rgba(160, 40, 30, .92);
    color: #ffd;
    font-size: 9px;
    padding: 1px 18px;
    z-index: 5;
    font-family: 'Cinzel', serif;
    letter-spacing: 1px;
}

/* corner stats */
.c-corners-top,
.c-corners-bottom {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
}

.c-corners-bottom {
    margin-top: auto;
    align-items: flex-end;
}

.c-stat img {
    width: 17px;
    height: 17px;
    object-fit: contain;
    filter: drop-shadow(0 1px 2px #000);
}

.c-stat.hurt b {
    color: #ff9a8a;
}

.c-stat.buffed b {
    color: #a5f08c;
}

/* the level medallion (XP_token_1..5) rides centered on the card's top edge;
   moon tokens (x2 massive, -1 devolved) line up on its RIGHT (v0.6.4.3) */
.c-lvl {
    position: absolute;
    top: 2px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 4;
    /* v0.6.4.7 item 8: the medallion grew 25% (26 -> 33) */
    width: 33px;
    height: 33px;
    object-fit: contain;
    filter: drop-shadow(0 1px 3px #000);
}

/* v0.6.4.7 item 9: the medallion glows with the card's epicness — the
   classic CCG rarity ramp; guardians & bosses pulse (see .legend) */
.card.r2 .c-lvl {
    filter: drop-shadow(0 0 4px rgba(87, 211, 100, .9)) drop-shadow(0 1px 3px #000);
}

.card.r3 .c-lvl {
    filter: drop-shadow(0 0 4px rgba(74, 168, 255, .95)) drop-shadow(0 1px 3px #000);
}

.card.r4 .c-lvl {
    filter: drop-shadow(0 0 5px rgba(194, 107, 255, .95)) drop-shadow(0 1px 3px #000);
}

.card.r5 .c-lvl {
    filter: drop-shadow(0 0 3px rgba(255, 200, 80, 1)) drop-shadow(0 0 7px rgba(255, 152, 0, .85)) drop-shadow(0 1px 3px #000);
}

.card.legend .c-lvl {
    animation: lvl-pulse 2.2s ease-in-out infinite;
}

@keyframes lvl-pulse {

    0%,
    100% {
        filter: drop-shadow(0 0 3px rgba(255, 200, 80, .9)) drop-shadow(0 1px 3px #000);
    }

    50% {
        filter: drop-shadow(0 0 6px rgba(255, 80, 40, .95)) drop-shadow(0 0 10px rgba(255, 170, 60, .8)) drop-shadow(0 1px 3px #000);
    }
}

.c-moons {
    position: absolute;
    top: 5.5px;
    /* Anton's kiss position (v0.6.4.8 item 1): the moon tucks onto the
       medallion's right half */
    left: calc(50% + 1px);
    z-index: 4;
    display: flex;
    gap: 0;
    align-items: center;
}

.c-moons img {
    /* item 8: moons grew with the medallion; item 12: all the same size */
    width: 25px;
    height: 25px;
    object-fit: contain;
    filter: drop-shadow(0 1px 2px #000);
}

.c-moons img+img {
    margin-left: -4px;
}

.c-name-row img {
    width: 25px;
    height: 25px;
    object-fit: contain;
    filter: drop-shadow(0 1px 2px #000);
    flex: 0 0 auto;
    /* the icon grew ~70% (v0.6.4.5 item 13) — negative margins keep its
       center exactly where the 15px icon sat, overflowing the name plate */
    margin: -3px -5px;
}

/* ability text — always printed (req 4.9) */
.c-text {
    font-size: 8.5px;
    line-height: 1.25;
    text-align: center;
    color: #e2d5b2;
    background: rgba(10, 7, 3, .58);
    border-radius: 5px;
    padding: 2px 3px;
    margin-top: 2px;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: visible;
    margin-left: 5%;
    margin-right: 5%;
    min-height: 12px;
}

.c-text:empty {
    visibility: hidden;
}

/* defense row: hp, armor, wards, immunity (req 4.11) */
.c-defense {
    display: flex;
    align-items: center;
    gap: 3px;
    margin-top: auto;
    margin-bottom: 1px;
    min-height: 15px;
}

.c-armor {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    background: url("assets/icons/status/armor.png") center/contain no-repeat;
    /* v0.6.4.7 item 7: the count reads bigger and bold */
    font-size: 11px;
    font-weight: 800;
    color: #101623;
    padding-top: 2px;
    text-shadow: 0 0 2px rgba(255, 255, 255, .8);
}

.c-ward {
    position: relative;
    display: inline-flex;
    width: 17px;
    height: 17px;
    border-radius: 50%;
    border: 1.5px solid #a8c8ea;
    background: rgba(20, 30, 50, .55);
    box-shadow: 0 0 6px rgba(138, 180, 221, .95), inset 0 0 3px rgba(138, 180, 221, .6);
    align-items: center;
    justify-content: center;
}

.c-ward img {
    width: 15px;
    height: 15px;
    object-fit: contain;
}

/* v0.6.4.8 items 3/13: statuses, wards and immunities rise from the card's
   bottom frame in up to two columns — positives/defenses beside the HP
   plate, afflictions beside the SPD plate; stat diamonds keep their own
   corners and are never overlapped */
.fx-col {
    position: absolute;
    /* v0.6.4.9 item 1: defenses lie in a horizontal ROW, 8px off the bottom
       (Anton's anchor), instead of stacking up */
    bottom: 8px;
    display: flex;
    flex-direction: row;
    gap: 2px;
    z-index: 4;
    align-items: center;
    flex-wrap: wrap-reverse;
    max-width: calc(var(--card-w) * 0.4);
}

.fx-col.pos {
    left: calc(var(--card-w) * 0.3);
    justify-content: flex-start;
}

.fx-col.neg {
    right: calc(var(--card-w) * 0.3);
    justify-content: flex-end;
}

.fx-status {
    width: 17px;
    height: 17px;
    object-fit: contain;
    filter: drop-shadow(0 1px 2px #000);
}

.fx-glyph {
    width: 17px;
    height: 17px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    text-shadow: 0 1px 2px #000;
}

/* v0.6.4.7 item 4: immunity = the source icon under a 🚫 overlay —
   slightly larger than the other badges (v0.6.4.8 item 9) */
.fx-immune {
    position: relative;
    width: 19px;
    height: 19px;
    display: inline-flex;
}

.fx-immune img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    filter: drop-shadow(0 1px 2px #000);
}

.fx-immune::after {
    content: "🚫";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    opacity: .4;
    /* text-shadow: 0 0 1px #000; */
}

/* buff/debuff diamonds at their stat corners (restored, v0.6.4.8 item 3) */
.c-diamonds {
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 3px;
    z-index: 4;
    align-items: center;
}

.c-diamonds.tl {
    top: 26px;
    left: 8px;
}

.c-diamonds.tr {
    top: 26px;
    right: 8px;
}

.c-diamonds.bl {
    bottom: 26px;
    left: 8px;
    flex-direction: column-reverse;
}

.c-diamonds.br {
    bottom: 26px;
    right: 8px;
    flex-direction: column-reverse;
}

.diamond {
    width: 15px;
    height: 15px;
    transform: rotate(45deg);
    border-radius: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, .5);
    box-shadow: 0 1px 3px #000;
}

.diamond span {
    transform: rotate(-45deg);
    font-size: 8.5px;
    font-weight: 700;
    color: #fff;
    text-shadow: 0 1px 1px #000;
}

.diamond.buff {
    background: linear-gradient(135deg, #3f7a34, #71b757);
}

.diamond.debuff {
    background: linear-gradient(135deg, #8c2b21, #c95545);
}

/* hp bar */
.hp-bar {
    height: 3.5px;
    background: #40100a;
    border-radius: 2px;
    overflow: hidden;
    margin-top: 1px;
}

.hp-bar i {
    display: block;
    height: 100%;
    background: linear-gradient(90deg, #b8433a, #e0743a);
    transition: width .4s;
}

/* spell card variant: clean bottom (req 4.13) */
.card.spell .c-defense,
.card.spell .c-corners-bottom,
.card.spell .hp-bar {
    visibility: hidden;
}

.card.spell .c-text {
    -webkit-line-clamp: 6;
}

/* damage flash on hit */
.card.hit-flash {
    animation: hit-shake .85s;
}

.card.heal-flash {
    animation: heal-glow 1.2s;
}

.card.deny-shake {
    animation: deny .3s;
}

/* card-in-flight (FLIP animation) */
.card-flight {
    position: fixed;
    z-index: 450;
    pointer-events: none;
    transition: all .42s ease-out;
}

/* v0.3.2 --------------------------------------------------------------- */
/* item 6: a healer's "attack" is the heal amount */
.c-stat.heal-out b {
    color: #7fe08a !important;
    margin: 0 -2px;
}

.card:hover .discard-btn,
.card:hover .blood-btn {
    display: flex;
}

/* v0.6.5.6 item 14: the compact micro-layout is gone — the zoomed face
   keeps every element at any size (the class still flips; nothing listens) */
/* the night hand renders 92px pristine cards — same verdict */
body.night-theme #hand-dock .fx-col {
    display: none;
}

/* artless faces keep the legible legacy pill (dark ink has no parchment there) */
.card:not(.has-art) .c-text {
    color: #e2d5b2;
    background: rgba(10, 7, 3, .58);
    text-shadow: none;
    font-weight: 400;
}

.card.has-art.spell .c-text {
    -webkit-line-clamp: 6;
}

/* item 20: very long effect texts shrink instead of clipping */
.card .c-text.long {
    font-size: 7px;
    line-height: 1.15;
    -webkit-line-clamp: 8;
}

/* ==================================================== v0.6.3 client pass */
/* item 17: the unit medallion drops ~5% to sit inside the frame's ornament
   ring (tuned against screenshots of the ring frame) */
.card .c-art {
    position: absolute;
    object-fit: cover;
    pointer-events: none;
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    height: auto;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    box-shadow: inset 0 0 16px rgba(0, 0, 0, .7);
    top: 6.8%;
    width: 79%;
}

.card.has-art .c-name-row {
    background: rgba(12, 8, 4, .55);
    border-radius: 4px;
    margin-top: calc(var(--card-h) * 0.525);
    padding: 0 3px;
}

/* name row: type icon | name | targeting icon */
.c-name-row {
    display: flex;
    align-items: center;
    margin-top: 10px;
    gap: 2px;
}

/* issue 9: ability text prints in dark ink straight on the parchment */
/* v0.6.4.4 items 11 + 12: a few px narrower per side, and the content
   pass's line breaks are honored */
.card .c-text {
    color: #2a1a0c;
    background: none;
    text-shadow: none;
    font-weight: 600;
    padding: 1px 11px;
    display: block;
    overflow: hidden;
    -webkit-line-clamp: unset;
    flex: 1 1 auto;
    min-height: 0;
    font-size: 9px;
    line-height: 1.22;
    white-space: pre-line;
}

/* item 10: damage-source words render as their icons on the face */
.c-text .src-inline {
    width: 10px;
    height: 10px;
    object-fit: contain;
    vertical-align: -2.5px;
    margin: 0 1px;
}

.card.wordy .c-art {
    top: 5%;
    width: 62%;
}

.card.has-art.wordy .c-name-row {
    margin-top: calc(var(--card-h) * 0.40);
}

.card.saga .c-art {
    top: 4%;
    width: 42%;
}

.card.has-art.saga .c-name-row {
    margin-top: calc(var(--card-h) * 0.28);
}

/* the decorative hp-bar gave its pixels to the scroll (numbers carry the info) */
.card .hp-bar {
    display: none;
}

.card .c-corners-bottom {
    margin-bottom: 1px;
}

.c-stat {
    display: flex;
    align-items: center;
    gap: 2px;
    font-family: 'Cinzel', serif;
    font-weight: 700;
    text-shadow: 0 1px 3px #000, 0 0 5px #000;
    /* v0.6.4.5 item 12: fixed box + fixed background-size — the plate
       must never stretch, whatever the value's width */
    width: 42px;
    flex: 0 0 auto;
    height: 21px;
    overflow: visible;
    padding: 1px 8px 1px 5px;
    /* v0.6.4.8 item 14: contain — the plate art can never stretch */
    background: center / contain no-repeat;
    background-size: contain;
    font-size: 12px;
    justify-content: flex-start;
}

/* guardians print '2x' — one letter longer than anyone; the attack value
   alone slides 2.5px left to stay on its plate (item 12) */
.card.guardian .c-stat.tl b {
    margin-left: -2.5px;
}

/* item 2: the bottom pair sits 3px lower on its plates */
.c-stat.bl b,
.c-stat.br b {
    padding-top: 1px;
}

.c-stat.tl {
    background-image: url("assets/icons/plate/dmg.png");
    padding: 1px 8px 1px 5px;
}

.c-stat.tr {
    background-image: url("assets/icons/plate/acc.png");
    justify-content: flex-end;
    padding: 1px 21px 1px 8px;
}

.c-stat.bl {
    background-image: url("assets/icons/plate/hp.png");
    padding: 2px 5px 1px 18px;
}

.c-stat.br {
    background-image: url("assets/icons/plate/spd.png");
    justify-content: flex-end;
    padding: 2px 21px 1px 8px;
}

/* the plates carry their own crosshair / heart / boot — drop the doubles */
.c-stat.tr img {
    display: none;
}

/* v0.6.4.8 item 4: the SPD plate shows the speed CHEVRON, not a number */
.c-stat.br .spd-chevron {
    position: relative;
    display: inline-flex;
    align-items: center;
    margin: 0 -2px;
}

.c-stat.br .spd-chevron img {
    display: block;
    width: 23px;
    height: 14px;
    object-fit: contain;
    filter: drop-shadow(0 1px 2px #000);
    padding-left: 7px;
}

.spd-over {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-style: normal;
    font-weight: 800;
    font-size: 12px;
    color: #fff;
    text-shadow: 0 0 3px #000, 0 1px 2px #000;
}

.c-stat.bl b {
    margin-left: 1px;
}

/* the source icon sits IN the DMG plate's medallion circle */
.c-stat.tl img {
    width: 15px;
    height: 15px;
    margin: 0 3px 0 -1px;
}

/* ================================================== v0.6.4.2 client pass */
/* ruling 6 — the face, re-cut again (review variant C, tuned to 0/428):
   the medallion aligns with the frame's ornament ring; the name floats free
   on the parchment's rolled top edge — anchored to the FRAME, not the flex
   flow, so it holds at every card size; the scroll below holds the whole
   effect text. Length tiers change TYPE SIZE only:
     base  (<= 26 chars) 9px · wordy (27-120) 8px · saga (> 120) 6.5px    */
.card .c-art,
.card.wordy .c-art,
.card.saga .c-art {
    top: 7.5%;
    width: 79%;
}

.card.has-art .c-name-row,
.card.has-art.wordy .c-name-row,
.card.has-art.saga .c-name-row {
    position: absolute;
    top: 55%;
    left: 4%;
    right: 4%;
    z-index: 3;
    margin: 0;
    /* v0.6.4.8 item 5: the L1 plate — dark with a gray hint; rarities tint
       it below (the tier variants above share this specificity, so the
       rarity rules must stay AFTER this block) */
    background: rgba(50, 50, 55, .55);
    padding: 0 3px;
}

.c-name {
    flex: 1 1 auto;
    /* v0.6.4.7 item 10: breathing room from the grown side icons — long
       names (Grand Inquisitor, Sanctuary Golem) wrap instead of colliding */
    margin: 0 2px;
    min-width: 0;
    overflow-wrap: break-word;
    text-align: center;
    font-family: 'Cinzel', serif;
    font-weight: 700;
    color: #f4e9cc;
    line-height: 1.05;
    font-size: 10px;
    text-shadow: 0 1px 3px #000, 0 0 6px #000, 0 0 10px #000;
}

.card.saga .c-name {
    font-size: 9.5px;
}

.card.has-art .c-text {
    -webkit-line-clamp: 5;
    margin-top: calc(var(--card-h) * 0.695 - 32px);
    padding: 1px 2px;
}

.card.wordy .c-text {
    font-size: 7.8px;
    line-height: 1.1;
}

.card.saga .c-text {
    margin-top: calc(var(--card-h) * 0.658 - 28px);
    font-size: 6.5px;
    line-height: 1.14;
}

.card .c-defense {
    margin-bottom: 0;
    position: absolute;
    bottom: 23px;
    left: 20px;
    right: 6px;
    z-index: 3;
    min-height: 0;
    margin: 0;
}

/* issue 7: spell art sits square inside the book window of the spell frame */
.card.spell .c-art {
    top: 10%;
    left: 50%;
    transform: translateX(-50%);
    width: 74%;
    height: 44%;
    aspect-ratio: auto;
    border-radius: 6px 6px 4px 4px;
}

/* v0.6.7.4 item 7: the Ancient Boon's frame opens a taller book — the art
   fills its window; the bronze arch crowns the medallion */
.card.boon .c-art {
    top: 11%;
    width: 76%;
    height: 46%;
    border-radius: 8px 8px 5px 5px;
}

/* v0.6.4.8 item 5: the name plate echoes the rarity — dark, a hint of color */
.card.r2.has-art .c-name-row,
.card.r2.has-art.wordy .c-name-row,
.card.r2.has-art.saga .c-name-row {
    background: rgba(22, 58, 30, .58);
}

.card.r3.has-art .c-name-row,
.card.r3.has-art.wordy .c-name-row,
.card.r3.has-art.saga .c-name-row {
    background: rgba(18, 42, 78, .58);
}

.card.r4.has-art .c-name-row,
.card.r4.has-art.wordy .c-name-row,
.card.r4.has-art.saga .c-name-row {
    background: rgba(52, 24, 78, .58);
}

.card.r5.has-art .c-name-row,
.card.r5.has-art.wordy .c-name-row,
.card.r5.has-art.saga .c-name-row,
.card.legend.has-art .c-name-row,
.card.legend.has-art.wordy .c-name-row,
.card.legend.has-art.saga .c-name-row {
    background: rgba(78, 58, 16, .58);
}