/* Zodiverse — global theme.
   Dark cosmos backdrop, gold/violet accents, serif headings (Cinzel-feel). */

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; }

body {
    background: #050310;
    color: #f4e9c1;
    font-family: 'Cinzel', 'Trajan Pro', 'Times New Roman', serif;
    letter-spacing: 0.05em;
    overflow: hidden;
}

/* ─── Splash ─────────────────────────────────────────────────────── */
.splash-body { background: #000; }
.game-frame:has(.splash-stage) { background: radial-gradient(ellipse at top, #1a0f30 0%, #07050f 60%, #000000 100%); }
.splash-stars {
    position: absolute; inset: 0;
    background-image:
        radial-gradient(1px 1px at 12% 18%, #fff 50%, transparent 100%),
        radial-gradient(1px 1px at 28% 72%, #ffe9b3 50%, transparent 100%),
        radial-gradient(1px 1px at 47% 22%, #fff 50%, transparent 100%),
        radial-gradient(2px 2px at 56% 88%, #d6b8ff 50%, transparent 100%),
        radial-gradient(1px 1px at 71% 41%, #fff 50%, transparent 100%),
        radial-gradient(1px 1px at 82% 67%, #ffe9b3 50%, transparent 100%),
        radial-gradient(1px 1px at 91% 14%, #fff 50%, transparent 100%),
        radial-gradient(2px 2px at 19% 52%, #d6b8ff 50%, transparent 100%),
        radial-gradient(1px 1px at 38% 95%, #fff 50%, transparent 100%),
        radial-gradient(1px 1px at 64% 9%, #ffe9b3 50%, transparent 100%);
    background-size: 100% 100%;
    pointer-events: none;
    animation: drift 80s linear infinite;
    opacity: 0.85;
}
@keyframes drift {
    0% { transform: translate3d(0,0,0); }
    100% { transform: translate3d(-3%, -2%, 0); }
}
.splash-stage {
    position: relative; width: 100%; height: 100%;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4vh; z-index: 1;
}
.splash-wordmark { text-align: center; animation: fadeUp 1.2s ease-out both; }
.splash-wordmark h1 {
    font-size: clamp(2.5rem, 8vw, 5rem);
    font-weight: 700;
    letter-spacing: 0.25em;
    background: linear-gradient(180deg, #fff5d6 0%, #daa520 60%, #8b6914 100%);
    -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
    text-shadow: 0 0 40px rgba(218, 165, 32, 0.4);
}
.splash-wordmark p {
    margin-top: 0.5em; font-size: clamp(0.8rem, 2vw, 1.1rem);
    color: #b8a080; letter-spacing: 0.4em; text-transform: uppercase;
}
.splash-loader {
    width: min(280px, 60vw); height: 2px;
    background: rgba(218, 165, 32, 0.15); border-radius: 1px; overflow: hidden;
}
.splash-loader-fill {
    height: 100%; width: 0%;
    background: linear-gradient(90deg, transparent, #daa520, transparent);
    animation: fill 2.2s ease-out forwards;
}
@keyframes fill { from { width: 0%; } to { width: 100%; } }
@keyframes fadeUp {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ─── Map (home) — celestial backdrop with floating constellations ─── */
.map-body { background: #000; }

.map-bg {
    position: absolute; inset: 0;
    background-image: url('/static/images/bg_map.jpg?v=2026063002');
    background-size: cover;
    background-position: center;
    z-index: 0;
}
.map-vignette {
    position: absolute; inset: 0;
    background:
        radial-gradient(ellipse at center, transparent 40%, rgba(5,3,16,0.55) 100%),
        linear-gradient(180deg, rgba(5,3,16,0.6) 0%, transparent 18%, transparent 82%, rgba(5,3,16,0.7) 100%);
    z-index: 1; pointer-events: none;
}

/* Top bar — stardust + avatar */
.map-header {
    position: absolute; top: 0; left: 0; right: 0;
    padding: 0.8rem 1rem;
    display: flex; align-items: center; justify-content: space-between;
    z-index: 10;
}
.map-stardust {
    display: flex; align-items: center; gap: 0.6rem;
    font-size: 1.2rem; font-weight: 600;
    color: #daa520;
    text-shadow: 0 1px 6px rgba(0,0,0,0.8);
}
.stardust-icon {
    width: 32px; height: 32px; object-fit: contain;
    filter: drop-shadow(0 0 8px rgba(218,165,32,0.5));
}
.map-avatar {
    width: 52px; height: 52px; border-radius: 50%; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid rgba(218,165,32,0.5);
    cursor: pointer;
    background: rgba(10,5,24,0.4);
    box-shadow: 0 4px 18px rgba(0,0,0,0.5);
}
.map-avatar img { width: 100%; height: 100%; object-fit: cover; }

/* Constellation field — icons sit on a circle around the zodiac wheel.
   Polar layout: --angle per icon, --radius shared. Sizes are FIXED design
   pixels — the 1920×1080 frame-canvas scales the whole layout uniformly. */
.constellation-field {
    position: relative; z-index: 2;
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    --radius: 380px;     /* circle radius in design pixels */
}
.constellation {
    position: absolute;
    top: 50%; left: 50%;
    /* Polar trick: rotate to angle, translate outward by radius, then counter-rotate to keep icon upright. */
    transform:
        translate(-50%, -50%)
        rotate(var(--angle))
        translate(0, calc(-1 * var(--radius)))
        rotate(calc(-1 * var(--angle)));
    display: flex; flex-direction: column; align-items: center;
    text-decoration: none; color: inherit;
    cursor: pointer;
    transition: transform 0.25s ease-out;
    animation: starpulse 4s ease-in-out infinite;
}
.constellation:nth-child(1) { animation-delay: 0.0s; }
.constellation:nth-child(2) { animation-delay: 0.7s; }
.constellation:nth-child(3) { animation-delay: 1.3s; }
.constellation:nth-child(4) { animation-delay: 2.1s; }
.constellation:nth-child(5) { animation-delay: 2.8s; }
.constellation:nth-child(6) { animation-delay: 3.5s; }

.constellation:hover, .constellation:active {
    transform:
        translate(-50%, -50%)
        rotate(var(--angle))
        translate(0, calc(-1 * var(--radius)))
        rotate(calc(-1 * var(--angle)))
        scale(1.08);
    animation-play-state: paused;
}
.constellation:hover .constellation-icon,
.constellation:active .constellation-icon {
    filter: drop-shadow(0 0 20px rgba(218,165,32,0.85))
            drop-shadow(0 0 6px rgba(255,239,170,0.6));
}

.constellation-icon {
    width: 130px;
    height: 130px;
    object-fit: contain;
    filter: drop-shadow(0 0 14px rgba(218,165,32,0.45))
            drop-shadow(0 2px 8px rgba(0,0,0,0.7));
    transition: filter 0.25s ease-out;
    /* gentle wiggle — runs independent of the outer polar positioning */
    animation: starwiggle 7s ease-in-out infinite;
    will-change: transform, filter;
}
.constellation:nth-child(1) .constellation-icon { animation-delay: 0.3s; }
.constellation:nth-child(2) .constellation-icon { animation-delay: 1.4s; }
.constellation:nth-child(3) .constellation-icon { animation-delay: 0.8s; }
.constellation:nth-child(4) .constellation-icon { animation-delay: 2.5s; }
.constellation:nth-child(5) .constellation-icon { animation-delay: 1.9s; }
.constellation:nth-child(6) .constellation-icon { animation-delay: 3.1s; }

.constellation-label {
    margin-top: 0.55rem;
    font-size: 1rem;
    color: #f4e9c1;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-shadow: 0 1px 6px rgba(0,0,0,0.95), 0 0 12px rgba(218,165,32,0.3);
    white-space: nowrap;
}
.constellation-sub {
    margin-top: 0.2rem;
    font-size: 0.75rem;
    color: #8a7558;
    letter-spacing: 0.1em;
    text-shadow: 0 1px 4px rgba(0,0,0,0.95);
}

/* Twinkle: opacity + brightness ride together for a distant-star shimmer.
   Irregular keyframe spacing fights the metronomic feel of even pulses. */
@keyframes starpulse {
    0%   { opacity: 0.78; filter: brightness(0.88); }
    18%  { opacity: 1;    filter: brightness(1.12); }
    34%  { opacity: 0.85; filter: brightness(0.95); }
    52%  { opacity: 0.95; filter: brightness(1.02); }
    70%  { opacity: 0.72; filter: brightness(0.82); }
    100% { opacity: 0.78; filter: brightness(0.88); }
}

/* Wiggle: floaty drift + sway. Runs on the icon, not the outer
   .constellation (which owns the polar transform — can't share). */
@keyframes starwiggle {
    0%   { transform: translate(0, 0)       rotate(0deg); }
    20%  { transform: translate(6px, -4px)  rotate(2deg); }
    40%  { transform: translate(-4px, 6px)  rotate(-1.5deg); }
    60%  { transform: translate(7px, 5px)   rotate(1.5deg); }
    80%  { transform: translate(-6px, -5px) rotate(-2deg); }
    100% { transform: translate(0, 0)       rotate(0deg); }
}

.map-footer {
    position: absolute; bottom: 0; left: 0; right: 0;
    padding: 0.4rem;
    text-align: center;
    font-size: 0.65rem;
    color: #5a4a30;
    letter-spacing: 0.2em;
    z-index: 5;
    text-shadow: 0 1px 4px rgba(0,0,0,0.9);
}

/* ═══ Generic page shell (Collection / Adventure / Store / etc) ═══════
   Strict 16:9 outer frame. The body letterboxes; .game-frame is the
   actual UI canvas. Everything sized in % / vw-of-frame so it scales. */
.page-body {
    margin: 0;
    width: 100vw;
    height: 100dvh;
    min-height: 100dvh;
    overflow: hidden;
    background: #000;
    display: flex;
    align-items: flex-start;       /* top-align — frame hugs the top, letterbox below */
    justify-content: center;
}
.game-frame {
    position: relative;
    box-sizing: border-box;
    /* Always 16:9 — fit to whichever viewport dimension is the binding constraint.
       One pair of sides ALWAYS touches the viewport edge. */
    width: min(100vw, calc(100dvh * 16 / 9));
    height: min(100dvh, calc(100vw * 9 / 16));
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: #05030f;
    /* No border — the frame edge is invisible; black letterbox is the only signal. */
    border: none;
    box-shadow: 0 0 30px rgba(0,0,0,0.6);
    /* Container query: cqw inside this element = 1% of frame width.
       Enables proportional scaling of the inner canvas below. */
    container-type: size;
}

/* Fixed 1920×1080 reference canvas, uniformly scaled to fill the 16:9 frame.
   Every child renders at its design-pixel value (1920 logical = full width),
   and the whole canvas scales as one. Same UI on a phone or 4K monitor. */
.frame-canvas {
    position: absolute;
    top: 0; left: 0;
    width: 1920px;
    height: 1080px;
    transform-origin: top left;
    transform: scale(calc(100cqw / 1920px));
    display: flex;
    flex-direction: column;
}

.page-bg {
    position: absolute; inset: 0;
    background-image: url('/static/images/bg_map.jpg?v=2026063002');
    background-size: cover; background-position: center;
    z-index: 0;
    opacity: 0.35;
}
.page-vignette {
    position: absolute; inset: 0;
    background: radial-gradient(ellipse at center, transparent 30%, rgba(5,3,16,0.7) 100%);
    z-index: 1; pointer-events: none;
}

.page-header {
    flex: 0 0 auto;
    position: relative;
    display: flex; align-items: center; gap: 1rem;
    padding: 0.6rem 1rem;
    z-index: 20;
    background: linear-gradient(180deg, rgba(5,3,16,0.92) 0%, rgba(5,3,16,0.6) 80%, transparent 100%);
}
.page-back {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; border-radius: 50%;
    background: rgba(218,165,32,0.08);
    border: 1px solid rgba(218,165,32,0.35);
    color: #daa520; text-decoration: none;
    font-size: 1.6rem; line-height: 1;
    transition: background 0.18s, border-color 0.18s;
}
.page-back:hover { background: rgba(218,165,32,0.18); border-color: #daa520; }
.back-chevron { transform: translateX(-1px); }
.page-title {
    flex: 1; font-size: clamp(1rem, 2.4vw, 1.5rem);
    color: #daa520; text-transform: uppercase; letter-spacing: 0.25em;
    text-shadow: 0 1px 6px rgba(0,0,0,0.9);
}
.page-header-right { display: flex; align-items: center; gap: 1rem; }

.page-main {
    position: relative; z-index: 2;
    flex: 1 1 auto;
    min-height: 0;
    padding: 0.5rem 0.75rem;
    overflow: hidden;
}
.game-footer {
    flex: 0 0 auto;
    text-align: right;
    padding: 0.15rem 0.6rem;
    font-size: 0.65rem;
    color: rgba(255, 200, 100, 0.35);
    z-index: 20;
}

/* ─── Placeholder pages ─────────────────────────────────────────────── */
.placeholder {
    display: flex; flex-direction: column; align-items: center;
    text-align: center;
    padding: 6vh 1rem;
    gap: 1.2rem;
}
.placeholder-icon {
    width: clamp(120px, 22vmin, 200px); height: clamp(120px, 22vmin, 200px);
    filter: drop-shadow(0 0 24px rgba(218,165,32,0.4));
    animation: starwiggle 7s ease-in-out infinite;
}
.placeholder-title {
    font-size: clamp(1.6rem, 4vw, 2.5rem); letter-spacing: 0.2em;
    color: #f4e9c1; text-shadow: 0 0 18px rgba(218,165,32,0.35);
}
.placeholder-tag {
    font-size: clamp(0.95rem, 1.6vw, 1.15rem);
    color: #b8a080; max-width: 38ch; line-height: 1.5;
}
.placeholder-sub {
    margin-top: 0.5rem; font-size: 0.85rem;
    color: #6b5a3d; letter-spacing: 0.1em;
}
.placeholder-sub a { color: #daa520; text-decoration: none; }
.placeholder-sub a:hover { text-decoration: underline; }

/* ─── Collection page ───────────────────────────────────────────────── */
.collection-filters {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
    padding: 0.5rem 0 1rem;
    border-bottom: 1px solid rgba(218,165,32,0.15);
    margin-bottom: 1rem;
}
.filter-chip {
    background: rgba(218,165,32,0.06);
    border: 1px solid rgba(218,165,32,0.25);
    color: #c4a574;
    padding: 0.35rem 0.85rem;
    border-radius: 999px;
    font-family: inherit;
    font-size: 0.78rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.filter-chip:hover { background: rgba(218,165,32,0.14); color: #f4e9c1; }
.filter-chip.active {
    background: rgba(218,165,32,0.22);
    border-color: #daa520;
    color: #fff5d6;
    box-shadow: 0 0 12px rgba(218,165,32,0.25);
}
.filter-sep { color: rgba(218,165,32,0.25); margin: 0 0.4rem; }

.collection-summary {
    font-size: 0.85rem; color: #8a7558;
    letter-spacing: 0.12em; text-transform: uppercase;
    margin-bottom: 1.2rem;
}

.collection-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 1.2rem;
}
.card-tile {
    position: relative;
    aspect-ratio: 211 / 327;  /* portrait card proportion (≈ tarot) */
    border-radius: 8px;
    overflow: hidden;
    background: rgba(10,5,24,0.55);
    border: 1px solid rgba(218,165,32,0.18);
    transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
    cursor: pointer;
}
.card-tile:hover {
    transform: translateY(-4px) scale(1.02);
    border-color: rgba(218,165,32,0.6);
    box-shadow: 0 12px 32px rgba(0,0,0,0.55), 0 0 16px rgba(218,165,32,0.2);
    z-index: 5;
}
.card-tile-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-tile-missing {
    width: 100%; height: 100%;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 0.4rem;
    background: linear-gradient(160deg, rgba(43,26,77,0.5), rgba(10,5,24,0.85));
    color: #4a3c20;
}
.missing-sign {
    font-size: 3rem; color: rgba(218,165,32,0.4); letter-spacing: 0.1em;
}
.missing-meta {
    font-size: 0.65rem; letter-spacing: 0.12em; text-transform: uppercase;
    text-align: center; color: #6b5a3d;
}

/* Rarity tints */
.rarity-demi    { box-shadow: 0 0 0 1px rgba(180,180,180,0.15) inset; }
.rarity-god     { box-shadow: 0 0 0 1px rgba(160,200,255,0.25) inset; }
.rarity-supreme { box-shadow: 0 0 0 1px rgba(255,196,120,0.4) inset, 0 0 14px rgba(255,196,120,0.18); }

/* ─── Card detail modal ─────────────────────────────────────────────── */
.card-modal { position: absolute; inset: 0; z-index: 100; display: flex;
    align-items: center; justify-content: center; padding: 2rem;
    opacity: 0; pointer-events: none; transition: opacity 0.2s; }
.card-modal[aria-hidden="false"] { opacity: 1; pointer-events: auto; }
.card-modal-backdrop { position: absolute; inset: 0;
    background: rgba(5,3,16,0.85); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.card-modal-content {
    position: relative; display: flex; gap: 2rem;
    max-width: 900px; width: 100%; max-height: 90vh;
    padding: 2rem;
    background: linear-gradient(160deg, rgba(43,26,77,0.65), rgba(10,5,24,0.95));
    border: 1px solid rgba(218,165,32,0.4);
    border-radius: 12px;
    box-shadow: 0 30px 80px rgba(0,0,0,0.7), 0 0 40px rgba(218,165,32,0.15);
    overflow-y: auto;
}
.card-modal-close {
    position: absolute; top: 0.5rem; right: 0.75rem;
    width: 36px; height: 36px;
    background: transparent; color: #daa520;
    border: none; font-size: 2rem; line-height: 1; cursor: pointer;
    transition: color 0.15s;
}
.card-modal-close:hover { color: #fff5d6; }
.card-modal-art-wrap { flex: 0 0 320px; display: flex; align-items: center; justify-content: center; }
.card-modal-art { max-width: 100%; max-height: 70vh; border-radius: 8px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.7); }
.card-modal-missing { color: #6b5a3d; font-style: italic; text-align: center; padding: 4rem 1rem; }
.card-modal-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1rem; }
.card-modal-id { font-size: 0.75rem; color: #6b5a3d; letter-spacing: 0.18em; text-transform: uppercase; }
.card-modal-title { font-size: clamp(1.2rem, 2.4vw, 1.7rem); color: #f4e9c1;
    letter-spacing: 0.12em; text-transform: uppercase; line-height: 1.3; }
.card-modal-stats { display: flex; gap: 0.6rem; margin-top: 0.5rem; }
.stat-pill {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    padding: 0.6rem 0.4rem;
    background: rgba(218,165,32,0.08);
    border: 1px solid rgba(218,165,32,0.3);
    border-radius: 8px;
}
.stat-label { font-size: 0.7rem; color: #8a7558; letter-spacing: 0.2em; }
.stat-val   { font-size: 1.8rem; color: #daa520; font-weight: 700; line-height: 1.2; }
.stat-name  { font-size: 0.65rem; color: #6b5a3d; letter-spacing: 0.15em; text-transform: uppercase; }

.card-modal-tags { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }
.tag {
    padding: 0.3rem 0.8rem;
    background: rgba(218,165,32,0.06);
    border: 1px solid rgba(218,165,32,0.25);
    border-radius: 999px;
    font-size: 0.72rem; color: #c4a574;
    letter-spacing: 0.12em; text-transform: uppercase;
}
.tag.rarity.rarity-demi    { color: #c4c4c4; border-color: rgba(180,180,180,0.4); }
.tag.rarity.rarity-god     { color: #a0c8ff; border-color: rgba(160,200,255,0.5); }
.tag.rarity.rarity-supreme { color: #ffc478; border-color: rgba(255,196,120,0.6);
    box-shadow: 0 0 10px rgba(255,196,120,0.2); }

/* ═══ Battle screen ═══════════════════════════════════════════════════ */
.battle {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    /* Shared card size. Card art is 1024×1600 (aspect 0.64).
       --card-h is the BINDING dimension (board-row height limits it);
       --card-w is derived so the aspect always matches the art exactly. */
    --card-h: 420px;
    --card-w: calc(var(--card-h) * 1024 / 1600);   /* ≈ 268.8px — exact aspect */
}

/* Battle page hides the global breadcrumb + footer chrome — clean canvas. */
.frame-canvas:has(.battle) .page-header { display: none; }
.frame-canvas:has(.battle) .game-footer  { display: none; }
.frame-canvas:has(.battle) .page-main    { padding: 0; }
.frame-canvas:has(.battle) .page-bg,
.frame-canvas:has(.battle) .page-vignette { display: none; }

/* Surrender white flag, top-right of frame. */
.battle-surrender {
    position: absolute;
    top: 10px; right: 10px;
    z-index: 30;
    width: 44px; height: 44px;
    padding: 0;
    background: rgba(8, 6, 16, 0.85);
    border: 1px solid rgba(218,165,32,0.5);
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: background 0.15s, transform 0.1s;
}
.battle-surrender:hover { background: rgba(80, 20, 20, 0.9); transform: scale(1.06); }
.battle-surrender svg {
    width: 24px; height: 24px;
    fill: none;
    stroke: #fff;
    stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
    filter: drop-shadow(0 1px 3px rgba(0,0,0,0.7));
}
.battle-grid {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: 340px 1fr;            /* wider hero col → bigger hero art */
    grid-template-rows: 1fr;
    gap: calc(var(--card-w) * 0.5);              /* breathing space: half a card wide */
    padding: 0.75rem;
}
.hero-col {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    min-height: 0;
}
.board-stack {
    display: flex;
    flex-direction: column;
    gap: 0;                              /* NO space between the two rows */
    min-height: 0;
    /* Breathing space ABOVE opp row + BELOW player row (only outside).
       Trimmed so 420px-tall slots fit each row without vertical clip. */
    padding-top: 5%;
    padding-bottom: 5%;
}

.hero-portrait {
    flex: 1;                                /* opp + player share the column equally */
    position: relative;
    display: flex; flex-direction: column;
    background: linear-gradient(160deg, rgba(43,26,77,0.6), rgba(10,5,24,0.9));
    border: 1px solid rgba(218,165,32,0.4);
    border-radius: 8px;
    min-height: 0;
    overflow: hidden;
}
.hero-portrait--opp { border-top: 2px solid rgba(255,100,100,0.55); }
.hero-portrait--ply { border-bottom: 2px solid rgba(120,220,120,0.55); }
.hero-img {
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    min-height: 0;
    object-fit: contain;            /* show the whole character art */
    object-position: center center;  /* vertically centered */
    background: transparent;
    border: none;
    border-radius: 0;
    transition: transform 0.12s ease-out;
    transform-origin: center;
}
/* Planets render as full-panel images — no circular clip (the source art
   already paints the sphere). Spin filter stays for life. */
.hero-portrait--planet .hero-img {
    animation: planetTurn 18s linear infinite;
}

/* Hit reactions: humanoid heroes shake; planets spin 360°. Both anims
   target the .hero-img directly so the frame + shadow stay put — only
   the portrait art moves. (Previously the shake was on the whole
   .hero-portrait, which dragged the rectangular shadow with the planet.) */
.hero-portrait.hero-hit .hero-img      { animation: heroShake 600ms ease-out 2; }
.hero-portrait.hero-hit-spin .hero-img { animation: heroSpin 1000ms ease-in-out 1, heroShake 500ms ease-out 1; }
@keyframes heroShake {
    0%, 100% { transform: translate(0, 0); }
    10%      { transform: translate(-4px, 2px) rotate(-1deg); }
    25%      { transform: translate( 5px,-1px) rotate( 1.5deg); }
    40%      { transform: translate(-5px, 1px) rotate(-1.5deg); }
    55%      { transform: translate( 3px, 2px) rotate( 1deg); }
    70%      { transform: translate(-3px,-1px) rotate(-0.5deg); }
    85%      { transform: translate( 2px, 0)  rotate( 0.5deg); }
}
@keyframes heroSpin {
    0%   { transform: rotate(0deg)   scale(1); }
    50%  { transform: rotate(180deg) scale(1.1); }
    100% { transform: rotate(360deg) scale(1); }
}
.hero-portrait--planet .hero-img {
    border-color: rgba(255, 120, 60, 0.7);
    box-shadow: 0 0 18px rgba(255, 100, 40, 0.55), inset 0 0 10px rgba(0,0,0,0.6);
    animation: planetTurn 18s linear infinite;
}
@keyframes planetTurn {
    from { filter: hue-rotate(0deg); }
    to   { filter: hue-rotate(20deg); }
}
/* Name overlay strip at the bottom — no bar, just the name. */
.hero-meta {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 0.35rem 0.55rem;
    background: linear-gradient(180deg, transparent, rgba(5,3,16,0.92) 65%);
    z-index: 2;
}
.hero-name { font-size: 0.85rem; color: #daa520; letter-spacing: 0.1em; text-transform: uppercase; text-shadow: 0 1px 4px rgba(0,0,0,0.9); }
.hero-epithet { color: #8a7558; font-size: 0.62rem; margin-left: 0.3rem; }
/* Rainbow HP bar removed — heart + number is the HP indicator (v0.9.17). */
.hp-bar, .hp-label { display: none; }

/* Heart-with-number HP indicator, top-left corner. */
.hero-hp {
    position: absolute;
    top: 8px; left: 8px;
    width: 96px; height: 88px;
    display: flex; align-items: center; justify-content: center;
    z-index: 3;
    pointer-events: none;
}
.hero-hp-heart {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    fill: #d22;
    filter: drop-shadow(0 2px 4px rgba(0,0,0,0.9)) drop-shadow(0 0 8px rgba(255,80,80,0.55));
}
.hero-hp-num {
    position: relative;
    z-index: 1;
    color: #fff;
    font-weight: 800;
    font-size: 1.7rem;
    letter-spacing: 0.02em;
    text-shadow: 0 1px 3px rgba(0,0,0,0.95);
    transform: translateY(-2px);
}
.hp-fill {
    height: 100%;
    background: linear-gradient(90deg, #d22 0%, #ffa500 60%, #4caf50 100%);
    transition: width 0.4s ease-out;
    box-shadow: 0 0 8px rgba(255,165,0,0.4) inset;
}
.hp-label { font-size: 0.75rem; color: #c4a574; letter-spacing: 0.1em; }

/* Boards: card-sized slots in a row. Scrolls horizontally when there are
   more slots than fit the viewport (canon allows up to VISIBLE_COLUMNS=8). */
.board {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 0.5rem;
    padding: 0.5rem;
    background: rgba(5,3,16,0.55);
    border: 1px solid rgba(218,165,32,0.18);
    border-radius: 8px;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: thin;
    scrollbar-color: rgba(218,165,32,0.4) transparent;
}
.board-col { scroll-snap-align: center; }
.board--opponent { border-top: 1px solid rgba(255,100,100,0.3); }
.board--player   { border-bottom: 1px solid rgba(120,220,120,0.3); }

.board-col {
    /* Slot geometry — width follows the bond-frame's native 500×484 aspect
       (~1.033:1) so the ornate silver/red/gold/purple frames are not
       squashed. Height stays anchored to --card-h so the row's vertical
       rhythm is unchanged. No border, no background — the frame image
       IS the slot styling. */
    position: relative;
    width: calc(var(--card-h) * 500 / 484);
    height: var(--card-h);
    flex: 0 0 auto;
    transition: box-shadow 0.15s;
}
.board-card {
    /* Fill the slot edge-to-edge; image inside uses object-fit: contain so it
       NEVER crops regardless of container aspect drift. */
    position: absolute;
    inset: 0;
}
.col-empty {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.7rem; color: #4a3c20; letter-spacing: 0.15em; text-transform: uppercase;
}
.drop-target.over {
    box-shadow: 0 0 28px rgba(218,165,32,0.5) inset;
}
.col-empty {
    flex: 1;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.7rem; color: #4a3c20; letter-spacing: 0.15em; text-transform: uppercase;
}

.board-card {
    position: relative;
    border-radius: 4px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0,0,0,0.6);
    border: 1px solid rgba(218,165,32,0.3);
}
.board-card.g-F { border-color: rgba(255,120,180,0.45); }
.board-card.g-M { border-color: rgba(120,180,255,0.45); }
.board-card.g-N { border-color: rgba(140,200,140,0.45); }   /* flora: neutral green */

/* Beast rarity frames — 5-tier scale, same art, different border. */
.board-card.rarity-tier-1 { border-width: 1px; border-color: rgba(140,140,140,0.6); }
.board-card.rarity-tier-2 { border-width: 2px; border-color: rgba(180,180,180,0.8);
    box-shadow: 0 0 0 1px rgba(0,0,0,0.4) inset; }
.board-card.rarity-tier-3 { border-width: 2px; border-color: rgba(160,200,255,0.85);
    box-shadow: 0 0 12px rgba(120,170,255,0.4), 0 0 0 1px rgba(0,0,0,0.4) inset; }
.board-card.rarity-tier-4 { border-width: 3px; border-color: rgba(200,140,255,0.9);
    box-shadow: 0 0 18px rgba(170,100,255,0.55), 0 0 0 1px rgba(0,0,0,0.4) inset; }
.board-card.rarity-tier-5 { border-width: 3px; border-color: rgba(255,196,120,1);
    box-shadow: 0 0 24px rgba(255,180,60,0.6), 0 0 0 1px rgba(0,0,0,0.4) inset;
    animation: legendaryPulse 2.5s ease-in-out infinite; }
@keyframes legendaryPulse {
    0%, 100% { box-shadow: 0 0 18px rgba(255,180,60,0.45), 0 0 0 1px rgba(0,0,0,0.4) inset; }
    50%      { box-shadow: 0 0 36px rgba(255,180,60,0.9),  0 0 0 1px rgba(0,0,0,0.4) inset; }
}
.board-card img { width: 100%; height: 100%; object-fit: contain; display: block; }
.board-card-hp,
.board-bond-hp {
    position: absolute; bottom: 0; left: 0; right: 0;
    padding: 2px 4px;
    font-size: 0.72rem; color: #fff;
    background: rgba(0,0,0,0.78);
    text-align: center;
    letter-spacing: 0.05em;
    font-weight: 600;
}
.board-card-name {
    position: absolute; top: 0; left: 0; right: 0;
    padding: 3px 4px;
    font-size: 0.62rem; color: #fff5d6;
    background: linear-gradient(180deg, rgba(0,0,0,0.85), rgba(0,0,0,0));
    text-align: center;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1.1;
    text-shadow: 0 1px 2px rgba(0,0,0,0.9);
}

/* Bonded column — render the two cards as one unit. */
.board-col.col-bonded {
    border-style: solid;
    border-color: rgba(255, 200, 80, 0.45);
    box-shadow: inset 0 0 18px rgba(255, 180, 40, 0.18);
}
/* Diagonal-split bond: anchor in upper-left triangle, partner in lower-right.
   Diagonal runs from top-right corner to bottom-left corner. */
.board-bond {
    /* Bonded slot fills the parent .board-col which is already --card-w × --card-h. */
    position: absolute;
    inset: 0;
    border-radius: 6px;
    overflow: hidden;
    border: 2px solid rgba(255, 200, 80, 0.85);
    box-shadow: 0 0 18px rgba(255, 180, 40, 0.55);
    transition: transform 0.2s, box-shadow 0.2s;
}
.board-bond-card {
    position: absolute;
    inset: 0;
    overflow: hidden;
}
.board-bond-card img {
    width: 100%; height: 100%;
    object-fit: contain; display: block;  /* NO clipping — show full card art */
}
.board-bond-card.bond-anchor {
    /* Upper-left triangle: corners TL, TR, BL */
    clip-path: polygon(0% 0%, 100% 0%, 0% 100%);
}
.board-bond-card.bond-partner {
    /* Lower-right triangle: corners TR, BR, BL */
    clip-path: polygon(100% 0%, 100% 100%, 0% 100%);
}
/* Diagonal line at the split — colour reflects bond chemistry.
   Worst (½ / 0.5)  → black,
   Neutral (X1 / 1) → grey,
   Good   (X2 / 2)  → gold,
   Best   (X3 / 3)  → gold-pink.
   Each variant defines its own gradient + glow filter via CSS vars. */
.bond-divider {
    position: absolute; inset: 0;
    pointer-events: none;
    --c-line:  rgba(255, 195, 60, 1);
    --c-edge:  rgba(255, 215, 110, 0.85);
    --c-glow:  rgba(255, 180, 40, 0.8);
    /* Split edge runs TR → BL (matches clip-path diagonal of both triangles).
       Gradient direction must be `to bottom right` so the colored band lies
       perpendicular to that axis — i.e. ON the TR→BL diagonal, not across it. */
    background: linear-gradient(to bottom right,
        transparent 49%,
        var(--c-edge) 49.5%,
        var(--c-line) 50%,
        var(--c-edge) 50.5%,
        transparent 51%);
    filter: drop-shadow(0 0 4px var(--c-glow));
}
.bond-divider.bond-worst   { --c-line:rgba(20,20,20,1); --c-edge:rgba(60,60,60,0.85); --c-glow:rgba(0,0,0,0.85); }
.bond-divider.bond-neutral { --c-line:rgba(170,170,180,1); --c-edge:rgba(210,210,220,0.8); --c-glow:rgba(180,180,200,0.55); }
.bond-divider.bond-good    { --c-line:rgba(255,195,60,1); --c-edge:rgba(255,215,110,0.9); --c-glow:rgba(255,180,40,0.85); }
.bond-divider.bond-best    { --c-line:rgba(255,160,210,1); --c-edge:rgba(255,210,140,0.95); --c-glow:rgba(255,140,200,1);
    /* Best bond gets a soft animated shimmer */
    animation: bondBestShimmer 2.2s ease-in-out infinite;
}
@keyframes bondBestShimmer {
    0%, 100% { filter: drop-shadow(0 0 4px rgba(255,140,200,0.7)); }
    50%      { filter: drop-shadow(0 0 14px rgba(255,180,220,1)); }
}

/* The bond container frame also reflects bond quality for extra clarity. */
.board-bond:has(.bond-divider.bond-best)    { border-color: rgba(255,170,220,1); box-shadow: 0 0 22px rgba(255,140,200,0.7); }
.board-bond:has(.bond-divider.bond-good)    { border-color: rgba(255,200,80,0.95); box-shadow: 0 0 20px rgba(255,180,40,0.6); }
.board-bond:has(.bond-divider.bond-neutral) { border-color: rgba(190,190,200,0.85); box-shadow: 0 0 14px rgba(180,180,200,0.4); }
.board-bond:has(.bond-divider.bond-worst)   { border-color: rgba(60,60,60,0.9); box-shadow: 0 0 16px rgba(0,0,0,0.7); }
.board-bond-hp {
    color: #ffe9b3; background: rgba(0,0,0,0.85);
    z-index: 2;
}

/* ─── Combat animations ──────────────────────────────────────────────── */
.board-card { transition: transform 0.2s, box-shadow 0.2s, opacity 0.4s; }
.board-card.place-anim {
    animation: cardDrop 0.34s ease-out;
}
@keyframes cardDrop {
    0% { transform: scale(1.4) translateY(-20px); opacity: 0; box-shadow: 0 0 30px rgba(218,165,32,0.7); }
    60% { transform: scale(1.05) translateY(0); opacity: 1; }
    100% { transform: scale(1); }
}

.board-card.hit-flash,
.hero-portrait.hit-flash {
    animation: hitFlash 0.22s ease-out;
}
@keyframes hitFlash {
    0%   { filter: brightness(1) drop-shadow(0 0 0 rgba(255,0,0,0)); transform: translateX(0); }
    25%  { filter: brightness(2.3) drop-shadow(0 0 18px rgba(255,80,80,0.9)); transform: translateX(-6px); }
    50%  { transform: translateX(6px); }
    75%  { transform: translateX(-3px); }
    100% { filter: brightness(1); transform: translateX(0); }
}

.board-card.die-anim {
    animation: cardDie 0.42s ease-in forwards;
    pointer-events: none;
}
@keyframes cardDie {
    0%   { transform: scale(1) rotate(0); opacity: 1; }
    50%  { transform: scale(0.85) rotate(-8deg); opacity: 0.7; filter: brightness(0.5) blur(1px); }
    100% { transform: scale(0.15) rotate(40deg); opacity: 0; filter: brightness(0) blur(4px); }
}

.board-col.bond-flash {
    animation: bondFlash 0.6s ease-out;
}
@keyframes bondFlash {
    0% { box-shadow: 0 0 0 rgba(255,200,80,0); background: rgba(10,5,24,0.25); }
    40% { box-shadow: inset 0 0 30px rgba(255,200,80,0.7), 0 0 25px rgba(255,200,80,0.8);
          background: rgba(255,200,80,0.18); }
    100% { box-shadow: 0 0 0 rgba(255,200,80,0); background: rgba(10,5,24,0.25); }
}

/* Floating damage number — tier coloured */
.dmg-pop {
    position: absolute; z-index: 300;
    transform: translate(-50%, 0);
    font-family: 'Cinzel', serif;
    font-size: 2.2rem; font-weight: 800;
    color: #ffe9b3;
    text-shadow:
        0 0 6px rgba(255, 80, 80, 0.9),
        0 0 14px rgba(255, 60, 30, 0.6),
        0 2px 0 #000;
    pointer-events: none;
    animation: dmgPop 1.1s ease-out forwards;
    display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.dmg-pop-badge {
    font-size: 0.6rem; letter-spacing: 0.2em;
    color: #ffdf80; background: rgba(0,0,0,0.5);
    padding: 1px 6px; border-radius: 3px;
    border: 1px solid rgba(255,200,80,0.6);
    text-shadow: none;
}
.dmg-pop-tier {
    font-size: 0.75rem; letter-spacing: 0.18em;
    color: inherit; opacity: 0.9; margin-top: -4px;
    text-shadow: 0 1px 2px rgba(0,0,0,0.9);
}
.dmg-pop.tier-X3 { font-size: 3rem; color: #ffd166;
    text-shadow: 0 0 12px rgba(255,180,30,1), 0 0 30px rgba(255,140,0,0.9), 0 2px 0 #000; }
.dmg-pop.tier-X2 { font-size: 2.5rem; color: #ffb87a;
    text-shadow: 0 0 10px rgba(255,140,80,0.9), 0 0 20px rgba(255,100,40,0.6), 0 2px 0 #000; }
.dmg-pop.tier-H  { font-size: 1.6rem; color: #c8aacc; opacity: 0.85; }
.dmg-pop.resist-strong { filter: drop-shadow(0 0 14px rgba(120,255,150,0.7)); }
.dmg-pop.resist-weak   { filter: drop-shadow(0 0 14px rgba(120,160,255,0.45)) saturate(0.7); }
.dmg-pop.bonded { text-shadow: 0 0 14px rgba(255,200,80,1), 0 0 30px rgba(255,180,40,0.7), 0 2px 0 #000; }

@keyframes dmgPop {
    0% { opacity: 0; transform: translate(-50%, 10px) scale(0.4); }
    15% { opacity: 1; transform: translate(-50%, -10px) scale(1.25); }
    35% { transform: translate(-50%, -30px) scale(1.05); }
    70% { opacity: 1; transform: translate(-50%, -60px) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -110px) scale(0.85); }
}

/* Attacker glow during lunge */
.board-card.attacker-glow {
    box-shadow:
        0 0 0 2px rgba(255, 200, 80, 0.9),
        0 0 24px rgba(255, 180, 40, 0.85),
        0 0 50px rgba(255, 140, 30, 0.55);
    filter: brightness(1.15);
}

/* Screen-shake for big hits (>=8 dmg) */
body.screen-shake { animation: screenShake 0.25s ease-in-out; }
@keyframes screenShake {
    0%, 100% { transform: translate(0, 0); }
    25% { transform: translate(-4px, 3px); }
    50% { transform: translate(3px, -2px); }
    75% { transform: translate(-2px, -3px); }
}

/* Smooth HP bar drain */
.hp-fill { transition: width 0.6s cubic-bezier(0.45, 0, 0.25, 1); }

/* Round banner */
.banner {
    position: absolute; left: 50%; top: 38%;
    transform: translate(-50%, -50%);
    z-index: 150;
    padding: 1rem 2rem;
    background: linear-gradient(160deg, rgba(43,26,77,0.92), rgba(10,5,24,0.95));
    border: 1px solid rgba(218,165,32,0.6);
    border-radius: 8px;
    color: #f4e9c1;
    font-family: 'Cinzel', serif;
    font-size: 1.4rem; letter-spacing: 0.25em; text-transform: uppercase;
    box-shadow: 0 12px 40px rgba(0,0,0,0.65), 0 0 30px rgba(218,165,32,0.25);
    opacity: 0; pointer-events: none;
    transition: opacity 0.2s, transform 0.2s;
}
.banner.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }

/* Hand: anchored to bottom, OVERLAPS the player row so cards "rise" from
   the bottom edge. Left edge aligned past the hero column so they don't sit
   over the hero portraits. Card width fixed in design pixels. */
.hand-strip {
    position: absolute;
    bottom: 0;
    left: calc(340px + var(--card-w) * 0.5);   /* clears hero col + half-card gap */
    right: 12px;
    height: 460px;                /* fits a full card-sized hand (var(--card-h) + breathing) */
    z-index: 5;
    display: flex;
    justify-content: center;
    align-items: flex-end;
    gap: 1rem;
    padding: 0.5rem 1rem 0;
    background: linear-gradient(180deg, transparent 0%, rgba(5,3,16,0.4) 35%, rgba(5,3,16,0.88) 100%);
    overflow: visible;
    pointer-events: none;
}
.hand-card {
    flex: 0 0 var(--card-w);
    width: var(--card-w);
    height: var(--card-h);
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 8px 24px rgba(0,0,0,0.85), 0 0 18px rgba(218,165,32,0.3);
    border: 2px solid rgba(218,165,32,0.55);
    cursor: grab;
    transition: transform 0.15s, box-shadow 0.15s;
    touch-action: none;
    user-select: none;
    pointer-events: auto;
}
.hand-card:hover { transform: translateY(-14px) scale(1.04); box-shadow: 0 18px 36px rgba(0,0,0,0.9), 0 0 26px rgba(218,165,32,0.55); }
/* When a card is picked, hide siblings so the board + bond preview is unobstructed. */
.hand-strip.is-picking .hand-card:not(.dragging) { visibility: hidden; }
.hand-strip.is-picking { background: transparent; }
.hand-card.dragging { visibility: hidden; }
.hand-card img { width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none; }
.hand-empty { color: #6b5a3d; font-size: 0.8rem; padding: 1rem; pointer-events: auto; }

/* Ghost element follows pointer during drag */
.drag-ghost {
    position: absolute; z-index: 200;
    border-radius: 4px; overflow: hidden;
    box-shadow: 0 14px 30px rgba(0,0,0,0.7), 0 0 22px rgba(218,165,32,0.45);
    border: 1px solid #daa520;
    pointer-events: none;
    transition: transform 0.05s;
    transform: rotate(-3deg) scale(1.08);
}
.drag-ghost img { width: 100%; display: block; }

/* While AI is thinking, soften the player UI */
/* Opponent's turn → hand is hidden entirely (no peeking, no dragging). */
body.opponent-thinking .hand-strip { display: none; }
/* Empty player slots keep a subtle border cue while opponent is thinking,
   but no opacity dim — cards (attacker or bystander) must always render
   at full opacity. Previously .drop-target opacity: 0.6 dimmed the whole
   column, including a card sitting in it. */
body.opponent-thinking .board--player .drop-target:not(:has(.board-card)):not(:has(.board-bond)) {
    border-style: dotted;
}

/* End turn button */
.end-turn-btn {
    background: linear-gradient(180deg, #daa520, #8b6914);
    color: #1a0f30;
    font-family: inherit; font-weight: 700; font-size: 0.9rem;
    letter-spacing: 0.15em; text-transform: uppercase;
    padding: 0.7rem 1.5rem;
    border: 1px solid rgba(218,165,32,0.6);
    border-radius: 6px;
    cursor: pointer;
    transition: transform 0.12s, box-shadow 0.12s;
    text-decoration: none;
    display: inline-block;
}
.end-turn-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(218,165,32,0.4); }
.end-turn-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.end-turn-btn--ghost { background: transparent; color: #daa520; }

/* In-slot bond preview — fills the hovered column during drag with the
   diagonal split (anchor + dropped) + combined attack/HP. No tooltips. */
.bond-slot-preview {
    position: absolute;
    inset: 0;
    z-index: 6;
    border-radius: 6px;
    overflow: hidden;
    background: rgba(8, 6, 16, 0.95);
    border: 2px solid rgba(255, 220, 140, 0.85);
    box-shadow: 0 0 18px rgba(255,180,60,0.4);
    display: flex; flex-direction: column;
    pointer-events: none;
}
.bond-slot-preview .bsp-cards {
    flex: 1 1 auto;
    position: relative;
    overflow: hidden;
    min-height: 0;
}
.bond-slot-preview .bsp-half { position: absolute; inset: 0; }
.bond-slot-preview .bsp-half img { width: 100%; height: 100%; object-fit: contain; display: block; }
.bond-slot-preview .bsp-anchor  { clip-path: polygon(0% 0%, 100% 0%, 0% 100%); }
.bond-slot-preview .bsp-partner { clip-path: polygon(100% 0%, 100% 100%, 0% 100%); }
.bond-slot-preview .bsp-divider {
    position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(to bottom right,
        transparent 49%, rgba(255,215,110,0.85) 49.5%, rgba(255,195,60,1) 50%, rgba(255,215,110,0.85) 50.5%, transparent 51%);
    filter: drop-shadow(0 0 4px rgba(255,180,40,0.8));
}
.bond-slot-preview .bsp-stats {
    flex: 0 0 auto;
    background: rgba(0,0,0,0.7);
    padding: 0.3rem 0.4rem;
    text-align: center;
    color: #fff;
    font-family: 'Cinzel', serif;
}
.bond-slot-preview .bsp-chem { font-size: 0.95rem; font-weight: 800; letter-spacing: 0.12em; color: #ffd76b; }
.bond-slot-preview .bsp-line { display: flex; justify-content: space-between; font-size: 0.75rem; padding: 0 0.3rem; }
.bond-slot-preview .bsp-line b { color: #fff; }
/* Tier-coloured divider/border */
.bond-slot-preview[data-tier="worst"]   { border-color: rgba(80,40,40,0.95); }
.bond-slot-preview[data-tier="worst"]   .bsp-chem { color: #ff9090; }
.bond-slot-preview[data-tier="worst"]   .bsp-divider { background: linear-gradient(to bottom right, transparent 49%, rgba(60,60,60,0.85) 49.5%, rgba(20,20,20,1) 50%, rgba(60,60,60,0.85) 50.5%, transparent 51%); }
.bond-slot-preview[data-tier="neutral"] { border-color: rgba(200,200,210,0.7); }
.bond-slot-preview[data-tier="neutral"] .bsp-chem { color: #d0d0d8; }
.bond-slot-preview[data-tier="neutral"] .bsp-divider { background: linear-gradient(to bottom right, transparent 49%, rgba(210,210,220,0.8) 49.5%, rgba(170,170,180,1) 50%, rgba(210,210,220,0.8) 50.5%, transparent 51%); }
.bond-slot-preview[data-tier="best"]    { border-color: rgba(255,170,210,1); box-shadow: 0 0 22px rgba(255,140,200,0.65); }
.bond-slot-preview[data-tier="best"]    .bsp-chem { color: #ffb0d8; }
.bond-slot-preview[data-tier="best"]    .bsp-divider { background: linear-gradient(to bottom right, transparent 49%, rgba(255,210,140,0.95) 49.5%, rgba(255,160,210,1) 50%, rgba(255,210,140,0.95) 50.5%, transparent 51%); }

/* Invalid-bond feedback — dim red X + short reason string. Same slot
   as a valid preview so the player consistently sees SOMETHING when
   their drag registers over an anchor. */
.bond-slot-preview.bond-invalid {
    border-color: rgba(120, 40, 40, 0.85);
    box-shadow: 0 0 12px rgba(180, 60, 60, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(30, 8, 8, 0.85);
}
.bond-slot-preview.bond-invalid .bsp-invalid {
    font-family: 'Cinzel', serif;
    font-size: 0.82rem;
    letter-spacing: 0.06em;
    color: #ff8a8a;
    text-transform: uppercase;
    text-align: center;
    padding: 0.3rem 0.6rem;
}

/* Old floating bubble kept for shared rules — but unused by battle now. */
.bond-preview {
    position: absolute;
    z-index: 250;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem;
    border-radius: 8px;
    background: rgba(8, 6, 16, 0.96);
    border: 2px solid rgba(255, 220, 140, 0.75);
    box-shadow: 0 8px 24px rgba(0,0,0,0.8), 0 0 18px rgba(255,180,60,0.35);
    transform: translate(-50%, -100%);
    min-width: 160px;
}
.bond-preview .bp-cards {
    position: relative;
    width: 140px; height: 180px;
    border-radius: 6px;
    overflow: hidden;
    background: #0a0612;
}
.bond-preview .bp-half {
    position: absolute; inset: 0;
}
.bond-preview .bp-half img {
    width: 100%; height: 100%; object-fit: cover; display: block;
}
.bond-preview .bp-anchor   { clip-path: polygon(0% 0%, 100% 0%, 0% 100%); }
.bond-preview .bp-partner  { clip-path: polygon(100% 0%, 100% 100%, 0% 100%); }
.bond-preview .bp-divider {
    position: absolute; inset: 0;
    pointer-events: none;
    background: linear-gradient(to bottom right,
        transparent 49%, rgba(255,215,110,0.85) 49.5%, rgba(255,195,60,1) 50%, rgba(255,215,110,0.85) 50.5%, transparent 51%);
    filter: drop-shadow(0 0 4px rgba(255,180,40,0.8));
}
.bond-preview .bp-stats {
    width: 100%;
    text-align: center;
    font-family: 'Cinzel', serif;
    color: #ffe9b3;
}
.bond-preview .bp-chem {
    font-size: 0.95rem;
    font-weight: 800;
    letter-spacing: 0.14em;
    margin-bottom: 0.2rem;
}
.bond-preview .bp-line {
    display: flex; justify-content: space-between;
    font-size: 0.85rem;
    padding: 0 0.4rem;
    letter-spacing: 0.06em;
}
.bond-preview .bp-line b { color: #fff; }
.bond-preview[data-tier="worst"]   { border-color: rgba(80, 40, 40, 0.9);   box-shadow: 0 8px 24px rgba(0,0,0,0.85), 0 0 18px rgba(120,30,30,0.4); }
.bond-preview[data-tier="worst"]   .bp-chem,
.bond-preview[data-tier="worst"]   .bp-divider { color: #ff9090; }
.bond-preview[data-tier="worst"]   .bp-divider { background: linear-gradient(to bottom right, transparent 49%, rgba(60,60,60,0.85) 49.5%, rgba(20,20,20,1) 50%, rgba(60,60,60,0.85) 50.5%, transparent 51%); }
.bond-preview[data-tier="neutral"] { border-color: rgba(200,200,210,0.6); }
.bond-preview[data-tier="neutral"] .bp-divider { background: linear-gradient(to bottom right, transparent 49%, rgba(210,210,220,0.8) 49.5%, rgba(170,170,180,1) 50%, rgba(210,210,220,0.8) 50.5%, transparent 51%); }
.bond-preview[data-tier="best"]    { border-color: rgba(255,170,210,0.95); box-shadow: 0 8px 24px rgba(0,0,0,0.85), 0 0 22px rgba(255,140,200,0.7); }
.bond-preview[data-tier="best"]    .bp-divider { background: linear-gradient(to bottom right, transparent 49%, rgba(255,210,140,0.95) 49.5%, rgba(255,160,210,1) 50%, rgba(255,210,140,0.95) 50.5%, transparent 51%); }
.bond-preview[data-tier="invalid"] { border-color: rgba(120,120,120,0.6); opacity: 0.85; }
.bond-preview[data-tier="invalid"] .bp-chem { color: #888; }

.end-modal-content {
    flex-direction: column; align-items: center; text-align: center;
    padding: 3rem 2rem;
}

/* ═══════════════════════════════════════════════════════════════════════
   SHARED "SHELL" LAYOUT — center fills the frame; peek OVERLAYS on top.
   Map never shifts when peek opens — it stays full-bleed underneath.
   Peek docks to the LEFT 20% of the frame width.
   ═══════════════════════════════════════════════════════════════════════ */
.shell {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.shell-center {
    position: absolute;
    inset: 0;
    overflow: hidden;
    background: rgba(10, 8, 24, 0.45);
    border: 1px solid rgba(180, 140, 60, 0.35);
    border-radius: 12px;
    padding: 1rem;
    display: flex;
    flex-direction: column;
}
/* Peek overlays the map on the LEFT 20% — map never shifts when it opens.
   Dark opaque slab, full height. */
.shell-peek {
    position: absolute;
    top: 0;
    left: 0;
    width: 20%;
    height: 100%;
    z-index: 10;
    background: rgba(8, 6, 16, 0.95);
    border-right: 1px solid rgba(255, 200, 100, 0.4);
    padding: 1rem 0.85rem 0.5rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: transform 220ms ease;
}
.shell-peek[data-open="false"] {
    display: none;
}

.shell-peek-close {
    position: absolute;
    top: 0.25rem; right: 0.5rem;
    background: transparent;
    border: none;
    color: rgba(255, 210, 130, 0.85);
    font-size: 1.6rem;
    cursor: pointer;
    padding: 0 0.3rem;
    line-height: 1;
}
.shell-peek-close:hover { color: #fff; }
.shell-peek-body {
    flex: 1 1 auto;
    overflow-y: auto;  /* peek body is the ONLY allowed scroll surface */
}
.shell-peek-foot {
    flex: 0 0 auto;
    padding-top: 0.6rem;
    border-top: 1px solid rgba(255, 200, 100, 0.25);
    margin-top: 0.5rem;
}
.shell-cta {
    width: 100%;
    padding: 0.7rem 1rem;
    background: linear-gradient(180deg, #d18c2d, #8a4f0f);
    color: #fff7d8;
    border: 1px solid rgba(255, 220, 140, 0.6);
    border-radius: 8px;
    font-family: inherit;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    cursor: pointer;
    text-transform: uppercase;
}
.shell-cta:hover:not(:disabled) { filter: brightness(1.12); }
.shell-cta:disabled { opacity: 0.55; cursor: wait; }

/* ── Adventure: world-map IS the page. Header/footer/breadcrumb hidden;
   page-main reset to fill the canvas so the map runs frame-to-frame. ─── */
.frame-canvas:has(.shell[data-page="adventure"]) .page-header { display: none; }
.frame-canvas:has(.shell[data-page="adventure"]) .game-footer  { display: none; }
.frame-canvas:has(.shell[data-page="adventure"]) .page-main    { padding: 0; }
.frame-canvas:has(.shell[data-page="adventure"]) .page-bg,
.frame-canvas:has(.shell[data-page="adventure"]) .page-vignette { display: none; }

.adv-stage {
    /* Reset shell-center chrome — the map fills frame-to-frame. */
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 0;
    inset: 0;
}
.adv-map {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.adv-image {
    position: absolute;
    inset: 0;
    background-image: url('/static/images/maps/w1-map.jpg?v=2026063018');
    background-size: 100% 100%;     /* map IS the canvas, no crop, no letterbox */
    background-position: center;
    background-repeat: no-repeat;
}
.adv-path {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    pointer-events: none;
    z-index: 1;
}

/* Top overlays — stardust top-left + invisible rose-close top-right. */
.adv-overlay {
    position: absolute;
    z-index: 3;
}
.adv-stardust {
    top: 1.5%;
    left: 2%;
    display: flex;
    align-items: center;
    gap: 0.45rem;
    background: rgba(8, 6, 16, 0.7);
    border: 1px solid rgba(255, 200, 100, 0.35);
    border-radius: 999px;
    padding: 0.25rem 0.75rem 0.25rem 0.3rem;
    color: #ffd76b;
    font-weight: 700;
    text-shadow: 0 1px 4px rgba(0,0,0,0.9);
}
.adv-stardust img { width: 28px; height: 28px; object-fit: contain; display: block; }
.adv-close {
    position: absolute;
    top: 1%;
    right: 1.2%;
    width: 6%;
    height: 11%;
    z-index: 4;
    display: block;
    background: transparent;
    border-radius: 50%;
    cursor: pointer;
}
.adv-close:hover {
    background: radial-gradient(circle, rgba(255,200,100,0.18), transparent 70%);
}
/* World title — sits under the ornamental rose in the top-right,
   right-aligned. Cinzel per lore spec. Text-shadow keeps it legible
   over any map color. */
.adv-world-title {
    top: 12%;
    right: 2%;
    text-align: right;
    font-family: 'Cinzel', serif;
    color: #ffd76b;
    pointer-events: none;
    text-shadow: 0 2px 8px rgba(0,0,0,0.9), 0 0 18px rgba(255, 120, 40, 0.35);
}
.adv-world-title h1 {
    margin: 0;
    font-size: clamp(1.6rem, 3.4vw, 2.6rem);
    font-weight: 800;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    background: linear-gradient(135deg, #ffe8b0 0%, #ffb060 60%, #a05820 100%);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
}
.adv-world-title p {
    margin: 0.35rem 0 0;
    font-size: clamp(0.7rem, 1.1vw, 0.95rem);
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #f0d59a;
    font-style: italic;
}
.adv-nodes {
    position: absolute;
    inset: 0;
    z-index: 2;
}
/* Quest pin: round chapter icon with 3 stars arcing along its bottom.
   Mid star is larger and partly overlaps the icon's bottom; left+right
   stars are smaller and offset along the curve. */
.adv-node {
    --node-size: 68px;       /* base icon diameter (design pixels) — tighter, more cinematic */
    --star-size: 14px;       /* all 3 stars same size, sit in a row under the icon */
    --backdrop-extend: 8px;  /* dark plate radius past icon edge (tight — must not bleed onto label) */
    position: absolute;
    transform: translate(-50%, -50%);
    width: var(--node-size);
    height: var(--node-size);
    background: transparent;
    border: none;
    border-radius: 50%;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
    transition: transform 120ms ease, filter 120ms ease;
    z-index: 2;
    overflow: visible;
}
/* LAYER 1 — dark blurred backing plate (the user's "shield"). Sits behind
   everything so the icon has its own readable island regardless of map. */
.adv-node::before {
    content: '';
    position: absolute;
    inset: calc(var(--backdrop-extend) * -1);
    border-radius: 50%;
    background:
        radial-gradient(circle,
            rgba(0, 0, 0, 0.78) 0%,
            rgba(0, 0, 0, 0.48) 45%,
            rgba(0, 0, 0, 0.00) 72%);
    filter: blur(10px);
    pointer-events: none;
    z-index: -2;
}
/* LAYER 2 — DELICATE rim glow. Thin band around the medallion — not a
   chunky halo. Normal = gold, cleared = brighter gold, current = pulsing
   gold, boss = red ember, locked = no rim at all. */
.adv-node::after {
    content: '';
    position: absolute;
    inset: -3px;
    border-radius: 50%;
    background: radial-gradient(circle,
        rgba(255, 220, 140, 0.00) 56%,
        rgba(255, 200, 100, 0.45) 66%,
        rgba(255, 160,  60, 0.18) 76%,
        rgba(255, 120,  40, 0.00) 90%);
    pointer-events: none;
    z-index: -1;
}
/* BOSS — red plate, red ember rim */
.adv-node.is-boss::before {
    background:
        radial-gradient(circle,
            rgba(0, 0, 0, 0.82) 0%,
            rgba(40, 0, 0, 0.55) 45%,
            rgba(60, 0, 0, 0.00) 75%);
    filter: blur(10px);
}
.adv-node.is-boss::after {
    background: radial-gradient(circle,
        rgba(255, 100,  90, 0.00) 54%,
        rgba(255,  70,  60, 0.70) 66%,
        rgba(255,  40,  30, 0.32) 76%,
        rgba(255,  20,  10, 0.00) 92%);
    animation: advBossRim 2.2s ease-in-out infinite;
}
@keyframes advBossRim {
    0%, 100% { filter: brightness(1.0); }
    50%      { filter: brightness(1.4); }
}
/* LOCKED — no rim glow at all (mockup-accurate: locked pins read as silhouette) */
.adv-node.is-locked::before {
    background:
        radial-gradient(circle,
            rgba(0, 0, 0, 0.70) 0%,
            rgba(0, 0, 0, 0.42) 45%,
            rgba(0, 0, 0, 0.00) 72%);
}
.adv-node.is-locked::after {
    background: none;
}
/* CLEARED — brighter, warmer gold rim */
.adv-node.is-cleared::after {
    background: radial-gradient(circle,
        rgba(255, 240, 180, 0.00) 54%,
        rgba(255, 215, 120, 0.65) 66%,
        rgba(255, 180,  80, 0.30) 76%,
        rgba(255, 140,  50, 0.00) 92%);
}
/* CURRENT — the next-to-clear chapter. Modestly larger + pulsing gold ring. */
.adv-node.is-current {
    transform: translate(-50%, -50%) scale(1.20);
}
.adv-node.is-current::after {
    background: radial-gradient(circle,
        rgba(255, 240, 180, 0.00) 52%,
        rgba(255, 220, 130, 0.85) 64%,
        rgba(255, 180,  80, 0.45) 76%,
        rgba(255, 140,  50, 0.00) 92%);
    animation: advCurrentPulse 2.0s ease-in-out infinite;
}

/* Hidden side-mission pin — smaller diamond glyph; locked variant dims
   and shows the star-count gate label instead of a lore preview. */
.adv-node.adv-node-hidden {
    filter: drop-shadow(0 0 8px rgba(200, 150, 255, 0.55));
}
.adv-node-hidden .adv-node-hidden-icon {
    font-size: 1.8rem;
    line-height: 1;
    color: #d8b0ff;
    text-shadow: 0 0 10px rgba(200, 150, 255, 0.9),
                 0 0 22px rgba(160, 100, 255, 0.6);
}
.adv-node.adv-node-hidden.is-locked {
    filter: grayscale(0.8) brightness(0.6);
    opacity: 0.55;
    cursor: pointer;    /* still tappable so the peek can explain the gate */
}
.adv-node.adv-node-hidden.is-locked .adv-node-hidden-icon {
    color: #7a6a8a;
    text-shadow: none;
}
.adv-node.adv-node-hidden.is-locked .adv-node-label .adv-node-num {
    color: #c8a0ff;
}
@keyframes advCurrentPulse {
    0%, 100% { filter: brightness(1.0); opacity: 0.95; }
    50%      { filter: brightness(1.45); opacity: 1.0; }
}
.adv-node:hover { transform: translate(-50%, -50%) scale(1.08); filter: brightness(1.12); }
.adv-node.is-current:hover { transform: translate(-50%, -50%) scale(1.20); }

.adv-node-icon {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    position: relative;
    z-index: 1;            /* sits ABOVE the label so the label backdrop tucks under the icon */
    filter:
        drop-shadow(0 1px 2px rgba(0, 0, 0, 1))
        drop-shadow(0 0 2px rgba(0, 0, 0, 0.85));
}
.adv-node.is-boss .adv-node-icon {
    filter:
        drop-shadow(0 1px 2px rgba(0, 0, 0, 1))
        drop-shadow(0 0 2px rgba(0, 0, 0, 0.85))
        drop-shadow(0 0 6px rgba(255, 90, 80, 0.55));
}
.adv-node.is-locked .adv-node-icon {
    /* Black silhouette feel — preserve user's "unavailable" spec */
    filter:
        grayscale(0.90)
        brightness(0.40)
        drop-shadow(0 2px 4px rgba(0, 0, 0, 1));
}
.adv-node.is-locked .adv-node-star {
    /* Muted grey stars on locked pins */
    filter: grayscale(1) brightness(0.55) drop-shadow(0 1px 2px rgba(0,0,0,0.9));
}
.adv-node.is-locked .adv-node-icon { filter: grayscale(0.85) brightness(0.45); }
.adv-node.is-locked { cursor: pointer; }

/* 3 equally-sized stars sitting ON the bottom of the icon (overlapping
   the lower edge of the medallion — see mockup). All same size, evenly
   spaced. Side stars offset by ±1× star-size from centre. */
.adv-node-star {
    position: absolute;
    display: block;
    width: var(--star-size);
    height: auto;
    bottom: calc(var(--star-size) * 0.1);
    transform: translateX(-50%);
    pointer-events: none;
    filter:
        drop-shadow(0 2px 3px rgba(0,0,0,1))
        drop-shadow(0 0 4px rgba(0,0,0,0.95))
        drop-shadow(0 0 6px rgba(0,0,0,0.7));
    z-index: 2;
}
.adv-star-c { left: 50%; }
.adv-star-l { left: calc(50% - var(--star-size) * 1.0); }
.adv-star-r { left: calc(50% + var(--star-size) * 1.0); }
/* Chapter number + name printed below the stars. Two-line stack with the
   number in a bigger gold band and the title beneath. Heavy text-shadow
   so it stays legible over any map background. */
.adv-node-label {
    position: absolute;
    left: 50%;
    /* Text directly below the icon — minimal gap. */
    top: 100%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 2px 14px 4px;
    pointer-events: none;
    white-space: nowrap;
    z-index: 0;            /* below the icon (z-index 1) — icon clips any overlap */
}
/* Dark patch directly under the text. No negative top inset and small
   blur radius so the patch does NOT spread up into the icon area —
   even through the icon's transparent corner pixels. */
.adv-node-label::before {
    content: '';
    position: absolute;
    inset: 0 -22px -8px -22px;
    background: radial-gradient(ellipse,
        rgba(0,0,0,1.00) 0%,
        rgba(0,0,0,0.95) 40%,
        rgba(0,0,0,0.55) 75%,
        rgba(0,0,0,0.00) 100%);
    filter: blur(4px);
    /* Hard top cut so the small remaining upward blur fan is invisible. */
    mask-image: linear-gradient(to bottom, transparent 0%, black 20%, black 100%);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 20%, black 100%);
    z-index: -1;
    pointer-events: none;
    border-radius: 50%;
}
.adv-node-num {
    font-family: 'Cinzel', serif;
    font-weight: 700;
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.08em;
    color: #ffd47a;
    text-shadow:
        0 1px 2px rgba(0,0,0,1),
        0 0 4px rgba(0,0,0,0.95),
        0 0 8px rgba(255, 160, 50, 0.5);
}
.adv-node-name {
    font-family: 'Cinzel', serif;
    font-size: 11px;
    line-height: 1.1;
    color: #f4ecd0;
    text-shadow:
        0 1px 2px rgba(0,0,0,1),
        0 0 4px rgba(0,0,0,0.95);
    text-align: center;
    white-space: nowrap;
}
.adv-node.is-boss .adv-node-num,
.adv-node.is-boss .adv-node-name {
    color: #ffb0a0;
    text-shadow:
        0 1px 2px rgba(0,0,0,1),
        0 0 6px rgba(0,0,0,0.95),
        0 0 12px rgba(255, 80, 60, 0.6);
}
.adv-node.is-locked .adv-node-num,
.adv-node.is-locked .adv-node-name {
    color: rgba(200, 200, 200, 0.55);
    text-shadow: 0 1px 2px rgba(0,0,0,1);
}
.peek-stars {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 1.15rem;
    margin: 0.4rem 0 0.3rem;
}
.peek-star {
    color: rgba(80, 70, 90, 0.5);
    text-shadow: 0 1px 2px rgba(0,0,0,0.7);
}
.peek-star.earned {
    color: #ffd357;
    filter: drop-shadow(0 0 4px rgba(255,200,60,0.85));
}
.peek-stars-meta {
    margin-left: 0.45rem;
    color: rgba(255, 220, 170, 0.65);
    font-size: 0.78rem;
    letter-spacing: 0.06em;
}
.peek-stars-rules {
    font-size: 0.78rem;
    color: rgba(220, 200, 180, 0.55);
    margin-bottom: 0.6rem;
}
.peek-stars-rules div { padding: 0.1rem 0; }
.peek-stars-rules .earned { color: #ffd357; }
.peek-locked-note {
    color: rgba(255, 180, 100, 0.85);
    font-size: 0.85rem;
    padding: 0.5rem 0.7rem;
    background: rgba(60, 30, 10, 0.5);
    border-left: 3px solid rgba(255, 160, 60, 0.7);
    border-radius: 4px;
    margin-bottom: 0.6rem;
}
.shell-cta:disabled { background: linear-gradient(180deg, #555, #2a2a2a); }

/* Peek panel inner content (Adventure) */
.peek-chapter-num {
    color: rgba(255, 210, 130, 0.7);
    font-size: 0.78rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}
.peek-chapter-title {
    color: #ffd76b;
    font-size: 1.15rem;
    margin: 0.15rem 0 0.5rem;
}
.peek-chapter-beat {
    color: rgba(255, 240, 210, 0.78);
    font-size: 0.85rem;
    line-height: 1.4;
    margin: 0 0 0.7rem;
}
.peek-boss-badge {
    display: inline-block;
    padding: 0.25rem 0.55rem;
    background: linear-gradient(90deg, #d23a3a, #5a0a0a);
    color: #fff;
    border-radius: 4px;
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: 0.7rem;
}
.peek-section-label {
    color: rgba(255, 220, 170, 0.55);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin: 0.6rem 0 0.3rem;
}
.peek-enemy-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.4rem;
}
.peek-enemy {
    background: rgba(20, 14, 36, 0.7);
    border: 1px solid rgba(180, 140, 60, 0.3);
    border-radius: 6px;
    padding: 0.3rem;
    text-align: center;
}
.peek-enemy img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 4px;
    display: block;
}
.peek-enemy-name {
    color: #fff1c8;
    font-size: 0.7rem;
    margin-top: 0.2rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.peek-enemy-stars {
    color: #ffc857;
    font-size: 0.7rem;
}


/* ═════════════════════════════════════════════════════════════════════
   WORLD THEMES — every world declares its own color scheme + panel
   artwork on <body class="{world}-theme">. Adventure map & battle screen
   both inherit; the exact same panel components render everywhere the
   theme is active, only the CSS custom-properties + background art swap.

   Adding a new world = add a new theme block below + drop the four
   asset PNGs under /static/images/{world}/. No structural markup change.
   ═════════════════════════════════════════════════════════════════════ */

/* --- ARIETIS: fire realm --- */
body.arietis-theme {
    --world-fg:          #ffd76b;
    --world-fg-strong:   #ffe9b3;
    --world-fg-mute:     #c8a878;
    --world-accent:      #ff7040;   /* volcanic ember */
    --world-accent-deep: #a02818;   /* dried blood-brick */
    --world-accent-glow: rgba(255, 100, 40, 0.55);
    --world-panel-tint:  rgba(30, 8, 4, 0.72);
    --world-shadow:      rgba(255, 90, 30, 0.4);
    --world-bg-tint:     rgba(20, 6, 4, 0.55);
    --world-bg-image:    url('/static/images/maps/w1-map.jpg?v=2026063018');
    --world-frame-realm: url('/static/images/arietis/realm-frame.png');
    --world-frame-hero:  url('/static/images/arietis/hero-frame.png');
    --world-frame-info:  url('/static/images/arietis/info-panel.png');
    --world-frame-row:   url('/static/images/arietis/row-banner.png');
}

/* --- TAURIA: earth/stone realm (world 2 placeholder — assets pending) --- */
body.tauria-theme {
    --world-fg:          #cbe6a8;
    --world-fg-strong:   #eaffce;
    --world-fg-mute:     #94a675;
    --world-accent:      #7fbe4a;
    --world-accent-deep: #305a1a;
    --world-accent-glow: rgba(127, 190, 74, 0.5);
    --world-panel-tint:  rgba(10, 22, 8, 0.72);
    --world-shadow:      rgba(120, 200, 80, 0.4);
    --world-bg-tint:     rgba(8, 20, 6, 0.55);
    /* --world-bg-image + --world-frame-* land when Tauria assets ship. */
}

/* --- Background: world map dimmed + blurred behind every themed page.
   Applies on both /battle and /adventure via body.{world}-theme.
   Adventure page has its own .adv-image (interactive map with pins), so
   we only tint the .page-bg beneath it and let its own image render on
   top; battle has no interactive map — the dimmed world map fills. --- */
body[class*="-theme"] .page-bg {
    display: block;   /* battle page hides .page-bg globally — un-hide when themed */
    background-image: var(--world-bg-image, url('/static/images/bg_map.jpg?v=2026063002'));
    background-size: cover;
    background-position: center;
    opacity: 1;
    filter: blur(8px) brightness(0.35) saturate(1.1);
}
body[class*="-theme"] .page-bg::after {
    content: '';
    position: absolute; inset: 0;
    background: var(--world-bg-tint, rgba(0,0,0,0.55));
    pointer-events: none;
}

/* --- Themed panel primitives — decorative shells with the world's
   ornate frame art baked in as `background-image`. Content sits inside
   the padded interior; the frame arts already have transparent
   backgrounds after strip-and-crop. --- */
.world-panel {
    position: relative;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    color: var(--world-fg, #e5d5a8);
    font-family: 'Cinzel', serif;
}
.world-panel--realm { background-image: var(--world-frame-realm); }
.world-panel--hero  { background-image: var(--world-frame-hero); }
.world-panel--info  { background-image: var(--world-frame-info); }
.world-panel--row   { background-image: var(--world-frame-row); }

/* Ornate frames were painted at portrait aspect; keep them portrait
   at any size so the ornate corners align to their intended positions. */
.world-panel--realm { aspect-ratio: 635 / 1566; }
.world-panel--hero  { aspect-ratio: 901 / 1466; }
.world-panel--info  { aspect-ratio: 740 / 1478; }
.world-panel--row   { aspect-ratio: 1874 / 664; }

/* --- Themed BATTLE chrome (Slice A) ---
   Swap the flat rectangular hero-portraits for the world's ornate frame
   PNGs. Structural DOM is unchanged — this is purely a decorative reskin
   on top of the existing #hero-A / #hero-B nodes so the entire battle
   JS pipeline (HP, hit animations, portrait swap) keeps working. The
   frame PNGs stretch to fill the portrait rectangle; the character art
   remains centered by the existing .hero-img { object-fit: contain }. */
body[class*="-theme"] .hero-portrait {
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 4% 6% 8%;              /* inset the character art so the ornate frame border is visible */
    background-size: 100% 100%;
    background-repeat: no-repeat;
    filter: drop-shadow(0 4px 12px var(--world-shadow, rgba(0,0,0,0.45)));
}
body[class*="-theme"] .hero-portrait--opp { background-image: var(--world-frame-realm); }
body[class*="-theme"] .hero-portrait--ply { background-image: var(--world-frame-hero); }

/* HP badge, name, epithet — recolor to the theme instead of the old
   purple/gold combo. Only color/shadow; no size or padding changes. */
body[class*="-theme"] .hero-hp        { color: var(--world-fg-strong); text-shadow: 0 1px 3px var(--world-accent-deep); }
body[class*="-theme"] .hero-hp-heart  { fill: var(--world-accent); filter: drop-shadow(0 0 4px var(--world-accent-glow)); }
body[class*="-theme"] .hero-name      { color: var(--world-fg-strong); }
body[class*="-theme"] .hero-epithet   { color: var(--world-fg-mute); }

/* --- SLICE B: 3-column grid + info panel + deck badge (themed pages only) ---
   Unthemed pages keep the 2-column layout and simply hide the info column +
   deck badge. Themed pages get a 260px right column populated by JS.
   Board cards shrink under theme so both rows + hand fit within the
   16:9 canvas without the row-banner cropping the art. */
.battle-info-col     { display: none; }
.deck-badge          { display: none; }
body[class*="-theme"] .battle {
    --card-h: 280px;                              /* was 420 — cards shrink to fit two rows + hand */
}
body[class*="-theme"] .battle-grid {
    /* The two hero+row rows share the TOP 78% of the canvas. The bottom
       22% is reserved for the deck stack (left), floating hint text
       (center), and the hand (bottom-center). Info column spans the
       full grid height on the right. */
    inset: 0 0 22% 0;
    grid-template-columns: 260px 1fr 260px;
    grid-template-rows: 1fr 1fr;
    grid-template-areas:
        "opp-hero opp-row info"
        "ply-hero ply-row info";
    gap: 0.4rem;
    padding: 0.5rem;
}
/* Promote hero-col + board-stack children directly onto the outer grid so
   grid-area placement lands each element in the correct row/col. Wrappers
   still exist for unthemed pages — display:contents removes them from
   layout only when themed. */
body[class*="-theme"] .hero-col,
body[class*="-theme"] .board-stack {
    display: contents;
}
/* Hand strip sits in the reserved bottom strip. Its `left` offset must
   clear the deck stack on the far left and skip the info column on the
   right — hand cards center in the space between. */
body[class*="-theme"] .hand-strip {
    top: 78%;
    left: 520px;             /* clears the widened 500px deck stack */
    right: 268px;            /* clears info col at right */
    height: auto;
    bottom: 8px;
    background: none;
}
body[class*="-theme"] .battle-info-col {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-height: 0;
    padding: 1rem 1rem 1.2rem;
    background-image: var(--world-frame-info);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    color: var(--world-fg);
    font-family: 'Cinzel', serif;
    filter: drop-shadow(0 4px 12px var(--world-shadow, rgba(0,0,0,0.45)));
    overflow: hidden;
}
body[class*="-theme"] .info-turn {
    text-align: center;
    padding: 0.6rem 0 0.4rem;
}
body[class*="-theme"] .info-turn-label {
    font-size: 0.7rem; letter-spacing: 0.35em;
    color: var(--world-fg-mute);
}
body[class*="-theme"] .info-turn-num {
    font-size: 2.6rem; font-weight: 700;
    color: var(--world-fg-strong);
    text-shadow: 0 0 12px var(--world-accent-glow);
    line-height: 1;
    margin: 0.15rem 0;
}
body[class*="-theme"] .info-turn-who {
    font-size: 0.85rem;
    color: var(--world-accent);
    text-transform: uppercase; letter-spacing: 0.15em;
}
body[class*="-theme"] .info-score {
    display: flex; flex-direction: column; gap: 0.25rem;
    padding: 0.5rem 0.5rem;
    border-top: 1px solid var(--world-accent-deep);
    border-bottom: 1px solid var(--world-accent-deep);
}
body[class*="-theme"] .info-score-row {
    display: flex; justify-content: space-between;
    font-size: 0.9rem;
}
body[class*="-theme"] .info-score-row span { color: var(--world-fg-mute); }
body[class*="-theme"] .info-score-row b    { color: var(--world-fg-strong); font-weight: 600; }
body[class*="-theme"] .info-realm {
    padding: 0.4rem 0.2rem;
}
body[class*="-theme"] .info-realm-label {
    font-size: 0.7rem; letter-spacing: 0.3em;
    color: var(--world-fg-mute);
}
body[class*="-theme"] .info-realm-name {
    font-size: 1.1rem; font-weight: 700;
    color: var(--world-accent);
    text-shadow: 0 0 8px var(--world-accent-glow);
}
body[class*="-theme"] .info-realm-note {
    font-size: 0.78rem; font-style: italic;
    color: var(--world-fg-mute);
    margin-top: 0.15rem;
}
body[class*="-theme"] .info-selected {
    flex: 1; min-height: 0;
    padding: 0.6rem 0.4rem;
    border-top: 1px solid var(--world-accent-deep);
    overflow: hidden;
}
body[class*="-theme"] .info-selected-empty {
    color: var(--world-fg-mute);
    font-style: italic;
    text-align: center;
    padding-top: 1rem;
    font-size: 0.85rem;
}
body[class*="-theme"] .info-selected-name {
    font-size: 1.05rem; font-weight: 700;
    color: var(--world-fg-strong);
    margin-bottom: 0.15rem;
}
body[class*="-theme"] .info-selected-meta {
    font-size: 0.8rem;
    color: var(--world-accent);
    letter-spacing: 0.05em;
    margin-bottom: 0.5rem;
}
body[class*="-theme"] .info-selected-meta span { color: var(--world-fg-mute); margin-left: 0.5rem; }
body[class*="-theme"] .info-selected-stats {
    display: flex; gap: 0.75rem;
    justify-content: center;
}
body[class*="-theme"] .info-selected-stats > div {
    display: flex; flex-direction: column;
    align-items: center;
    background: var(--world-panel-tint);
    border: 1px solid var(--world-accent-deep);
    border-radius: 6px;
    padding: 0.4rem 0.8rem;
    min-width: 68px;
}
body[class*="-theme"] .info-selected-stats span { font-size: 0.7rem; color: var(--world-fg-mute); }
body[class*="-theme"] .info-selected-stats b    { font-size: 1.15rem; color: var(--world-fg-strong); }

/* Deck stack — NAKED (no container). A few slightly-rotated card-back
   rectangles peek out from behind the player hero frame's lower-left
   corner; a gold coin with the count sits centered on the top card.
   Positioned absolute inside .hero-portrait--ply so the cards get partly
   clipped by the frame's own edge — mirrors reference art where the deck
   spills out from under the hero portrait. */
.deck-stack { display: none; }
/* Deck stack lives in the bottom 22% strip, centered under the player's
   hero column (which is the left grid column, 260px wide). Only the top
   ~40% of each card back peeks over the canvas edge — the rest is
   clipped. Three card backs stack on each other with a shallow ±6° fan,
   pivot at the CENTER so the visible tops fan symmetrically without
   dipping out of frame. No coin/badge overlay. */
body[class*="-theme"] .deck-stack {
    display: block;
    position: absolute;
    left: 10px;
    bottom: 0;
    width: 500px;                  /* doubled — was 240 */
    height: 220px;                 /* visible strip — everything below still fades to black */
    pointer-events: none;
    overflow: hidden;
    z-index: 4;
}
/* Transparent-to-black overlay covering the deck cards — sits above
   the cards but below the coin (z-index 5), so the deck fades into
   black without touching the numeral. */
body[class*="-theme"] .deck-stack::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom,
        transparent 0%,
        transparent 25%,
        rgba(0, 0, 0, 0.6) 65%,
        rgba(0, 0, 0, 1) 100%);
    pointer-events: none;
    z-index: 3;
}
body[class*="-theme"] .deck-back-card {
    position: absolute;
    top: 0;
    left: 100px;                   /* centre a 300-wide card in the 500-wide container */
    width: 300px;
    aspect-ratio: 1024 / 1600;
    background-image: url('/static/images/arietis/card-back.png');
    background-size: cover;
    background-position: center top;
    border-radius: 6%;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.7);
    transform-origin: 50% 50%;
}
/* Three cards fanned: middle straight, outer two tilted ±8° and shifted
   ±60px (scaled with the doubled card width). */
body[class*="-theme"] .deck-back-card--1 { transform: rotate(0deg); }
body[class*="-theme"] .deck-back-card--2 { transform: translateX(-60px) rotate(-8deg); }
body[class*="-theme"] .deck-back-card--3 { transform: translateX( 60px) rotate( 8deg); }

/* Coin — an OUTLINE circle only. Transparent interior lets the card
   art show through, a thick gold ring rides the edge, and the heavy
   drop-shadow from before still lifts it off the cards. Golden
   numerals sit centered inside the ring. */
body[class*="-theme"] .deck-coin {
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 108px; height: 108px;                          /* scaled with the doubled deck */
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0, 0, 0, 0.92);
    border: 4px solid #d9a838;
    box-shadow:
        0 14px 30px rgba(0, 0, 0, 0.9),
        0 6px 12px rgba(0, 0, 0, 0.75);
    z-index: 5;
}
body[class*="-theme"] .deck-coin span {
    font-family: 'Cinzel', serif;
    font-size: 2.7rem;
    font-weight: 800;
    line-height: 1;
    color: #d9a838;
    text-shadow:
        0 2px 4px rgba(0, 0, 0, 0.95),
        0 0 2px rgba(0, 0, 0, 0.9);
    letter-spacing: 0.02em;
}

/* Board rows: each slot uses the ×1 silver bond frame. No row-banner
   backing, no dashed slot border. */
body[class*="-theme"] .board {
    background: none;
    border: none;
}
/* Bond frames are shared across every world theme — not per-world assets. */
body[class*="-theme"] .board-col {
    background-image: url('/static/images/bonds/silver.png');
    background-size: 100% 100%;
    background-repeat: no-repeat;
    background-color: transparent;
    border: none;
}
body[class*="-theme"] .board-col .col-empty { display: none; }

/* Bond-tier frame swap — silver (×1) is the default via .board-col above;
   worst/good/best swap in when the anchor's bond_chem sets col.dataset.bondTier. */
body[class*="-theme"] .board-col[data-bond-tier="worst"]   { background-image: url('/static/images/bonds/red.png'); }
body[class*="-theme"] .board-col[data-bond-tier="good"]    { background-image: url('/static/images/bonds/gold.png'); }
body[class*="-theme"] .board-col[data-bond-tier="best"]    { background-image: url('/static/images/bonds/purple.png'); }

/* Cavity positioning — cards drop into the left or right cutout of the
   frame image. Percentages match the transparent card-slot rectangles
   in the 500×484 bond PNGs (cavities at 8.8-45.4% and 54.2-90.8%
   horizontally, 20.2-80.6% vertically — larger top badge + bottom
   nameplate shrinks the vertical run vs the prior 942×1074 art). */
body[class*="-theme"] .board-card {
    position: absolute;
    inset: auto;                                          /* override the base `inset: 0` fill */
    top: 20.2%;
    height: 60.3%;
    width: 36.6%;
    border: none;                                         /* the frame IS the border */
    border-radius: 3px;
    background: transparent;
}
body[class*="-theme"] .board-card.cavity-left  { left: 8.8%; }
body[class*="-theme"] .board-card.cavity-right { left: 54.2%; }
