/* Deck Selector Modal - Rehauled */
.deck-selector-modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.95);
    z-index: 10000;
    justify-content: center;
    align-items: center;
    padding-top: max(12px, var(--mf-safe-top));
    padding-right: max(12px, var(--mf-safe-right));
    padding-bottom: max(12px, var(--mf-safe-bottom));
    padding-left: max(12px, var(--mf-safe-left));
    box-sizing: border-box;
    overflow: hidden;
    color: #fff;
    font-family: 'Cinzel', serif;
    /* Assuming global font */
}

.deck-selector-container {
    box-sizing: border-box;
    width: min(100%, clamp(1200px, 111.111vmin, 2400px));
    max-width: 2400px;
    height: min(100%, clamp(720px, 66.667vmin, 1440px));
    max-height: 100%;
    font-size: clamp(16px, 1.4815vmin, 32px);
    background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 1.25em;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
    padding: 2em;
    position: relative;
    display: flex;
    flex-direction: column;
}

.deck-selector-close {
    position: absolute;
    top: 1.25em;
    right: 1.5625em;
    /* 44x44 minimum hit area: the bare glyph was 39px wide — too small even for mouse. */
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    line-height: 1;
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.5);
    font-size: 2em;
    cursor: pointer;
    transition: color 0.2s;
    z-index: 10;
}

.deck-selector-close:hover {
    color: #fff;
}

/* Content Area */
.ds-content-area {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

/* THE HEADER ROW — CENTRED, IN FLOW, BETWEEN THE SUBTITLE AND THE GRID.

   It was `position: absolute` in the title bar once. An absolute box occupies
   no space, so the header held a gap open for it with a hand-tuned
   `min-height` sized for ONE switch; with two the stack painted 88-105px DOWN
   OVER the character grid at every viewport measured, clipping the upsell
   mid-sentence. The switches cleared each other the whole time, which is why
   the guard comparing them to each other stayed green.

   The row cannot be authored inside the stage markup — that HTML is rebuilt
   from a template string on every render — so `deck-selector.js` re-appends
   the same element after each render and `order` places it. Ordering, not
   source position, is what puts it under the subtitle. */
.battle-preferences {
    order: 1;
    flex: none;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    /* FLEX-START, not center. Each control is a pill with an optional upsell
       line under it, so the two have different total heights — centring them
       in the row pushed the taller one up and left the two SWITCHES on
       different lines, which is the misalignment the owner reported. Aligning
       the tops puts both pills on one line whatever the text below does. */
    align-items: flex-start;
    gap: 0.5em 0.9em;
    margin: 0 0 0.9em;
}

/* The stage's own markup is appended after the row, so everything that is not
   the header or the row sorts below both. */
.ds-content-area > * { order: 2; }
.ds-content-area > .ds-header { order: 0; }
.ds-content-area > .battle-preferences { order: 1; }

/* THE ROW BELONGS TO A STAGE, SO IT IS NOT SHOWN WITHOUT ONE.

   It survives the stage wipe on purpose (the controls are mounted into it), but
   while the content area holds only a spinner or an error there is no header —
   so the row sat at y=45, exactly where the title was about to go, and was then
   shoved 93.5px DOWN the moment the stage rendered. Measured twice per picker
   open at 1400x640. That is the "starts in one layout and mutates into another"
   defect: the header, the row and the grid must all arrive in one frame, in
   their final positions. Hidden here means it takes no space either, so nothing
   else is pushed around when it returns. */
.ds-content-area:not(:has(.ds-header)) .battle-preferences {
    display: none;
}

/* THE SWITCHES BELONG TO THE SUMMONER STAGE, NOT THE DECK STAGE.

   The owner: "the summoner selection screen looks very good. It's not crowded
   at all. So what if we just allow that selection on that screen and we no
   longer allow it once the summoner is selected? So when you select your deck,
   it's just deck. Nothing else."

   Both are per-battle choices made before the battle, so either stage can carry
   them — but only one of the two has room. The summoner grid has space to
   spare; the deck stage is the tightest screen in the product, and the row was
   costing it a whole 44px deck row on a landscape phone. The controls stay
   mounted and keep their values; only their box goes away, and the deck page
   size (which is measured, not fixed) hands that height straight back to the
   decks. */
.ds-content-area[data-stage="decks"] .battle-preferences {
    display: none;
}

.manga-game-preference-slot,
.lucid-game-preference-slot {
    display: flex;
    align-items: center;
    min-height: 0;
    padding: 0;
}

.manga-game-preference-slot:empty,
.lucid-game-preference-slot:empty {
    min-height: 0;
}

.manga-game-preference,
.lucid-game-preference {
    display: grid;
    grid-template-columns: auto auto;
    align-items: center;
    gap: 0.25em 0.65em;
    color: rgba(255, 255, 255, 0.72);
    font-family: inherit;
    font-size: 0.78em;
}

/* The visual label is ALWAYS the short one on this surface — "Manga" is what
   the owner asked the rail to say, and the switch keeps its full accessible
   name through aria-label. The long label stays in the DOM for readers only. */
.manga-game-preference__label,
.lucid-game-preference__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.manga-game-preference__compact-label,
.lucid-game-preference__compact-label {
    display: inline;
    white-space: nowrap;
}

/* THE "On"/"Off" WORDS ARE GONE FROM THE PILL, NOT FROM THE ACCESSIBILITY TREE.
   `role="switch"` + `aria-checked` already state the position, so the text was
   duplicating what the thumb and the reader both say — and it made the two
   pills different widths for no reason. Hidden, not deleted: removing the node
   would take the state away from readers that announce the label rather than
   the role. */
.manga-game-preference__state,
.lucid-game-preference__state {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.manga-game-preference__switch,
.lucid-game-preference__switch {
    appearance: none;
    min-width: 76px;
    min-height: 44px;
    padding: 0 0.2em 0 0.55em;
    border: 1px solid rgba(238, 219, 241, 0.24);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.045);
    color: rgba(255, 255, 255, 0.7);
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.45em;
    font: inherit;
    cursor: pointer;
}

.manga-game-preference__track,
.lucid-game-preference__track {
    width: 34px;
    height: 20px;
    padding: 2px;
    box-sizing: border-box;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.18);
    transition: background-color 160ms ease;
}

