        .card-item {
            border-radius: calc(12px * var(--card-scale));
            padding: calc(8px * var(--card-scale));
            text-align: center;
            cursor: pointer;
            transition: opacity 0.3s, transform 0.3s, box-shadow 0.3s;
            border: 2px solid transparent;
            position: relative;
            min-height: calc(158px * var(--card-scale));
            display: flex;
            flex-direction: column;
            justify-content: space-between;
        }

        .card-item:hover {
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            border-color: var(--skin-accent-text);
            z-index: 100;
        }

        .card-item[data-rarity="Green"] { border: 2px solid #63D4A8; }
        .card-item[data-rarity="Blue"] { border: 2px solid #75C4F5; }
        .card-item[data-rarity="Purple"] { border: 2px solid #9392ED; }
        .card-item[data-rarity="Red"] { border: 2px solid #FF5948; }
        .card-item[data-rarity="Gold"] { border: 2px solid #F0B85D; }

        .card-item-full {
            padding: 0;
            overflow: hidden;
        }

        .card-bg-img {
            position: absolute;
            inset: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
            z-index: 0;
        }

        .card-name-overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            padding: 4px;
            text-align: center;
            font-weight: 700;
            font-size: calc(0.6em + 0.25em * var(--card-scale));
            line-height: 1.15;
            color: #fff;
            background: linear-gradient(180deg, var(--skin-fade-color) 0%, transparent 100%);
            text-shadow: -1px -1px 0 var(--skin-fade-color), 1px -1px 0 var(--skin-fade-color), -1px 1px 0 var(--skin-fade-color), 1px 1px 0 var(--skin-fade-color);
            z-index: 2;
        }

        .card-bottom-fade {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            height: calc(26px * var(--card-scale) + 8px);
            background: linear-gradient(0deg, var(--skin-fade-color) 0%, transparent 100%);
            z-index: 1;
            pointer-events: none;
        }

        /* Same "0 = desaturated, not just faded" convention already used for unlearned skill
           tiles (.skill-tile:not(.has-points) img) -- opacity alone reads as "the picture is
           just a bit see-through", grayscale reads as "you don't have this yet". */
        .card-item:not(.selected) .card-bg-img {
            opacity: 0.5;
            filter: grayscale(1);
        }

        .qty-btn {
            position: absolute;
            bottom: 4px;
            width: calc(26px * var(--card-scale));
            height: calc(26px * var(--card-scale));
            padding: 0;
            border: none;
            border-radius: calc(6px * var(--card-scale));
            font-weight: 700;
            /* The "-"/"+" text node stays in the DOM (screen readers still get it) but is drawn
               invisible -- the glyph itself is replaced by real CSS bars below, same fix as
               .skill-adj-btn and .hamburger-bars above (font glyphs don't reliably center in their
               own line box across fonts/OSes, which is what kept making this need re-fixing). */
            font-size: 0;
            cursor: pointer;
            z-index: 3;
            opacity: 0.95;
            /* opacity/transform only, not "all" -- a blanket transition on background-color here
               made a skin-switch's color change (driven by an inherited custom property changing
               on a far ancestor, not a direct write on this element) genuinely unreliable to
               verify via getComputedStyle in this project's dev tooling (see CLAUDE.md
               "Development Notes"), and there's no real reason to animate the fill color anyway --
               only opacity/scale change on hover. */
            transition: opacity 0.15s, transform 0.15s;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        /* "-" is just the horizontal bar; "+" (.plus) adds the vertical one on top of it. Sized
           relative to the button's own box (the nearest positioned ancestor, since .qty-btn is
           itself position:absolute) so it scales correctly with --card-scale automatically. */
        .qty-btn::before {
            content: "";
            position: absolute;
            top: 50%;
            left: 50%;
            width: 55%;
            height: 2px;
            background: currentColor;
            border-radius: 1px;
            transform: translate(-50%, -50%);
        }
        .qty-btn.plus::after {
            content: "";
            position: absolute;
            top: 50%;
            left: 50%;
            width: 2px;
            height: 55%;
            background: currentColor;
            border-radius: 1px;
            transform: translate(-50%, -50%);
        }

        .qty-btn:hover { opacity: 1; transform: scale(1.1); }

        .qty-btn.minus { left: 4px; background: var(--skin-accent-text); color: var(--skin-active-text); }
        .qty-btn.plus { right: 4px; background: var(--skin-accent-text); color: var(--skin-active-text); }

        .qty-badge {
            position: absolute;
            bottom: 4px;
            left: 50%;
            transform: translateX(-50%);
            min-width: calc(26px * var(--card-scale));
            background: var(--skin-accent-text);
            color: var(--skin-active-text);
            padding: calc(3px * var(--card-scale)) calc(9px * var(--card-scale));
            border-radius: 10px;
            font-weight: 700;
            font-size: calc(0.95em * var(--card-scale));
            /* No --skin-fade-color outline here (unlike .card-name-overlay etc.) -- that scrim
               exists for text sitting over unpredictable card art, but this badge sits on its own
               solid accent-colored pill, where activeText/accentText are already a guaranteed
               contrast pair. Doran is the one skin whose activeText is dark (for contrast against
               its bright gold accent) instead of white -- outlining already-dark text with the
               (always near-black) fade color produced a black-on-black smear there. Same fix
               already applied to .rarity-header/.maint-header for the same reason. */
            z-index: 3;
            box-shadow: 0 1px 3px rgba(0,0,0,0.3);
        }

        .qty-badge.zero {
            background: rgba(0,0,0,0.45);
            opacity: 0.85;
        }

        .awakening-missing-badge {
            position: absolute;
            top: 2px;
            left: 2px;
            z-index: 4;
            background: rgba(231, 76, 60, 0.9);
            color: #fff;
            border-radius: 8px;
            padding: 1px 5px;
            font-size: calc(0.75em * var(--card-scale));
            line-height: 1.3;
            box-shadow: 0 1px 3px rgba(0,0,0,0.35);
        }

        .card-item.error::before {
            content: attr(data-emoji);
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: calc(2.5em * var(--card-scale));
            background: white;
            z-index: 0;
        }

        .card-item.selected {
            border-color: var(--skin-accent-text);
        }

        .empty-state {
            text-align: center;
            color: var(--skin-text-faint);
            padding: 20px;
            font-style: italic;
        }

        .builds-section {
            display: none;
        }

        .builds-section.active {
            display: block;
        }

        .mvp-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
            gap: 15px;
        }

        .builds-loading {
            grid-column: 1 / -1;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            padding: 40px 20px;
            color: var(--skin-accent-text);
            font-weight: 600;
            font-size: 1em;
        }

        .builds-loading .spinner {
            width: 20px;
            height: 20px;
            border: 3px solid var(--skin-card-border);
            border-top-color: var(--skin-accent-text);
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
        }

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

        /* Content-bg, not card-bg -- a build card is a large, complete, self-contained unit (name,
           tags, bonus line, equipped card images), closer in visual weight to a boxed panel than
           to a small repeated leaf chip. The old --skin-card-bg (a light translucent tint by
           design on dark skins) read as washed-out/wrong against an otherwise dark theme. */
        .mvp-card {
            background: var(--skin-content-bg);
            backdrop-filter: blur(var(--skin-content-blur)) saturate(150%);
            -webkit-backdrop-filter: blur(var(--skin-content-blur)) saturate(150%);
            border: 1px solid var(--skin-content-border);
            border-radius: 8px;
            padding: 15px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            border-left: 4px solid #667eea;
        }

        .mvp-card.elem-fire { border-left-color: #e74c3c; }
        .mvp-card.elem-water { border-left-color: #3498db; }
        .mvp-card.elem-wind { border-left-color: #27ae60; }
        .mvp-card.elem-earth { border-left-color: #d68910; }
        .mvp-card.elem-holy { border-left-color: #f4d03f; }
        .mvp-card.elem-shadow { border-left-color: #8e44ad; }
        .mvp-card.elem-neutral { border-left-color: #95a5a6; }
        .mvp-card.elem-undead { border-left-color: #34495e; }
        .mvp-card.elem-poison { border-left-color: #16a085; }
        .mvp-card.elem-ghost { border-left-color: #5d4e8c; }

        .boss-card.elem-fire { border-color: #e74c3c; }
        .boss-card.elem-water { border-color: #3498db; }
        .boss-card.elem-wind { border-color: #27ae60; }
        .boss-card.elem-earth { border-color: #d68910; }
        .boss-card.elem-holy { border-color: #f4d03f; }
        .boss-card.elem-shadow { border-color: #8e44ad; }
        .boss-card.elem-neutral { border-color: #95a5a6; }
        .boss-card.elem-undead { border-color: #34495e; }
        .boss-card.elem-poison { border-color: #16a085; }
        .boss-card.elem-ghost { border-color: #5d4e8c; }
