/* Spoiler season — hub, gallery and card page.
   Lazy-loaded via asset-loader.js on /spoilers routes. Uses the site's existing tokens
   (base.100-400, accent-teal, text.*) so it reads as part of the same system. */

:root {
    --sp-mythic: #E4844A;
    --sp-rare: #C9B037;
    --sp-uncommon: #9FB3C8;
    --sp-common: #6B7280;

    /* Grade tiers. Colour carries the rating faster than the letter does, so these are the real
       signal — S gold, then green/blue/yellow/purple down to red. Chosen to stay distinguishable
       against the dark card frames they sit above. */
    --sp-s: #F5C542;
    --sp-a: #34D399;
    --sp-b: #60A5FA;
    --sp-c: #FBBF24;
    --sp-d: #A78BFA;
    --sp-f: #EF4444;
    --sp-none: #4a5260;
    /* NOT LEGAL. Deliberately grey, not the red of an F — an F means we judged the card and it was
       bad; NL means the question doesn't apply. Colouring them the same made Elvish Mystic look
       terrible in Standard rather than simply unplayable there. */
    --sp-nl: #566072;
}

/* One place to map tier -> colour; every grade surface below just adds .t-{tier}. */
.t-s { --grade: var(--sp-s); }
.t-a { --grade: var(--sp-a); }
.t-b { --grade: var(--sp-b); }
.t-c { --grade: var(--sp-c); }
.t-d { --grade: var(--sp-d); }
.t-f { --grade: var(--sp-f); }
.t-none { --grade: var(--sp-none); }
.t-nl   { --grade: var(--sp-nl); }

/* NL reads as a state, not a grade: no glow, lighter weight, narrower letterform than a real
   letter grade so the eye skips past it instead of parsing it as a score. */
.sp-grade.t-nl, .sp-pwr.t-nl, .sp-pwr-pop-row .g.t-nl {
    font-weight: 700; letter-spacing: .02em; opacity: .85; box-shadow: none;
}
/* An empty track reads as "zero score". A dashed rail reads as "not applicable". */
.sp-power-item.nl .sp-power-track {
    background: transparent;
    border-top: 1px dashed color-mix(in srgb, var(--sp-nl) 55%, transparent);
    height: 0; align-self: center;
}

/* ============================================================ hub */

.sp-wrap { max-width: 1400px; margin: 0 auto; padding: 1.5rem 1rem 4rem; }