.manga-game-preference__thumb,
.lucid-game-preference__thumb {
    display: block;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.72);
    transition: transform 160ms ease, background-color 160ms ease;
}

.manga-game-preference__switch[aria-checked="true"] .manga-game-preference__track,
.lucid-game-preference__switch[aria-checked="true"] .lucid-game-preference__track {
    background: rgba(154, 105, 214, 0.78);
}

.manga-game-preference__switch[aria-checked="true"] .manga-game-preference__thumb,
.lucid-game-preference__switch[aria-checked="true"] .lucid-game-preference__thumb {
    transform: translateX(14px);
    background: #fff;
}

.manga-game-preference__switch:focus-visible,
.lucid-game-preference__switch:focus-visible {
    outline: 3px solid rgba(238, 219, 241, 0.9);
    outline-offset: 3px;
}

/* Fade the CONTROL, never its explanation. A blanket opacity on the root also
   dimmed the reason the control is dark, stacking 0.52 on top of the
   description's own 0.48 alpha — an upsell nobody can read is the same as no
   upsell at all. */
.manga-game-preference.is-disabled .manga-game-preference__label,
.lucid-game-preference.is-disabled .lucid-game-preference__label,
.manga-game-preference.is-disabled .manga-game-preference__switch,
.lucid-game-preference.is-disabled .lucid-game-preference__switch {
    opacity: 0.52;
}

.manga-game-preference.is-disabled .manga-game-preference__switch,
.lucid-game-preference.is-disabled .lucid-game-preference__switch {
    cursor: default;
}

.manga-game-preference.is-disabled .manga-game-preference__description,
.lucid-game-preference.is-disabled .lucid-game-preference__description {
    color: rgba(246, 226, 122, 0.88);
}

.manga-game-preference__description,
.lucid-game-preference__description {
    grid-column: 1 / -1;
    /* Capped so a two-line upsell stays beside its own switch rather than
       stretching the whole band. */
    max-width: 16em;
    color: rgba(255, 255, 255, 0.48);
    font-size: 0.78em;
    text-align: left;
}

/* The ordinary paid state stays one quiet row. Its explanation remains
   available to assistive technology; only the actionable exhaustion reason
   becomes visible. */
.manga-game-preference:not(.is-disabled) .manga-game-preference__description,
.lucid-game-preference:not(.is-disabled) .lucid-game-preference__description {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

@media (max-width: 560px), (max-height: 560px) {
    .battle-preferences {
        margin: 0 0 0.5em;
        gap: 0.35em 0.6em;
    }

    .manga-game-preference-slot,
    .lucid-game-preference-slot {
        min-height: 0;
        padding: 0;
    }

.manga-game-preference,
    .lucid-game-preference {
        /* Side by side in the band, so each sizes to its own words. The
           ragged-left-edge problem this once solved belonged to the stacked
           corner layout and went with it. */
        width: auto;
        grid-template-columns: auto;
        font-size: 12px;
    }

.manga-game-preference__state,
    .lucid-game-preference__state {
        display: none;
    }

.manga-game-preference.is-disabled,
    .lucid-game-preference.is-disabled {
        grid-template-columns: minmax(0, 140px) auto;
    }

.manga-game-preference.is-disabled .manga-game-preference__description,
    .lucid-game-preference.is-disabled .lucid-game-preference__description {
        grid-column: 1;
        grid-row: 1;
        align-self: center;
        max-width: 140px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        text-align: right;
    }

.manga-game-preference.is-disabled .manga-game-preference__switch,
    .lucid-game-preference.is-disabled .lucid-game-preference__switch {
        grid-column: 2;
        grid-row: 1;
    }
}

@media (prefers-reduced-motion: reduce) {
    .manga-game-preference__track,
    .lucid-game-preference__track,
    .manga-game-preference__thumb,
    .lucid-game-preference__thumb {
        transition: none;
    }
}

/* Header */
.ds-header {
    text-align: center;
    margin-bottom: 1em;
    position: relative;
}

.ds-title {
    font-size: 2.1em;
    margin: 0 0 0.3125em 0;
    background: linear-gradient(to right, #eedbf1, #a39bca);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.ds-subtitle {
    color: rgba(255, 255, 255, 0.6);
    font-size: 1em;
    margin: 0;
}

.ds-back-btn {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: #fff;
    min-height: 44px;
    min-height: max(44px, 2.75em);
    padding: 0.5em 1em;
    border-radius: 0.5em;
    font: inherit;
    cursor: pointer;
    transition: all 0.2s;
}

.ds-back-btn:hover {
    background: rgba(255, 255, 255, 0.1);
    border-color: #fff;
}

/* Character Grid */
.ds-character-grid {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    /* `minmax(0, auto)`, NOT `auto`, and `align-items: stretch` with it.

       PRE-EXISTING DEFECT, found while fixing the deck picker and red on clean
       `main`: with `auto` the row takes its content height, and this grid is
       `overflow: hidden`, so anything taller than the box is CUT rather than
       scrolled. Measured at 1024x768 — a 4:3 tablet, where the card is tallest
       relative to the shell — the row ran 14px past the grid and that 14px of
       every champion card was simply gone.

       Bounding the row makes the cards share the height the grid actually has;
       their own internal `min-height: 0` then lets the contents fit instead of
       overflowing. Nothing changes where the row already fits, which is every
       other viewport in the sweep. */
    grid-template-rows: minmax(0, auto);
    gap: 1em;
    padding: 0 0.5em;
    align-content: center;
    /* `center` stays — `deckSummonerLandscape.test.js` pins it as part of the
       uncropped landscape composition, and bounding the ROW is what fixes the
       clip; the alignment never needed to change. */
    align-items: center;
    overflow: hidden;
}

.ds-character-card {
    /* BOUND THE CARD, NOT THE ALIGNMENT.

       PRE-EXISTING DEFECT, red on clean `main`: `.ds-character-grid` is
       `overflow: hidden` with auto-height rows, so where the card is taller than
       the grid the excess is CUT rather than scrolled — measured 14px at
       1024x768, a 4:3 tablet, where the card is tallest relative to the shell.

       The obvious fix, `align-items: stretch`, works but changes the composition
       that `deckSummonerLandscape.test.js` pins (`align-items: center`, the
       uncropped landscape portrait). Capping the item's height fixes the clip
       without touching alignment: the card can never exceed the track, so its
       own contents lay out inside the space that actually exists. */
    max-height: 100%;
    min-height: 0;
    appearance: none;
    width: 100%;
    margin: 0;
    color: inherit;
    font: inherit;
    text-align: center;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 0.75em;
    padding: 0;
    display: flex;
    flex-direction: column;
    cursor: pointer;
    transition: background-color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
    position: relative;
    overflow: hidden;
    height: auto;
    min-height: 0;
}

.ds-character-card:hover:not(.disabled) {
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(238, 219, 241, 0.6);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}

.ds-character-card:focus-visible {
    outline: 3px solid rgba(238, 219, 241, 0.9);
    outline-offset: 3px;
    border-color: rgba(238, 219, 241, 0.8);
}

.ds-character-card.disabled {
    opacity: 0.5;
    filter: grayscale(1);
    cursor: not-allowed;
    pointer-events: none;
    border-style: dashed;
}

/* Character Image */
.ds-char-image-wrapper {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    height: auto;
    min-height: 0;
    flex: 0 0 auto;
    overflow: hidden;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    background:
        radial-gradient(circle at 50% 35%, rgba(111, 82, 158, 0.2), transparent 62%),
        #090711;
    box-shadow: inset 0 0 0 1px rgba(238, 219, 241, 0.08);
    position: relative;
}

.ds-char-image-wrapper::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(circle at 50% 40%, rgba(238, 219, 241, 0.12), transparent 58%),
        linear-gradient(110deg, transparent 25%, rgba(255, 255, 255, 0.06) 42%, transparent 59%);
    background-size: 100% 100%, 250% 100%;
    /* Grace: the static gradient (the pre-doctrine look) shows immediately;
       the sweep only starts once the shared art grace elapses, so fast
       decodes never flash a moving shimmer. */
    animation: ds-portrait-shimmer 1.8s ease-in-out var(--card-art-grace, 200ms) infinite;
    pointer-events: none;
}

@keyframes ds-portrait-shimmer {
    0% {
        background-position: 0 0, -125% 0;
    }

    100% {
        background-position: 0 0, 125% 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .ds-char-image-wrapper::after {
        animation: none;
    }

    .deck-selector-close,
    .ds-back-btn,
    .ds-character-card,
    .ds-deck-item {
        transition: none;
    }

    .ds-deck-item:hover {
        transform: none;
    }
}

/* Progressive reveal (docs/asset-loading.md law 2): each portrait shows the
   moment its own real pixels settle; retrying slots keep their neutral shimmer. */
.ds-char-image-wrapper:has(> .ds-char-image[data-portrait-settled="true"])::after {
    display: none;
}

.ds-char-image-wrapper:has(> .ds-char-image-unavailable)::after {
    display: none;
}

.ds-char-image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: center;
    opacity: 0;
    transition: opacity 0.18s ease;
}

.ds-char-image[data-portrait-settled="true"] {
    opacity: 1;
}

.ds-char-info {
    padding: 0.9375em;
    text-align: center;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.ds-char-name {
    display: block;
    font-size: 1.4em;
    font-weight: bold;
    margin-bottom: 5px;
    color: #fff;
}

.ds-char-desc {
    display: block;
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.9em;
    line-height: 1.4;
    max-height: 2.8em;
    overflow: hidden;
}

.ds-char-status {
    display: block;
    margin-top: 0.75em;
    color: #ff6b6b;
    font-weight: bold;
    font-size: 0.8em;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    background: rgba(0, 0, 0, 0.3);
    padding: 0.3125em 0.5em;
    border-radius: 0.375em;
}

.ds-character-pagination {
    margin-top: 0.75em;
    padding: 0 0.5em;
}

#ds-character-prev,
#ds-character-next,
#ds-deck-prev,
#ds-deck-next {
    height: clamp(44px, 4.074vmin, 88px);
    min-height: clamp(44px, 4.074vmin, 88px);
    padding-block: 0;
    font-size: clamp(16px, 1.58vmin, 32px);
    transition: background-color 0.2s ease, color 0.2s ease,
        border-color 0.2s ease, opacity 0.2s ease;
}

#ds-character-page-info,
#ds-deck-page-info {
    font-size: clamp(16px, 1.58vmin, 32px);
}