.sp-page-head { margin-bottom: 1rem; }
.sp-page-head h1 {
    font-size: clamp(1.75rem, 4vw, 2.75rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--text-primary, #EAEAEA);
}
.sp-page-head .sp-sub { color: #9CA3AF; margin-top: .4rem; max-width: 46rem; }

.sp-kicker {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: .7rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
    color: #5BC0BE; margin-bottom: .5rem;
}
.sp-kicker::before {
    content: ''; width: 6px; height: 6px; border-radius: 50%;
    background: #5BC0BE; box-shadow: 0 0 0 0 rgba(91,192,190,.7);
    animation: sp-live 2s infinite;
}
@keyframes sp-live {
    70% { box-shadow: 0 0 0 8px rgba(91,192,190,0); }
    100% { box-shadow: 0 0 0 0 rgba(91,192,190,0); }
}

/* ---------------------------------------------------------------- hub set cards
   Art-forward tiles. The set's own illustration is the most compelling thing we have about an
   unreleased product, so it fills the card instead of sitting behind text at 30% opacity. */

/* --- the rail ---
   A horizontal scroller showing at most four sets, the rest one swipe (or one arrow click) away.
   grid-auto-flow:column with a fixed auto-column width is what pins it to exactly N-per-view: a
   flex row with flex-basis would let the last partial card stretch, and the whole point is that a
   sliver of the fifth card is visible so it reads as scrollable. */

.sp-rail-wrap { position: relative; }

.sp-rail {
    --sp-rail-gap: 1.1rem;
    --sp-rail-cols: 1;
    display: grid; grid-auto-flow: column;
    grid-auto-columns: calc((100% - (var(--sp-rail-cols) - 1) * var(--sp-rail-gap)) / var(--sp-rail-cols));
    gap: var(--sp-rail-gap);
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory; scroll-behavior: smooth;
    /* Room for the lift-on-hover transform, which an overflow:auto box would otherwise clip. */
    padding: .35rem .25rem 1rem;
    margin: 0 -.25rem;
    /* Without this, snapping aligns the first card to the CONTENT edge and the rail sits at
       scrollLeft 4 (the left padding) at rest — so the "previous" arrow never hides. */
    scroll-padding-left: .25rem;
    scrollbar-width: thin; scrollbar-color: #2A303A transparent;
}
.sp-rail::-webkit-scrollbar { height: 6px; }
.sp-rail::-webkit-scrollbar-thumb { background: #2A303A; border-radius: 999px; }
.sp-rail::-webkit-scrollbar-track { background: transparent; }

/* One at a time on a phone, two on a large phone/tablet, three on a laptop, four on a desktop. */
@media (min-width: 560px)  { .sp-rail { --sp-rail-cols: 2; } }
@media (min-width: 1000px) { .sp-rail { --sp-rail-cols: 3; } }
@media (min-width: 1360px) { .sp-rail { --sp-rail-cols: 4; } }

/* Arrows. Hidden by default and revealed by JS only when the rail actually overflows — an arrow
   that does nothing is worse than no arrow. */
.sp-rail-nav {
    position: absolute; top: 50%; transform: translateY(-50%); z-index: 5;
    display: grid; place-items: center; width: 2.4rem; height: 2.4rem;
    border-radius: 50%; border: 1px solid #2A303A;
    background: rgba(13,17,23,.92); color: #C7CDD6;
    cursor: pointer; backdrop-filter: blur(6px);
    transition: color .15s ease, border-color .15s ease, background .15s ease;
}
/* display:grid above beats the browser's own [hidden] { display:none }, so the arrows rendered on
   every set count including three-set seasons where they scroll nowhere. Same cascade trap that hid
   the whole gallery behind the lightbox. */
.sp-rail-nav[hidden] { display: none; }
.sp-rail-nav:hover { color: #FFF; border-color: rgba(91,192,190,.6); background: #12161d; }
.sp-rail-nav.prev { left: -.85rem; }
.sp-rail-nav.next { right: -.85rem; }
@media (max-width: 700px) { .sp-rail-nav { display: none; } }

.sp-card {
    position: relative; display: block; overflow: hidden;
    min-height: 17.5rem; border-radius: 1rem;
    border: 1px solid #252A33; background: #0d1117;
    text-decoration: none; scroll-snap-align: start;
    transition: transform .3s cubic-bezier(.22,.61,.36,1), border-color .3s, box-shadow .3s;
}
/* The nearest release keeps a teal edge so it is identifiable at a glance now that every slide in
   the rail is the same size. */
.sp-card.is-hero { border-color: rgba(91,192,190,.45); }
/* Sits in the card's normal flow at the top of the body, NOT absolutely positioned in the corner —
   the top-right already holds the release date, and the two overlapped into unreadable mush. */
.sp-card-flag {
    align-self: flex-start; margin-bottom: .55rem;
    padding: .18rem .5rem; border-radius: 999px;
    background: rgba(91,192,190,.92); color: #06231f;
    font-size: .58rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
}
.sp-card:hover {
    transform: translateY(-3px);
    border-color: rgba(91,192,190,.55);
    box-shadow: 0 18px 40px -16px rgba(0,0,0,.9), 0 0 24px -10px rgba(91,192,190,.5);
}

.sp-card-bg {
    position: absolute; inset: 0;
    background-size: cover; background-position: center 32%;
    transition: transform .6s cubic-bezier(.22,.61,.36,1);
}
.sp-card:hover .sp-card-bg { transform: scale(1.05); }

/* Veil: dark at the bottom where the text sits, clear at the top where the art should breathe. */
.sp-card-veil {
    position: absolute; inset: 0;
    background:
        linear-gradient(180deg, rgba(8,11,15,.15) 0%, rgba(8,11,15,.72) 52%, rgba(8,11,15,.96) 100%),
        linear-gradient(90deg, rgba(8,11,15,.6), transparent 60%);
}

.sp-card-body {
    position: relative; z-index: 2;
    display: flex; flex-direction: column; height: 100%;
    padding: 1.1rem 1.2rem 1.2rem;
}

.sp-card-top { display: flex; align-items: center; gap: .5rem; }
.sp-card-sym { filter: invert(1) brightness(1.8) drop-shadow(0 0 4px rgba(0,0,0,.8)); opacity: .95; }
.sp-card-code {
    padding: .1rem .4rem; border-radius: .3rem;
    background: rgba(255,255,255,.1); color: #C7CDD6;
    font-size: .62rem; font-weight: 800; letter-spacing: .08em;
}
.sp-card-when { margin-left: auto; color: #9CA3AF; font-size: .72rem; font-weight: 600; }

/* Release date on its own line. It was .72rem grey text in the top-right corner, sitting directly on
   the hero art — on Mystery Booster the title ran straight over it. It is the most useful fact on the
   card, so it gets weight, a chip background so art can never swallow it, and room to breathe. */
.sp-card-release {
    display: inline-flex; align-items: center; gap: .4rem; align-self: flex-start;
    margin-top: .5rem; padding: .28rem .6rem; border-radius: .45rem;
    background: rgba(8,11,15,.62); border: 1px solid rgba(255,255,255,.1);
    backdrop-filter: blur(3px);
    color: #C7CDD6; font-size: .78rem; white-space: nowrap;
}
.sp-card-release b { color: #FFFDF6; font-weight: 700; }
.sp-card-release svg { color: #5BC0BE; flex: none; }

.sp-card-name-lg {
    margin-top: auto; padding-top: 1.5rem;
    font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
    font-size: clamp(1.5rem, 3.2vw, 2.2rem); font-weight: 700; letter-spacing: -.005em;
    color: #FFFDF6; text-shadow: 0 2px 14px rgba(0,0,0,.9);
}
/* Every slide is the same width now, so the name has to fit one — a 3rem hero headline wrapped to
   four lines and pushed the countdown out of the card. */
.sp-card-name-lg { font-size: clamp(1.35rem, 2vw, 1.75rem); }

.sp-card-stats {
    display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
    margin-top: .55rem;
}
.sp-card-cd { display: inline-flex; gap: .18rem; }
/* Sized to fit BESIDE the button inside a ~330px rail slide. At the set page's dimensions the four
   units plus separators overran the card and the seconds were clipped off the edge. */
.sp-card-cd .cd-digit { width: .95rem; height: 1.5rem; font-size: .88rem; line-height: 1.5rem; }
.sp-card-cd .cd-unit i { font-size: .42rem; letter-spacing: .04em; }
/* The colons are pure decoration and cost ~24px across four units. */
.sp-card-cd .cd-colon { display: none; }
.sp-card-count { color: #C7CDD6; font-size: .82rem; }
.sp-card-count b { color: #FFF; font-size: 1.15rem; font-weight: 800; font-variant-numeric: tabular-nums; }

.sp-card-members { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .7rem; }
.sp-card-member {
    display: inline-flex; align-items: baseline; gap: .3rem;
    padding: .18rem .5rem; border-radius: 999px;
    background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
    color: #C7CDD6; font-size: .7rem;
}
.sp-card-member b { color: #FFF; font-variant-numeric: tabular-nums; }

/* Footer: call to action left, countdown hard right. Both live in one row so the card reads the same
   whether or not the split-flap script has run — the layout no longer depends on JS. */
.sp-card-foot {
    display: flex; align-items: center; justify-content: space-between; gap: .75rem;
    margin-top: .9rem;
}
.sp-card-go {
    display: inline-flex; align-items: center; gap: .35rem; flex: none;
    padding: .45rem .8rem; border-radius: .6rem;
    background: #5BC0BE; color: #06231f; font-weight: 800; font-size: .78rem;
    white-space: nowrap;
    transition: background .2s, gap .2s;
}
.sp-card:hover .sp-card-go { background: #6FD6D4; gap: .6rem; }

/* margin-left:auto rather than relying on justify-content alone, so it still pins right if the
   footer ever gains a third child. */
.sp-card-foot .sp-card-cd { margin-left: auto; }

/* The no-JS fallback is styled to look deliberate rather than like a broken widget: it is what
   everyone sees for the first moment of every page load, and what they keep seeing if the bundle
   never arrives. */
.sp-card-cd .cd-unit b {
    display: block; color: #FFFDF6; font-size: 1.35rem; font-weight: 800;
    line-height: 1.05; font-variant-numeric: tabular-nums;
}
.sp-card-cd .cd-unit i {
    display: block; color: #9CA3AF; font-size: .55rem; font-style: normal;
    letter-spacing: .1em; text-transform: uppercase; text-align: center;
}

/* --- legacy wide banner (kept for reference; superseded by .sp-card above) --- */

.sp-set {
    position: relative; display: block; overflow: hidden;
    border: 1px solid #252A33; border-radius: 1.25rem;
    background: #12151A; margin-bottom: 1.25rem;
    transition: border-color .25s, transform .25s;
}
.sp-set:hover { border-color: rgba(91,192,190,.5); transform: translateY(-2px); }

/* Blurred key art bleed + drifting gradient mesh. The art carries the mood; the mesh keeps it
   from looking like a static banner. */
.sp-set-art {
    position: absolute; inset: 0;
    background-size: cover; background-position: center 30%;
    filter: blur(28px) saturate(1.3); transform: scale(1.15); opacity: .38;
}
.sp-set-art::after {
    content: ''; position: absolute; inset: -30%;
    background: radial-gradient(closest-side, rgba(91,192,190,.30), transparent 70%);
    animation: sp-drift 18s ease-in-out infinite alternate;
}
@keyframes sp-drift {
    from { transform: translate(-12%, -8%) scale(1); }
    to   { transform: translate(14%, 10%) scale(1.25); }
}
.sp-set-scrim {
    position: absolute; inset: 0;
    background: linear-gradient(100deg, #0B0D10 12%, rgba(11,13,16,.86) 45%, rgba(11,13,16,.55) 100%);
}

.sp-set-inner {
    position: relative; display: grid; gap: 1.25rem; padding: 1.5rem;
    grid-template-columns: 1fr;
}
@media (min-width: 900px) {
    .sp-set-inner { grid-template-columns: 1fr auto; align-items: center; }
    .sp-set.is-hero .sp-set-inner { padding: 2.25rem; }
}

.sp-set-title { display: flex; align-items: center; gap: .65rem; flex-wrap: wrap; }
.sp-set-title img { width: 30px; height: 30px; filter: invert(1) brightness(1.6); opacity: .9; }
.sp-set-title h2 {
    font-size: clamp(1.35rem, 3vw, 2.1rem); font-weight: 800; letter-spacing: -.02em; color: #EAEAEA;
}
.sp-set-code {
    font-size: .65rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    color: #9CA3AF; border: 1px solid #252A33; border-radius: 999px; padding: .15rem .5rem;
}

/* ---------------------------------------------------------------- split-flap countdown
   A mechanical departure-board, not a number that updates. Each digit is a panel with a hinge
   line across the middle; when it changes the top half falls forward revealing the new value.
   Only changed digits animate, so seconds tick while days sit still. */

.sp-countdown { display: flex; align-items: flex-start; gap: .35rem; margin: .9rem 0 .35rem; }

.cd-unit { display: inline-flex; flex-direction: column; align-items: center; gap: .35rem; }
.cd-unit i {
    font-style: normal; font-size: .58rem; letter-spacing: .14em;
    text-transform: uppercase; color: #6B7280;
}

.cd-digits { display: flex; gap: 3px; }

.cd-digit {
    position: relative;
    width: 1.55rem; height: 2.3rem;
    perspective: 260px;
    border-radius: .35rem;
    background: linear-gradient(#1e232b, #14181e);
    box-shadow: inset 0 0 0 1px #2b313b, 0 2px 6px rgba(0,0,0,.5);
    font-variant-numeric: tabular-nums;
    font-weight: 800; font-size: 1.4rem; line-height: 2.3rem; text-align: center;
    color: #F2F4F7;
    overflow: hidden;
}
/* The hinge — a real flap board's most recognisable feature. */
.cd-digit::after {
    content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1px;
    background: rgba(0,0,0,.55); box-shadow: 0 1px 0 rgba(255,255,255,.05);
    z-index: 3; pointer-events: none;
}

.cd-cur, .cd-nxt { position: absolute; inset: 0; }
.cd-nxt { opacity: 0; }

/* The falling top half: the outgoing digit rotates away, the incoming one is revealed under it. */
.cd-digit.flip .cd-cur {
    transform-origin: 50% 50%;
    animation: cd-fall .42s cubic-bezier(.36,.06,.28,1) forwards;
    backface-visibility: hidden;
    z-index: 2;
}
.cd-digit.flip .cd-nxt { animation: cd-rise .42s cubic-bezier(.36,.06,.28,1) forwards; z-index: 1; }

@keyframes cd-fall {
    0%   { transform: rotateX(0deg);   opacity: 1; }
    100% { transform: rotateX(-90deg); opacity: 0; }
}
@keyframes cd-rise {
    0%   { transform: rotateX(90deg); opacity: 0; }
    60%  { opacity: 1; }
    100% { transform: rotateX(0deg);  opacity: 1; }
}

.cd-colon {
    width: 4px; height: 2.3rem; position: relative; flex: 0 0 auto;
}
.cd-colon::before, .cd-colon::after {
    content: ''; position: absolute; left: 0; width: 3px; height: 3px; border-radius: 50%;
    background: #3a424e;
}
.cd-colon::before { top: .75rem; }
.cd-colon::after { bottom: .75rem; }

/* Accent glow — subtle at distance, urgent inside the last day. */
.sp-countdown .cd-digit { transition: box-shadow .4s, color .4s; }
.sp-countdown.is-imminent .cd-digit {
    color: #FFD9A8;
    box-shadow: inset 0 0 0 1px rgba(228,132,74,.5), 0 0 14px -2px rgba(228,132,74,.45);
}
.sp-countdown.is-imminent .cd-unit i { color: var(--sp-mythic); }

.cd-released {
    display: inline-flex; align-items: center; padding: .4rem .8rem; border-radius: .5rem;
    background: rgba(91,192,190,.15); border: 1px solid rgba(91,192,190,.4);
    color: #5BC0BE; font-weight: 700; font-size: .85rem;
}

/* No-JS fallback. The server renders a single "<b>10</b><i>d</i>" unit which spoilers.js replaces
   with the flap board; style it so a crawler or a JS-less visitor still sees a sensible day count. */
.cd-unit b {
    display: inline-block; min-width: 1.55rem; padding: .35rem .4rem;
    border-radius: .35rem; background: linear-gradient(#1e232b, #14181e);
    box-shadow: inset 0 0 0 1px #2b313b;
    font-size: 1.3rem; font-weight: 800; line-height: 1;
    font-variant-numeric: tabular-nums; color: #F2F4F7; text-align: center;
}

/* Reduced motion: keep the board, drop the flapping. */
@media (prefers-reduced-motion: reduce) {
    .cd-digit.flip .cd-cur, .cd-digit.flip .cd-nxt { animation: none; }
    .cd-digit.flip .cd-cur { opacity: 0; }
    .cd-digit.flip .cd-nxt { opacity: 1; transform: none; }
}

/* --- reveal meter --- */

.sp-meter { margin-top: 1rem; max-width: 34rem; }
.sp-meter-head {
    display: flex; align-items: baseline; gap: .5rem; margin-bottom: .45rem;
}
.sp-meter-num { font-size: 1.5rem; font-weight: 800; color: #EAEAEA; font-variant-numeric: tabular-nums; }
.sp-meter-of { color: #6B7280; font-size: .95rem; }
.sp-meter-label { color: #9CA3AF; font-size: .8rem; margin-left: auto; }

/* One segmented bar split by rarity, so you read total progress AND "the rares are nearly all
   out" in a single glance — which a flat percentage bar cannot tell you. */
.sp-meter-bar {
    display: flex; gap: 2px; height: 10px; border-radius: 999px; overflow: hidden;
    background: #1A1E25;
}
.sp-seg { position: relative; overflow: hidden; }
.sp-seg > span {
    display: block; height: 100%; border-radius: 999px;
    transform-origin: left; animation: sp-fill 1s cubic-bezier(.22,.61,.36,1) both;
}
@keyframes sp-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.sp-seg[data-r="mythic"] > span { background: var(--sp-mythic); }
.sp-seg[data-r="rare"] > span { background: var(--sp-rare); }
.sp-seg[data-r="uncommon"] > span { background: var(--sp-uncommon); }
.sp-seg[data-r="common"] > span { background: var(--sp-common); }

.sp-meter-legend {
    display: flex; flex-wrap: wrap; gap: .75rem; margin-top: .55rem;
    font-size: .7rem; color: #9CA3AF;
}
.sp-meter-legend span { display: inline-flex; align-items: center; gap: .3rem; }
.sp-meter-legend i { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

/* --- family members --- */

.sp-members { margin-top: 1rem; display: flex; flex-direction: column; gap: .4rem; }
.sp-member {
    display: flex; align-items: center; gap: .6rem;
    padding: .55rem .75rem; border-radius: .6rem;
    background: rgba(26,30,37,.6); border: 1px solid #1A1E25;
    color: #9CA3AF; font-size: .85rem; text-decoration: none;
    transition: background .2s, border-color .2s;
}
.sp-member:hover { background: rgba(37,42,51,.8); border-color: rgba(91,192,190,.4); color: #EAEAEA; }
.sp-member .n { color: #EAEAEA; font-weight: 600; }
.sp-member .c { margin-left: auto; font-variant-numeric: tabular-nums; }

.sp-cta {
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .7rem 1.15rem; border-radius: .7rem;
    background: #5BC0BE; color: #06231f; font-weight: 700; font-size: .9rem;
    text-decoration: none; white-space: nowrap;
    transition: background .2s, transform .2s;
}
.sp-cta:hover { background: #4DB6AC; transform: translateY(-1px); }

/* ============================================================ set page */

.sp-hero { position: relative; overflow: hidden; border-radius: 1.25rem; margin-bottom: 1.25rem; }

/* Two-column header: identity left, live numbers right. Stacked, this ran ~600px tall on desktop —
   most of a screen of chrome before the first card. Tightened again once the subtitle paragraph
   went: with only an h1 on the left, `align-items: end` was leaving the title floating against the
   taller stats column. Centre-align and a smaller gap close the gap the text used to fill. */
.sp-head-band { display: grid; gap: .5rem 2.5rem; align-items: center; margin-bottom: .75rem; }
@media (min-width: 900px) {
    .sp-head-band { grid-template-columns: minmax(0, 1fr) auto; }
    .sp-head-stats { display: flex; flex-direction: column; align-items: flex-end; gap: .3rem; }
    .sp-head-stats .sp-countdown { margin: 0; }
    .sp-head-stats .sp-meter { margin-top: 0; min-width: 0; }
    /* In the narrow right column the count and its label must read as one unit — the default
       margin-left:auto on the label strands them at opposite ends of the column. */
    .sp-head-stats .sp-meter-head { justify-content: flex-end; gap: .4rem; }
    .sp-head-stats .sp-meter-label { margin-left: 0; }
    .sp-head-stats .sp-meter-bar,
    .sp-head-stats .sp-velocity { min-width: 20rem; }
}
.sp-head-band h1 { margin-bottom: 0; }

/* Sits directly above the countdown and names what it is counting to. Deliberately quiet — it is a
   label for the big number underneath, not a headline competing with it. */
.sp-release-date {
    font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: #6B7280; white-space: nowrap;
}

.sp-tabs { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; margin-bottom: 1rem; }

/* Divider between the family tabs and the gallery/leaderboard switch sharing the row. */
.sp-tab-sep {
    width: 1px; height: 1.4rem; margin: 0 .35rem;
    background: #252A33; flex: 0 0 auto;
}
@media (max-width: 640px) { .sp-tab-sep { display: none; } }
.sp-tab {
    display: inline-flex; align-items: baseline; gap: .45rem;
    padding: .5rem .9rem; border-radius: .6rem;
    background: #12151A; border: 1px solid #252A33;
    color: #9CA3AF; font-size: .85rem; font-weight: 600; text-decoration: none;
    transition: all .2s;
}
.sp-tab:hover { border-color: rgba(91,192,190,.5); color: #EAEAEA; }
.sp-tab.on { background: rgba(91,192,190,.12); border-color: #5BC0BE; color: #EAEAEA; }
.sp-tab .c {
    font-size: .7rem; color: #6B7280; font-variant-numeric: tabular-nums;
}

/* --- velocity sparkline --- */

.sp-velocity { display: flex; align-items: flex-end; gap: 3px; height: 34px; margin-top: .75rem; }
.sp-velocity i {
    flex: 1; min-width: 3px; background: rgba(91,192,190,.35); border-radius: 2px 2px 0 0;
    transform-origin: bottom; animation: sp-fill-y .6s cubic-bezier(.22,.61,.36,1) both;
}
.sp-velocity i.hot { background: #5BC0BE; }
@keyframes sp-fill-y { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* --- catch-up strip --- */

.sp-catchup {
    display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
    padding: .75rem 1rem; margin-bottom: 1.25rem;
    border: 1px solid rgba(91,192,190,.35); border-radius: .8rem;
    background: rgba(91,192,190,.07); color: #EAEAEA; font-size: .9rem;
}
/* Starts hidden and is revealed by JS only when there is something to catch up on — see the
   .sp-lightbox note above for why the attribute needs help against a display-setting class. */
.sp-catchup[hidden] { display: none; }
.sp-catchup button {
    margin-left: auto; padding: .4rem .8rem; border-radius: .5rem;
    background: #5BC0BE; color: #06231f; font-weight: 700; font-size: .8rem; border: 0; cursor: pointer;
}

/* --- filter bar --- */

.sp-filters {
    position: sticky; top: 0; z-index: 20;
    padding: .6rem 0; margin-bottom: 1rem;
    background: rgba(11,13,16,.94); backdrop-filter: blur(12px);
    border-bottom: 1px solid #1A1E25;
}

/* Single control row. Groups are separated by hairlines rather than whitespace so the whole thing
   reads as one instrument instead of a pile of loose buttons. */
.sp-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.sp-bar > * { flex: 0 0 auto; }

.sp-pips, .sp-rars { display: flex; align-items: center; gap: .2rem; }
.sp-rars { padding-left: .5rem; border-left: 1px solid #22272f; }

/* "+ multicolor" — a checkbox in behaviour, styled as a chip so it sits with the pips rather than
   looking like a form control dropped into a toolbar. Off by default: a colour pip means mono. */
.sp-pip-multi {
    display: inline-flex; align-items: center; gap: .25rem;
    margin-left: .35rem; padding: .2rem .45rem .2rem .3rem;
    height: 24px; border-radius: .35rem;
    background: #12151A; border: 1px solid #252A33;
    color: #6B7280; font-size: .68rem; font-weight: 700; white-space: nowrap;
    cursor: pointer; transition: all .16s;
}
.sp-pip-multi:hover { border-color: #3a424e; color: #C7CDD6; }
.sp-pip-multi.on { background: rgba(91,192,190,.14); border-color: rgba(91,192,190,.5); color: #5BC0BE; }
/* The tick only appears when it is actually on — an always-visible check reads as "already enabled". */
.sp-pip-multi svg { opacity: 0; transition: opacity .16s; }
.sp-pip-multi.on svg { opacity: 1; }
@media (max-width: 640px) { .sp-pip-multi span { display: none; } .sp-pip-multi svg { opacity: .5; } .sp-pip-multi.on svg { opacity: 1; } }

/* Rarity as single letters in their own colours — "M R U C" is instantly legible to a Magic player
   and takes a quarter of the width of the spelled-out chips. */
.sp-rar {
    width: 24px; height: 24px; border-radius: .35rem;
    background: #12151A; border: 1px solid #252A33;
    color: #6B7280; font-size: .7rem; font-weight: 900; cursor: pointer;
    transition: all .16s;
}
.sp-rar:hover { border-color: #3a424e; color: #C7CDD6; }
.sp-rar.r-mythic.on   { background: rgba(228,132,74,.18); border-color: var(--sp-mythic); color: var(--sp-mythic); }
.sp-rar.r-rare.on     { background: rgba(201,176,55,.18); border-color: var(--sp-rare); color: var(--sp-rare); }
.sp-rar.r-uncommon.on { background: rgba(159,179,200,.18); border-color: var(--sp-uncommon); color: var(--sp-uncommon); }
.sp-rar.r-common.on   { background: rgba(107,114,128,.22); border-color: #8A93A0; color: #C7CDD6; }

.sp-search-wrap {
    display: flex; align-items: center; gap: .35rem; flex: 1 1 12rem; min-width: 9rem; max-width: 20rem;
    padding: .3rem .6rem; border-radius: .5rem;
    background: #12151A; border: 1px solid #252A33; color: #6B7280;
}
.sp-search-wrap:focus-within { border-color: #5BC0BE; color: #5BC0BE; }
.sp-search-wrap .sp-search {
    flex: 1; min-width: 0; padding: 0; background: none; border: 0; color: #EAEAEA; font-size: .82rem;
}
.sp-search-wrap .sp-search:focus { outline: none; }

/* NB: the segmented SORT control (.sp-seg-btn) was removed — Colour/Rarity/New duplicated the pips,
   rarity buttons and Revealed filter that sit on this same bar. Sorting is always by power now.
   (.sp-seg itself still exists further up as the rarity strip on the card page — different thing.)

   The format picker used to be hidden unless Power was the active sort. Power is now the ONLY sort,
   so it is always shown; without this it would have disappeared with the class that revealed it. */
.sp-fmt { display: inline-block; }

.sp-more {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .32rem .65rem; border-radius: .5rem;
    background: #12151A; border: 1px solid #252A33;
    color: #9CA3AF; font-size: .78rem; font-weight: 600; cursor: pointer;
}
.sp-more:hover { border-color: #3a424e; color: #EAEAEA; }
.sp-more.on { border-color: #5BC0BE; color: #5BC0BE; }
.sp-more-count {
    min-width: 1.05rem; padding: 0 .2rem; border-radius: 999px;
    background: #5BC0BE; color: #06231f; font-size: .62rem; font-weight: 900; text-align: center;
}
.sp-more-count[hidden] { display: none; }

/* Advanced tray */
.sp-tray {
    display: flex; flex-direction: column; gap: .4rem;
    margin-top: .6rem; padding-top: .6rem; border-top: 1px solid #1A1E25;
}
.sp-tray[hidden] { display: none; }
.sp-tray-group { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.sp-tray-label {
    min-width: 4.2rem; font-size: .62rem; font-weight: 800; letter-spacing: .1em;
    text-transform: uppercase; color: #4a5260;
}
.sp-reset { margin-left: auto; color: #6B7280; }

.sp-chip {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .35rem .7rem; border-radius: 999px;
    background: #12151A; border: 1px solid #252A33;
    color: #9CA3AF; font-size: .78rem; font-weight: 600; cursor: pointer;
    transition: all .18s;
}
.sp-chip:hover { border-color: #5BC0BE; color: #EAEAEA; }
.sp-chip.on { background: rgba(91,192,190,.15); border-color: #5BC0BE; color: #EAEAEA; }

/* Colour pips use the real mana colours — instantly readable to any Magic player. */
.sp-pip { width: 26px; height: 26px; padding: 0; justify-content: center; border-radius: 50%; font-size: .7rem; }
.sp-pip[data-value="W"] { background: #F8F6D8; color: #3b3a2a; }
.sp-pip[data-value="U"] { background: #C1D7E9; color: #17324a; }
.sp-pip[data-value="B"] { background: #BAB1AB; color: #241f1c; }
.sp-pip[data-value="R"] { background: #E49977; color: #4a1f12; }
.sp-pip[data-value="G"] { background: #A3C095; color: #1d3318; }
.sp-pip[data-value="C"] { background: #CAC5C0; color: #2b2724; }
.sp-pip[data-value="M"] { background: linear-gradient(135deg, #F8F6D8, #C1D7E9, #BAB1AB, #E49977, #A3C095); color: #2b2724; }
.sp-pip { opacity: .45; border-color: transparent; }
.sp-pip.on, .sp-pip:hover { opacity: 1; box-shadow: 0 0 0 2px #5BC0BE; }

.sp-search {
    flex: 1; min-width: 10rem; max-width: 18rem;
    padding: .4rem .75rem; border-radius: .5rem;
    background: #12151A; border: 1px solid #252A33; color: #EAEAEA; font-size: .82rem;
}
.sp-search:focus { outline: none; border-color: #5BC0BE; }

.sp-select {
    padding: .4rem .6rem; border-radius: .5rem;
    background: #12151A; border: 1px solid #252A33; color: #EAEAEA; font-size: .8rem;
}
.sp-select:focus { outline: none; border-color: #5BC0BE; }

/* (.sp-result-count removed — the visible grid is the count.) */

/* On a phone the wrapped filter rows pushed the first card ~550px down the page — half a screen of
   controls before any spoiler. Keep each row on one line and let it scroll sideways instead, which is
   the same pattern the scanner and deck pages use. */
@media (max-width: 768px) {
    /* Trim the header so cards reach the fold. The blurb stays in the DOM (crawlers read it, and it
       is the page's own summary) but is clamped to two lines instead of three. */
    .sp-page-head { margin-bottom: 1rem; }
    .sp-page-head .sp-sub {
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
        overflow: hidden; font-size: .85rem;
    }
    .sp-countdown { margin: .6rem 0 .25rem; gap: .25rem; }
    .cd-digit { width: 1.3rem; height: 1.95rem; font-size: 1.15rem; line-height: 1.95rem; }
    .cd-colon { height: 1.95rem; }
    .cd-unit i { font-size: .52rem; letter-spacing: .1em; }
    .cd-unit b { font-size: 1.15rem; }
    .sp-meter { margin-top: .6rem; }
    .sp-tabs { margin-bottom: .75rem; }

    .sp-filters { padding: .45rem 0; }
    /* The bar scrolls sideways rather than wrapping into stacked rows that push the first card off
       screen — same pattern the scanner and deck pages use. */
    .sp-bar, .sp-tray-group {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -ms-overflow-style: none;
        -webkit-overflow-scrolling: touch;
        padding-bottom: .15rem;
    }
    .sp-bar::-webkit-scrollbar, .sp-tray-group::-webkit-scrollbar { display: none; }
    .sp-search-wrap { flex: 0 0 10rem; }
    .sp-tray-label { min-width: auto; }
}

/* ============================================================ the grid */

.sp-grid {
    display: grid; gap: .75rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 480px) { .sp-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 768px) { .sp-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; } }
/* Caps at 5. Six across made each card small enough that the art stopped reading and the power
   band's letters were the only legible thing on the tile — which is backwards for a gallery whose
   job is showing you the cards. Five is the widest that still renders as a card. */
@media (min-width: 1100px) { .sp-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

.sp-tile {
    position: relative; border-radius: 4.75% / 3.5%;
    /* Off-screen tiles cost nothing to lay out. With 193+ images this is the difference between
       smooth and janky scrolling on a mid-range Android. */
    content-visibility: auto;
    contain-intrinsic-size: auto 280px;
    transform-style: preserve-3d;
    transition: transform .35s cubic-bezier(.22,.61,.36,1), box-shadow .35s;
}
.sp-tile img {
    display: block; width: 100%; height: auto; border-radius: 4.75% / 3.5%;
    background: #1A1E25;
}

/* Foil tilt. --rx/--ry are written by spoilers.js from pointer position; the ::after layer is the
   specular highlight that sweeps across as the card turns. Pointer devices only. */
.sp-tile.tilting {
    transform: perspective(900px) rotateX(var(--rx, 0)) rotateY(var(--ry, 0)) scale(1.04);
    box-shadow: 0 18px 40px -12px rgba(0,0,0,.75);
    z-index: 5;
}
.sp-tile::after {
    content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
    background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%),
                rgba(255,255,255,.28), rgba(255,255,255,.05) 35%, transparent 60%);
    opacity: 0; transition: opacity .3s;
}
.sp-tile.tilting::after { opacity: 1; }

/* Mythics get a holo shimmer, so rarity is felt rather than read. */
.sp-tile[data-rarity="mythic"]::before {
    content: ''; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
    background: linear-gradient(115deg, transparent 30%, rgba(228,132,74,.35) 45%,
                rgba(255,215,140,.5) 50%, rgba(228,132,74,.35) 55%, transparent 70%);
    background-size: 300% 100%;
    animation: sp-holo 5.5s linear infinite;
    mix-blend-mode: screen; opacity: .55;
}
@keyframes sp-holo { from { background-position: 150% 0; } to { background-position: -150% 0; } }

/* Freshly revealed cards. */
.sp-tile[data-new="1"] { box-shadow: 0 0 0 2px rgba(91,192,190,.85), 0 0 22px -4px rgba(91,192,190,.6); }
.sp-new-ribbon {
    position: absolute; top: 6%; left: -1px; z-index: 3;
    padding: .12rem .5rem .12rem .4rem;
    background: #5BC0BE; color: #06231f;
    font-size: .6rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    border-radius: 0 .3rem .3rem 0;
}

/* Flip affordance for transform / modal cards. Sits over the art's top-right, away from the NEW
   ribbon on the left and the star in the bottom bar. */
.sp-flip {
    position: absolute; top: calc(6% + 1.5rem); right: .4rem; z-index: 4;
    display: grid; place-items: center; width: 1.65rem; height: 1.65rem;
    border-radius: 50%; border: 1px solid rgba(255,255,255,.18);
    background: rgba(10,12,16,.78); color: #9CA3AF;
    cursor: pointer; transition: color .15s ease, transform .2s ease, border-color .15s ease;
}
.sp-flip:hover { color: #EAEAEA; border-color: rgba(91,192,190,.6); transform: rotate(180deg); }
.sp-flip.on { color: #5BC0BE; border-color: rgba(91,192,190,.6); }
.sp-flip svg { width: 13px; height: 13px; }
/* Reduced motion: keep the state change, drop the spin. */
@media (prefers-reduced-motion: reduce) {
    .sp-flip:hover { transform: none; }
}

/* ---------------------------------------------------------------- tile power banner
   A thin strip across the top of every card so a whole screen can be scanned for playability
   without opening anything. Four formats only — beyond that a card tile stops reading as a card. */

/* ABOVE the art, not over it. Overlaying the top of the card covers the card's own name — the one
   piece of text a spoiler gallery must never hide. */
.sp-pwr-band {
    position: relative; z-index: 4;
    display: flex; gap: 2px; margin-bottom: .3rem;
    border-radius: .3rem;
    font-variant-numeric: tabular-nums;
}
.sp-pwr {
    flex: 1; min-width: 0;
    display: flex; align-items: baseline; justify-content: center; gap: .18rem;
    padding: .16rem .1rem;
    background: #12151A;
    font-size: .68rem; font-weight: 800; line-height: 1.2;
    border-top: 2px solid transparent;
}
.sp-pwr:first-child { border-radius: .3rem 0 0 .3rem; }
.sp-pwr:last-child { border-radius: 0 .3rem .3rem 0; }
.sp-pwr i {
    font-style: normal; font-size: .52rem; font-weight: 700;
    opacity: .55; letter-spacing: .04em; color: #C7CDD6;
}

/* Grade colour comes from the --grade custom property set by the .t-{tier} class. */
.sp-pwr { color: var(--grade, var(--sp-none)); border-top-color: var(--grade, var(--sp-none)); }

/* S tier is the only one that gets extra weight — a format-defining card should be obvious from
   across the page without reading anything. */
.sp-pwr.t-s {
    background: linear-gradient(180deg, rgba(245,197,66,.22), rgba(18,21,26,.95));
    text-shadow: 0 0 10px rgba(245,197,66,.5);
}

/* Hover card: every format, not just the four on the strip. This is the ONE thing that should
   appear on hover — the full-size card-image preview was removed from this grid, because at
   five-across the tile already shows the card and the two overlays fought each other. */
.sp-pwr-pop {
    position: absolute; top: calc(100% + .3rem); left: 0; right: 0; z-index: 20;
    padding: .5rem .55rem; border-radius: .5rem;
    background: rgba(10,12,17,.97); border: 1px solid #2b313b;
    box-shadow: 0 16px 34px -12px rgba(0,0,0,.9);
    opacity: 0; visibility: hidden; transform: translateY(-4px);
    transition: opacity .16s, transform .16s, visibility .16s;
    pointer-events: none;
}
.sp-tile:hover .sp-pwr-band .sp-pwr-pop { opacity: 1; visibility: visible; transform: translateY(0); }
@media (hover: none) { .sp-pwr-pop { display: none; } }

.sp-pwr-pop-row {
    display: grid; grid-template-columns: 4.3rem 1fr 1.5rem 1.6rem; align-items: center;
    gap: .35rem; margin-bottom: .18rem;
}
.sp-pwr-pop-row .f { font-size: .58rem; color: #9CA3AF; }
.sp-pwr-pop-row .t { display: block; height: 4px; border-radius: 999px; background: #1A1E25; overflow: hidden; }
.sp-pwr-pop-row .b { display: block; height: 100%; border-radius: 999px; background: var(--grade, var(--sp-none)); }
.sp-pwr-pop-row .v {
    text-align: right; font-size: .6rem; font-weight: 700; color: #9CA3AF;
    font-variant-numeric: tabular-nums;
}
.sp-pwr-pop-row .g {
    text-align: right; font-size: .62rem; font-weight: 900;
    color: var(--grade, var(--sp-none));
}
.sp-pwr-pop-role {
    margin-top: .3rem; padding-top: .3rem; border-top: 1px solid #22272f;
    font-size: .56rem; color: #5BC0BE; font-weight: 700;
}

/* Chrome, not the card — held back until hover so a browsing pass still reads as artwork. */
.sp-pwr-band { opacity: .85; transition: opacity .2s; }
.sp-tile:hover .sp-pwr-band { opacity: 1; }

/* Hover strip: power score for the selected format, star, comment count. */
.sp-tile-bar {
    position: absolute; left: 0; bottom: 0; z-index: 4;
    display: flex; align-items: center; gap: .4rem;
    padding: .5rem .55rem;
    /* Corner wash, not a full-width bar: darkens just enough behind the chip to make it pop off the
       art without hiding the bottom of the card. */
    background: radial-gradient(120% 120% at 0% 100%, rgba(6,8,11,.92) 0%, rgba(6,8,11,.72) 45%, transparent 75%);
    border-radius: 0 0 0 4.75% / 0 0 0 3.5%;
    opacity: 0; transform: translateY(4px); transition: opacity .22s, transform .22s;
}
.sp-tile:hover .sp-tile-bar,
.sp-tile:focus-within .sp-tile-bar { opacity: 1; transform: translateY(0); }
/* Already-hyped cards keep the chip visible even unhovered — otherwise you cannot see what you have
   already hyped without sweeping the mouse over the whole gallery. */
.sp-tile:has(.sp-hype.on) .sp-tile-bar { opacity: 1; transform: none; }
@media (hover: none) { .sp-tile-bar { opacity: 1; transform: none; } }

.sp-power {
    font-size: .78rem; font-weight: 800; color: #9CA3AF; font-variant-numeric: tabular-nums;
}
.sp-power.is-high { color: #5BC0BE; }

/* HYPE. Bottom-LEFT with its own dark corner behind it: on the right it sat over the card frame's
   power/toughness box, and against a wall of bright art an icon with no backing simply vanishes. */
.sp-hype {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .28rem .5rem; border-radius: .5rem;
    background: rgba(8,10,13,.82); border: 1px solid rgba(255,255,255,.12);
    backdrop-filter: blur(3px);
    color: #C7CDD6; font-size: .74rem; font-weight: 800; cursor: pointer;
    font-variant-numeric: tabular-nums;
    transition: color .16s, border-color .16s, background .16s, transform .16s;
}
.sp-hype:hover { color: #FFF; border-color: rgba(228,132,74,.7); transform: translateY(-1px); }
.sp-hype svg { width: 14px; height: 14px; color: #E4844A; flex: none; }
/* Already hyped: the flame fills in and the chip lights up, so the state reads at a glance across a
   whole screen of tiles. */
.sp-hype.on {
    background: rgba(228,132,74,.22); border-color: rgba(228,132,74,.75); color: #FFD9BE;
}
.sp-hype.on svg { color: #FF9A4D; fill: currentColor; }

/* --- entrance --- */

@media (prefers-reduced-motion: no-preference) {
    .sp-tile { animation: sp-rise .45s cubic-bezier(.22,.61,.36,1) both; }
    @keyframes sp-rise {
        from { opacity: 0; transform: translateY(14px) scale(.97); }
        to { opacity: 1; transform: none; }
    }
}

/* --- lightbox --- */

.sp-lightbox {
    position: fixed; inset: 0; z-index: 100;
    display: flex; align-items: center; justify-content: center;
    padding: 2rem 1rem; background: rgba(6,8,10,.92); backdrop-filter: blur(8px);
    animation: sp-fade .2s ease-out;
}
/* A class that sets `display` BEATS the browser's built-in [hidden] { display: none }, so without
   this the lightbox covers the whole page from first paint — the gallery just looks black. Same
   cascade trap the life counter and the sideboard builder both hit. */
.sp-lightbox[hidden] { display: none; }
.sp-lightbox img {
    max-width: min(92vw, 520px); max-height: 90vh; width: auto; height: auto;
    border-radius: 4.75% / 3.5%; box-shadow: 0 30px 80px -20px rgba(0,0,0,.9);
    animation: sp-pop .28s cubic-bezier(.22,.61,.36,1);
}
@keyframes sp-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes sp-pop { from { transform: scale(.9); opacity: 0; } to { transform: none; opacity: 1; } }

/* ============================================================ card page */

.sp-card-page { display: grid; gap: 2rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 900px) { .sp-card-page { grid-template-columns: minmax(0, 380px) 1fr; } }

.sp-card-art img { width: 100%; height: auto; border-radius: 4.75% / 3.5%; }

/* The card follows you down the page. Comments sit in the right column directly under the power
   panel, so while someone is writing about a card they can still see it — which is the whole reason
   to scroll to the thread rather than open it somewhere else.

   align-self:start is REQUIRED, not decoration. A grid item stretches to its row by default, which
   makes it exactly as tall as the row it is meant to stick within — leaving nothing to travel, so
   position:sticky silently does nothing and the card just scrolls away. `top` also has to clear the
   site's own sticky header or the card tucks underneath it. */
@media (min-width: 900px) {
    .sp-card-art {
        position: sticky;
        align-self: start;
        top: 5.25rem;
    }

    /* AppLayout's <main> carries Tailwind's `overflow-auto`, which makes it the nearest scrolling
       ancestor — and position:sticky resolves against THAT, not the viewport. Since <main> is never
       actually constrained here (the window is what scrolls), it never scrolls, so the sticky element
       has nothing to stick to and simply scrolls away with the page.

       Scoped with :has() to pages that contain a spoiler wrapper, so no other page's layout changes.
       Browsers without :has() just lose the sticky behaviour, which is a clean degradation. */
    main:has(.sp-wrap) { overflow: visible; }
}

.sp-card-title { display: flex; align-items: flex-start; gap: 1rem; }
.sp-card-name { font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em; color: #EAEAEA; }

.sp-comment-jump {
    flex: 0 0 auto; margin-left: auto; margin-top: .25rem;
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .35rem .7rem; border-radius: .5rem;
    background: #12151A; border: 1px solid #252A33;
    color: #9CA3AF; font-size: .8rem; font-weight: 600; text-decoration: none;
    transition: all .2s; white-space: nowrap;
}
.sp-comment-jump:hover { border-color: #5BC0BE; color: #5BC0BE; }
/* margin-left:auto lives on the FIRST button only, so the pair travels together to the right rather
   than splitting to opposite ends of the title row. */
.sp-card-title .sp-rate-btn { margin-left: 0; }

/* ---------------------------------------------------------------- player rater */

.sp-rate-btn {
    position: relative;
    flex: 0 0 auto; margin-top: .25rem;
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .35rem .7rem; border-radius: .5rem;
    background: #12151A; border: 1px solid #252A33;
    color: #9CA3AF; font-size: .8rem; font-weight: 600; cursor: pointer;
    transition: all .2s; white-space: nowrap;
}
.sp-rate-btn:hover { border-color: #E4844A; color: #E4844A; }
.sp-rate-btn svg { flex: none; }

/* Hover explainer. A tooltip rather than body copy: the button has to say what it does before
   anyone clicks something that asks them to grade seven formats. */
.sp-rate-tip {
    position: absolute; top: calc(100% + .5rem); right: 0; z-index: 20;
    width: 17rem; padding: .55rem .7rem; border-radius: .5rem;
    background: #0E1116; border: 1px solid #2A303A;
    color: #C7CDD6; font-size: .72rem; font-weight: 400; line-height: 1.5;
    text-align: left; white-space: normal;
    opacity: 0; visibility: hidden; transform: translateY(-4px);
    transition: opacity .16s, transform .16s, visibility .16s;
    box-shadow: 0 14px 30px -12px rgba(0,0,0,.9);
}
.sp-rate-btn:hover .sp-rate-tip,
.sp-rate-btn:focus-visible .sp-rate-tip { opacity: 1; visibility: visible; transform: translateY(0); }

/* Community grade beside the engine's. Deliberately a different SHAPE (outlined, with a people icon)
   so it can never be mistaken for the prediction it sits next to. */
/* DASHED border, where the engine grade is solid: the two sit side by side and must never be
   confused for one another. */
.sp-crowd {
    justify-self: end; text-align: center;
    display: inline-flex; flex-direction: column; align-items: center;
    min-width: 3.4rem; padding: .1rem .3rem; border-radius: .3rem;
    border: 1px dashed color-mix(in srgb, var(--grade, var(--sp-none)) 55%, transparent);
    color: var(--grade, var(--sp-none));
    font-size: .78rem; font-weight: 900; letter-spacing: -.02em;
}
.sp-crowd.is-empty { border-color: #252A33; color: #4a5260; }
/* The delta is the whole point — where players disagree with the engine. */
.sp-crowd b { font-size: .58rem; font-weight: 800; line-height: 1; margin-top: .05rem; }
.sp-crowd b.up { color: #34D399; }
.sp-crowd b.down { color: #F87171; }

.sp-rate-modal {
    position: fixed; inset: 0; z-index: 300;
    display: flex; align-items: center; justify-content: center;
    padding: 1rem;
    background: rgba(4,6,9,.78); backdrop-filter: blur(4px);
}
/* display:flex above beats [hidden] — the same cascade trap that once hid the entire gallery behind
   the lightbox. */
.sp-rate-modal[hidden] { display: none; }

.sp-rate-panel {
    width: min(34rem, 100%); max-height: 86vh; overflow-y: auto;
    padding: 1.15rem; border-radius: .9rem;
    background: #12151A; border: 1px solid #2A303A;
    box-shadow: 0 30px 70px -20px rgba(0,0,0,.95);
}
.sp-rate-head { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: 1rem; }
.sp-rate-head strong { color: #EAEAEA; font-size: 1rem; }
.sp-rate-head p { margin-top: .25rem; color: #9CA3AF; font-size: .76rem; line-height: 1.5; }
.sp-rate-x {
    flex: none; display: grid; place-items: center; width: 1.9rem; height: 1.9rem;
    border-radius: .45rem; background: #0E1116; border: 1px solid #252A33;
    color: #9CA3AF; cursor: pointer;
}
.sp-rate-x:hover { color: #EAEAEA; border-color: #3a424e; }

.sp-rate-rows { display: flex; flex-direction: column; gap: .55rem; }
.sp-rate-row {
    display: grid; grid-template-columns: 5.5rem 1fr 2.2rem 3.6rem; align-items: center; gap: .6rem;
}
.sp-rate-row .f { color: #C7CDD6; font-size: .8rem; }
.sp-rate-row output {
    text-align: right; color: #EAEAEA; font-weight: 800; font-size: .82rem;
    font-variant-numeric: tabular-nums;
}
.sp-rate-row .eng { color: #6B7280; font-size: .68rem; text-align: right; }
.sp-rate-row input[type="range"] { width: 100%; accent-color: #E4844A; }

.sp-rate-foot {
    display: flex; align-items: center; gap: .6rem; margin-top: 1.1rem;
    padding-top: .9rem; border-top: 1px solid #22272f;
}
.sp-rate-msg { flex: 1; font-size: .74rem; color: #9CA3AF; }
.sp-rate-msg.ok { color: #34D399; }
.sp-rate-msg.err { color: #F87171; }
.sp-rate-reset {
    padding: .4rem .7rem; border-radius: .5rem;
    background: #0E1116; border: 1px solid #252A33; color: #9CA3AF;
    font-size: .76rem; cursor: pointer;
}
.sp-rate-reset:hover { color: #EAEAEA; border-color: #3a424e; }
.sp-rate-save {
    padding: .45rem .9rem; border-radius: .5rem; border: 0;
    background: #E4844A; color: #1a0e05; font-size: .78rem; font-weight: 800; cursor: pointer;
}
.sp-rate-save:hover { background: #F0955C; }
.sp-rate-save:disabled { opacity: .6; cursor: default; }

@media (max-width: 560px) {
    .sp-rate-row { grid-template-columns: 4.6rem 1fr 2rem; }
    .sp-rate-row .eng { display: none; }
    .sp-rate-tip { right: auto; left: 0; width: min(17rem, 80vw); }
}

/* scroll-margin so the anchor jump doesn't tuck the heading under the sticky site header. */
.sp-card-comments { margin-top: 2rem; scroll-margin-top: 5rem; }
.sp-card-meta { color: #9CA3AF; font-size: .9rem; margin-top: .25rem; }
.sp-card-oracle {
    margin-top: 1rem; padding: 1rem; border-radius: .75rem;
    background: #12151A; border: 1px solid #252A33;
    color: #EAEAEA; font-size: .92rem; line-height: 1.65; white-space: pre-wrap;
}

/* --- power panel --- */

.sp-power-panel {
    margin-top: 1.25rem; padding: 1.15rem; border-radius: .9rem;
    background: #12151A; border: 1px solid #252A33;
}
.sp-power-title {
    display: flex; align-items: center; gap: .4rem; margin-bottom: .9rem;
    font-size: .7rem; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: #5BC0BE;
}
.sp-power-row {
    display: grid; grid-template-columns: 5.5rem 1fr 2.2rem 3.7rem 4.6rem; align-items: center;
    gap: .6rem; margin-bottom: .45rem;
}
/* Eng. / Usr. captions. Small, dim and above the letter so the grade stays the thing you read, but
   nobody has to guess which number came from the model and which from players. */
.sp-grade .lbl, .sp-crowd .lbl {
    display: block; font-size: .5rem; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; opacity: .65; line-height: 1;
    margin-bottom: .1rem;
}
/* Grade badge — the headline for each format; the number beside it is the detail. */
.sp-grade {
    justify-self: end; text-align: center;
    display: inline-flex; flex-direction: column; align-items: center;
    min-width: 2.8rem;
    padding: .1rem .35rem; border-radius: .3rem;
    font-size: .8rem; font-weight: 900; letter-spacing: -.02em;
    color: var(--grade, var(--sp-none));
    background: color-mix(in srgb, var(--grade, var(--sp-none)) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--grade, var(--sp-none)) 40%, transparent);
}
.sp-grade.t-s {
    background: linear-gradient(180deg, rgba(245,197,66,.3), rgba(245,197,66,.1));
    box-shadow: 0 0 14px -3px rgba(245,197,66,.65);
}
.sp-power-row .f { color: #9CA3AF; font-size: .82rem; }
.sp-power-track {
    display: block;
    height: 9px; border-radius: 999px; background: #1A1E25; overflow: hidden;
}
/* display:block is load-bearing. These are <span>s; the track only sized correctly because a grid
   ITEM gets blockified automatically, but its child does not — so the fill stayed display:inline,
   ignored its width entirely and rendered at 0px. The bars looked like empty tracks. */
.sp-power-fill {
    display: block;
    height: 100%; border-radius: 999px;
    background: linear-gradient(90deg, #3f8e8c, #5BC0BE);
    transform-origin: left; animation: sp-fill 1s cubic-bezier(.22,.61,.36,1) both;
}
/* The fill takes the row's grade colour, so the bar and the letter always agree. */
.sp-power-row .sp-power-fill {
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--grade, var(--sp-none)) 55%, #12151A),
        var(--grade, var(--sp-none)));
}
.sp-power-row .v {
    text-align: right; font-weight: 800; font-size: .85rem; color: #EAEAEA;
    font-variant-numeric: tabular-nums;
}
.sp-role {
    display: inline-block; margin-top: .5rem; padding: .2rem .6rem; border-radius: 999px;
    background: rgba(91,192,190,.12); border: 1px solid rgba(91,192,190,.35);
    color: #5BC0BE; font-size: .72rem; font-weight: 700;
}
.sp-rationale { margin-top: .6rem; color: #9CA3AF; font-size: .85rem; line-height: 1.55; font-style: italic; }
/* Never let this read as fact — it is a prediction about a card nobody has played. */
.sp-ai-disclaimer { margin-top: .7rem; color: #6B7280; font-size: .68rem; }

/* --- per-format reasoning (expandable rows) --- */

/* <details> so the reasoning works with no circuit and no JS. The row itself IS the summary, so the
   grid layout has to be re-applied here: a <summary> is display:list-item by default, which would
   drop the grid entirely and stack the bar under the format name. */
.sp-power-item > summary {
    display: grid; cursor: pointer; list-style: none;
    border-radius: .4rem; padding: .15rem .25rem; margin: 0 -.25rem .45rem;
    transition: background .15s ease;
}
.sp-power-item > summary::-webkit-details-marker { display: none; }
.sp-power-item:not(.bare) > summary:hover { background: #171B22; }
/* Nothing to expand — don't offer a pointer that does nothing. */
.sp-power-item.bare > summary { cursor: default; }
.sp-power-item[open] > summary { background: #171B22; }

.sp-power-why {
    margin: -.15rem 0 .8rem; padding: .7rem .85rem;
    border-left: 2px solid color-mix(in srgb, var(--grade, var(--sp-none)) 55%, transparent);
    background: #0E1116; border-radius: 0 .5rem .5rem 0;
}
.sp-why-reason { color: #D1D5DB; font-size: .84rem; line-height: 1.6; }
.sp-why-facts {
    display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: .5rem;
    color: #9CA3AF; font-size: .74rem;
}
.sp-why-facts b { color: #6B7280; font-weight: 700; margin-right: .3rem; }
.sp-why-risk { margin-top: .5rem; color: #9CA3AF; font-size: .74rem; line-height: 1.55; }
.sp-why-risk b { color: #E0A458; font-weight: 700; margin-right: .3rem; }

/* Honest about thin evidence rather than quietly presenting a guess as a measurement. */
.sp-context-warn {
    display: flex; align-items: center; gap: .35rem; margin-top: .6rem;
    color: #E0A458; font-size: .72rem;
}

/* --- the other face --- */

.sp-card-back {
    margin-top: .75rem; padding: .85rem 1rem; border-radius: .75rem;
    background: #0E1116; border: 1px solid #252A33; border-left: 3px solid #5BC0BE;
}
.sp-back-head {
    display: flex; align-items: center; gap: .4rem;
    color: #5BC0BE; font-size: .82rem;
}
.sp-back-head span { color: #9CA3AF; }
.sp-back-type { margin-top: .2rem; color: #9CA3AF; font-size: .8rem; }
.sp-card-back .sp-card-oracle { margin-top: .4rem; background: #12151A; }

.sp-credit { margin-top: 1rem; font-size: .8rem; color: #6B7280; }
.sp-credit a { color: #5BC0BE; text-decoration: none; }
.sp-credit a:hover { text-decoration: underline; }

/* ============================================================ leaderboard */

.sp-lb { display: flex; flex-direction: column; gap: .5rem; }
.sp-lb-row {
    display: flex; align-items: center; gap: .85rem;
    padding: .6rem .85rem; border-radius: .7rem;
    background: #12151A; border: 1px solid #252A33; text-decoration: none;
    transition: border-color .2s, transform .2s;
}
.sp-lb-row:hover { border-color: rgba(91,192,190,.5); transform: translateX(2px); }
.sp-lb-rank {
    width: 2rem; text-align: center; font-weight: 800; font-size: 1rem;
    color: #6B7280; font-variant-numeric: tabular-nums;
}
.sp-lb-row:nth-child(1) .sp-lb-rank { color: var(--sp-rare); }
.sp-lb-row:nth-child(2) .sp-lb-rank { color: #C0C6CE; }
.sp-lb-row:nth-child(3) .sp-lb-rank { color: #B08D57; }
.sp-lb-thumb { width: 44px; height: 62px; border-radius: .25rem; object-fit: cover; object-position: top; }
.sp-lb-name { color: #EAEAEA; font-weight: 600; font-size: .9rem; }
.sp-lb-sub { color: #6B7280; font-size: .75rem; }
.sp-lb-hype { margin-left: auto; color: #E4844A; font-weight: 800; font-variant-numeric: tabular-nums; }

/* NOTE: the seasonal nav dropdown (.sp-nav*) is deliberately NOT here. It renders in AppLayout on
   every page of the site, whereas this stylesheet is lazy-loaded only on /spoilers routes — so those
   rules live in Styles/app.tailwind.css instead. */

/* ============================================================ misc */

.sp-empty { padding: 3rem 1rem; text-align: center; color: #6B7280; }
.sp-empty[hidden] { display: none; }

/* Loading skeleton. Streamed rendering flushes the component before its data arrives, so this is what
   the first paint shows — it must read as "loading", never as "nothing here". */
.sp-skeleton-head {
    height: 9rem; border-radius: .9rem; margin-bottom: 1.5rem;
    background: linear-gradient(90deg, #12151A 25%, #1A1E25 37%, #12151A 63%);
    background-size: 400% 100%; animation: sp-shimmer 1.4s ease-in-out infinite;
}
.sp-skeleton-tile {
    aspect-ratio: 488 / 680; border-radius: 4.75% / 3.5%;
    background: linear-gradient(90deg, #12151A 25%, #1A1E25 37%, #12151A 63%);
    background-size: 400% 100%; animation: sp-shimmer 1.4s ease-in-out infinite;
}
@keyframes sp-shimmer {
    0% { background-position: 100% 0; }
    100% { background-position: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
    .sp-skeleton-head, .sp-skeleton-tile { animation: none; }
}

/* ---------------------------------------------------------------- card page actions */

.sp-card-actions {
    display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
    margin-top: 1.25rem;
}

/* Matches the gallery tile chip so the same action looks the same in both places. */
.sp-hype-btn {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .45rem .8rem; border-radius: .55rem;
    background: #12151A; border: 1px solid #252A33;
    color: #C7CDD6; font-size: .84rem; font-weight: 700; cursor: pointer;
    transition: all .16s;
}
.sp-hype-btn svg { color: #E4844A; }
.sp-hype-btn:hover { border-color: rgba(228,132,74,.7); color: #FFF; }
.sp-hype-btn.on {
    background: rgba(228,132,74,.2); border-color: rgba(228,132,74,.75); color: #FFD9BE;
}
.sp-hype-btn.on svg { color: #FF9A4D; fill: currentColor; }

/* Deliberately reads as a buy button rather than a grey caption — it is the one commercial moment on
   a page people reached specifically because they want a card that is not out yet. */
.sp-buy {
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .45rem .8rem; border-radius: .55rem;
    background: linear-gradient(180deg, rgba(91,192,190,.16), rgba(91,192,190,.08));
    border: 1px solid rgba(91,192,190,.45);
    color: #5BC0BE; font-size: .84rem; font-weight: 700; text-decoration: none;
    transition: all .16s;
}
.sp-buy:hover { background: rgba(91,192,190,.24); border-color: #5BC0BE; color: #8FE3E1; }
.sp-buy b { color: #EAEAEA; font-weight: 800; }
.sp-buy:hover b { color: #FFF; }
/* The merchant is named so the outbound click is never a surprise. */
.sp-buy i {
    font-style: normal; font-size: .62rem; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; opacity: .7; padding-left: .1rem;
}