/* Deck List */
.ds-deck-list-wrapper {
    flex: 1;
    overflow-y: auto;
    padding: 0 0.625em;
}

.ds-content-area[data-deck-pagination="phone"] .ds-deck-list-wrapper {
    overflow: hidden;
}

.ds-content-area[data-deck-pagination="phone"] .ds-deck-list {
    height: 100%;
}

/* A phone page divides its height between at most three choices, so a row is
   about 44px — enough for ONE line. The name on the first line and the starter
   badge on the second needed 51px, so the badge escaped its row and landed on
   the name of the row below it, and the last row's badge was sliced clean off
   by the list's own edge. Name and badge share the line here; the name yields
   first because the badge is a fixed, short word. */
.ds-content-area[data-deck-pagination="phone"] .ds-deck-info {
    display: flex;
    align-items: center;
    gap: 0.5em;
    min-width: 0;
}

.ds-content-area[data-deck-pagination="phone"] .ds-deck-name {
    min-width: 0;
    margin-bottom: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ds-content-area[data-deck-pagination="phone"] .ds-official-starter-badge {
    flex: 0 0 auto;
}

.ds-deck-list {
    display: flex;
    flex-direction: column;
    gap: 0.9375em;
}

.ds-deck-item {
    appearance: none;
    width: 100%;
    margin: 0;
    min-height: max(44px, 2.75em);
    color: inherit;
    font: inherit;
    text-align: left;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 0.75em;
    padding: 1.25em;
    display: flex;
    justify-content: space-between;
    align-items: center;
    cursor: pointer;
    transition: all 0.2s;
}

.ds-deck-item:hover {
    transform: translateX(5px);
    background: rgba(255, 255, 255, 0.1);
}

.ds-deck-item:focus-visible,
.ds-back-btn:focus-visible {
    outline: 3px solid rgba(238, 219, 241, 0.9);
    outline-offset: 3px;
    border-color: rgba(238, 219, 241, 0.8);
    box-shadow: 0 0 0 3px rgba(138, 43, 226, 0.35), 0 0 14px rgba(238, 219, 241, 0.45);
}

.ds-deck-info,
.ds-deck-name,
.ds-deck-select-arrow {
    display: block;
}

.ds-deck-name {
    font-size: 1.3em;
    font-weight: bold;
    margin-bottom: 5px;
}

.ds-official-starter-badge {
    display: inline-flex;
    align-items: center;
    min-height: 20px;
    padding: 2px 8px;
    border: 1px solid rgba(255, 223, 128, 0.42);
    border-radius: 999px;
    color: #ffe9a8;
    background: rgba(125, 88, 18, 0.28);
    font-size: 0.72em;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.ds-deck-count {
    color: rgba(255, 255, 255, 0.5);
    font-size: 0.9em;
}

.ds-deck-select-arrow {
    font-weight: bold;
    color: rgba(255, 255, 255, 0.5);
}

.ds-deck-item:hover .ds-deck-select-arrow {
    color: #fff;
}

.ds-no-decks {
    text-align: center;
    color: rgba(255, 255, 255, 0.4);
    font-style: italic;
    padding: 40px;
}

.ds-error {
    color: #ff6b6b;
    text-align: center;
    margin-top: 50px;
    font-size: 1.2em;
}

.ds-character-grid[data-card-count="1"] {
    grid-template-columns: minmax(0, min(100%, 18em));
    justify-content: center;
}

.ds-character-grid[data-card-count="2"] {
    grid-template-columns: repeat(2, minmax(0, 18em));
    justify-content: center;
}

.ds-character-grid[data-card-count="3"] {
    grid-template-columns: repeat(3, minmax(0, 18em));
    justify-content: center;
}

@media (orientation: portrait) and (max-width: 768px) {
    .ds-character-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        grid-template-rows: repeat(2, minmax(0, 1fr));
        gap: 10px;
    }

    .ds-char-desc {
        display: none;
    }
}

@media (orientation: landscape) and (min-width: 960px) and (min-height: 561px) and (max-width: 1560px) and (max-aspect-ratio: 17/10) {
    .ds-character-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        grid-template-rows: repeat(2, minmax(0, auto));
        gap: min(24px, 1.25em);
        padding: 0;
        align-content: center;
    }

    .deck-selector-modal {
        padding-top: max(16px, var(--mf-safe-top));
        padding-right: max(16px, var(--mf-safe-right));
        padding-bottom: max(16px, var(--mf-safe-bottom));
        padding-left: max(16px, var(--mf-safe-left));
    }

    .deck-selector-container {
        width: min(1200px, calc(100% - 32px));
        height: 100%;
        max-height: 100%;
        padding: 1em;
    }

    .deck-selector-close {
        top: 0;
        right: 0.5em;
    }

    .ds-header {
        margin-bottom: 0.5em;
    }

    .ds-title {
        font-size: 1.75em;
        margin-bottom: 0.1em;
    }

    .ds-subtitle {
        display: none;
    }

    .ds-char-info {
        padding: 0.35em 0.75em;
    }

    .ds-char-name {
        margin: 0;
        font-size: 1.1em;
    }

    .ds-char-desc {
        display: none;
    }

    .ds-character-pagination {
        margin-top: 0.5em;
        padding: 0;
    }

    .ds-character-grid[data-card-count="1"],
    .ds-character-grid[data-card-count="2"] {
        grid-template-rows: minmax(0, auto);
        align-content: center;
    }

    .ds-character-grid[data-card-count="1"] {
        grid-template-columns: minmax(0, calc((100% - min(24px, 1.25em)) / 2));
    }

    .ds-character-grid[data-card-count="2"] {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .ds-character-grid[data-card-count="3"] > :last-child {
        grid-column: 1 / -1;
        width: calc((100% - min(24px, 1.25em)) / 2);
        justify-self: center;
    }
}

@media (orientation: landscape) and (max-height: 560px) {
    .deck-selector-modal {
        padding-top: max(6px, var(--mf-safe-top));
        padding-right: max(6px, var(--mf-safe-right));
        padding-bottom: max(6px, var(--mf-safe-bottom));
        padding-left: max(6px, var(--mf-safe-left));
    }

    .deck-selector-container {
        width: 100%;
        height: 100%;
        max-height: 100%;
        /* 8px, not 10px. A notched iPhone in landscape spends 21px of the page
           box on the home indicator (max(6px, --mf-safe-bottom) above), and the
           three-choice deck page needs 3x44 + gaps inside what is left. Every
           pixel reclaimed here comes from padding — never from a tap target. */
        padding: 8px 14px;
        border-radius: 14px;
    }

    .deck-selector-close {
        top: 2px;
        right: 8px;
    }

    /* ONE symmetric inset, both stages. Back owns the left corner, Manga and
       Close share the right one, and the name centres on the DIALOG.

       The previous split — 112px on the champion stage, 226/60 on the deck
       stage — centred the name inside the lane left over after Back, which put
       it 83px right of the dialog centre at every landscape page box. And
       112px was reserved for a Manga control that is only that narrow for a
       Moon Pass holder: the free state also renders the reason line, ~195px,
       so Back was positioned on top of the switch. Both are gone: the rail
       control is one width now (below), so one number covers every side. */
    .ds-header {
        min-height: 44px;
        /* Back overhangs the 44px strip by 4px, so this margin IS the gap
           between Back and the first deck row. It stays at 8. */
        margin-bottom: 8px;
        padding-inline: 160px;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .ds-header-text {
        min-width: 0;
    }

    /* The rail control is the switch alone, whatever the entitlement. The Moon
       Pass reason cannot share a 44px strip with Back, Close and a centred
       name — at the 84px column it used to get it rendered as "Moon Pass…" —
       so it keeps its full text for assistive tech through aria-describedby
       and stops competing for the rail. The switch still reads as inert. */
    .manga-game-preference.is-disabled,
    .lucid-game-preference.is-disabled {
        grid-template-columns: auto;
    }

    /* The rail control must be exactly its switch — 44px, no taller. The
       switch carries a 4px button margin, which made the control 52px and hung
       it 8px past the 44px strip onto the first choice below. The margin goes;
       the 44px tap target does not. */
    .manga-game-preference__switch,
    .lucid-game-preference__switch {
        margin: 0;
    }

.manga-game-preference.is-disabled .manga-game-preference__description,
    .lucid-game-preference.is-disabled .lucid-game-preference__description {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }

    /* The band runs the full width of the modal foot. This rule used to tuck an
       ABSOLUTELY POSITIONED cluster inside the Close hit area and cap it at
       104px — and that cap outlived the corner: with the band in flow it was
       still 104px wide, so the two pills wrapped onto separate rows and stopped
       sharing a centre line. A corner-era constraint on a flow element. */
    .battle-preferences {
        margin: 0 0 6px;
        gap: 6px 8px;
    }

    /* Back keeps the left corner. The cap is what makes the symmetric inset
       safe in every language — "← Zurück" is the widest label we ship. */
    .ds-back-btn {
        top: 0;
        transform: none;
        max-width: 132px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .ds-title {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .ds-title {
        font-size: clamp(18px, 4.5vmin, 24px);
        margin-bottom: 2px;
    }

    .ds-subtitle,
    .ds-char-desc {
        display: none;
    }

    .ds-character-grid {
        width: calc(100% - 8px);
        align-self: center;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        grid-template-rows: minmax(0, 1fr);
        gap: 8px;
        padding: 0;
    }

    .ds-character-card:hover:not(.disabled) {
        transform: none;
    }

    .ds-char-info {
        padding: 6px 4px;
    }

    .ds-char-name {
        margin: 0;
        font-size: clamp(12px, 3.1vmin, 16px);
    }

    .ds-character-pagination {
        margin-top: 0;
        padding: 0;
    }

    /* Phone pages contain at most three choices. The list is deliberately a
       non-scrolling flex lane: pagination is the only navigation model. */
    .ds-deck-list {
        height: 100%;
        gap: 4px;
    }

    .ds-deck-item {
        flex: 1 1 0;
        min-height: 44px;
        padding: 6px 12px;
        border-radius: 10px;
    }

    .ds-deck-item:hover {
        transform: none;
    }

    .ds-deck-name {
        font-size: clamp(13px, 3.2vmin, 16px);
        margin-bottom: 0;
    }

    .ds-deck-select-arrow {
        font-size: clamp(11px, 2.6vmin, 13px);
        white-space: nowrap;
    }

    .ds-official-starter-badge {
        min-height: 16px;
        padding: 0 6px;
        font-size: 9px;
    }
}

/* ==========================================================================
   AN INCOMPLETE DECK IS SHOWN, NOT HIDDEN.

   A match needs 15 cards, and the picker used to drop anything short of that
   from the list entirely — so a deck the player had built and saved was simply
   absent from their own launch screen, indistinguishable from one that had been
   deleted. Reported as "my summoners have empty decks ... no choice other than
   random"; four of the reporting account's seven decks hold 12-14 cards.

   It is listed greyed and unclickable now, with its own count where the
   "Select →" affordance would be, so the fix is obvious and lives one screen
   away in the Deck Builder.
   ========================================================================== */
.ds-deck-item--incomplete {
    opacity: 0.55;
    cursor: not-allowed;
    filter: saturate(0.65);
}

.ds-deck-item--incomplete:hover {
    /* No lift, no glow — it must not read as pressable. */
    transform: none;
}

.ds-deck-incomplete {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    opacity: 0.9;
}

/* A failed LOAD is not an empty collection, and must not borrow its copy. */
.ds-deck-load-error {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
}
