:root {
    /* Tracks the top safe-area inset that .mud-appbar absorbs so any layout sizing
       itself off 100vh/100dvh can subtract the same amount. Without this,
       calc(100dvh - Xpx) heights overshoot by the inset and push their bottom
       content (e.g. SearchTab action buttons) off screen. The viewport meta sets
       viewport-fit=cover, so env(safe-area-inset-top) returns the device's actual
       top inset wherever one exists (notched iPhone Safari, iPad PWA status bar,
       etc.) and falls back to 0px on browsers/devices without a safe area. */
    --pkmds-safe-area-top: env(safe-area-inset-top, 0px);
}

body, html {
    /* Top inset is handled by .mud-appbar below so the iOS status bar overlays the
       app bar (not a blank gap above it) when running as an installed PWA with
       apple-mobile-web-app-status-bar-style=black-translucent. Adding it here too
       would double-count and leave a 44px gap of background colour above the bar. */
    padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom, 16px) env(safe-area-inset-left, 8px);
    box-sizing: border-box;
}

/* Push the app bar's contents below the iOS status bar in standalone PWA mode.
   In a regular browser tab env(safe-area-inset-top) is 0, so this is a no-op. */
.mud-appbar {
    padding-top: env(safe-area-inset-top, 0px);
}

/* MudBlazor anchors snackbars a fixed 24px from the screen edges, which lands
   them under the iOS status bar (portrait) or the Dynamic Island / notch
   (landscape) with viewport-fit=cover. The snackbar container is position:fixed,
   so the body's safe-area padding doesn't reach it — shift each anchor by the
   matching inset instead. Insets are 0 where no safe area exists, so this is a
   no-op elsewhere. Same specificity as MudBlazor's rules, but our stylesheet
   loads after, so this wins. */
.mud-snackbar-location-top-left,
.mud-snackbar-location-top-center,
.mud-snackbar-location-top-right {
    top: calc(24px + var(--pkmds-safe-area-top));
}

.mud-snackbar-location-top-right,
.mud-snackbar-location-bottom-right {
    right: calc(24px + env(safe-area-inset-right, 0px));
}

.mud-snackbar-location-top-left,
.mud-snackbar-location-bottom-left {
    left: calc(24px + env(safe-area-inset-left, 0px));
}

/* The padding above grows .mud-appbar's effective height by the iOS status-bar
   inset, but MudBlazor's .mud-main-content padding-top is fixed to the bar's
   nominal height — so page content (e.g. the save-file title row in
   SaveFileComponent) renders behind the now-taller bar in standalone PWA mode.
   Match the inset here so MudMainContent's reserved space tracks the bar.
   Selector specificity (0,2,0) matches MudBlazor's .mud-appbar-dense rule and
   our stylesheet loads after, so this wins. We always use MudAppBar Dense, so
   the dense variant is the only one that needs covering. */
.mud-appbar-dense ~ .mud-main-content {
    padding-top: calc(var(--mud-appbar-height) - var(--mud-appbar-height) / 4 + env(safe-area-inset-top, 0px));
}

/* MudBlazor 9.6.0 renders the active tab panel with display: contents
   (MudBlazor/MudBlazor#13288), so the panel div no longer generates a layout box
   and every height/overflow rule this stylesheet puts on .mud-tab-panel is
   silently ignored. On narrow screens (< 1280px) the outer tab panel is the
   page's only scroll container (html/body are overflow:hidden), so 9.6.0 broke
   all scrolling there — box grid, edit form, and Trainer tab were clipped with
   no way to reach the overflow. Restore the 9.5.x block box. The later
   :has(...) rules that switch the Bank/Pokédex panels to display:flex repeat
   these active-panel terms and add their panel context, so they outrank this
   rule. Keep the state terms in sync across all three selectors. */
.mud-tabs-panels > .mud-tab-panel:not(.mud-tab).mud-tab-panel-active:not(.mud-tab-panel-hidden) {
    display: block;
}

.menu-container {
    padding-bottom: env(safe-area-inset-bottom, 16px); /* Fallback to 16px */
    padding-left: env(safe-area-inset-left, 8px); /* Fallback to 8px */
    box-sizing: border-box;
}

.pkm-sprite {

}

.pkm-sprite-hires {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    height: auto;
    object-fit: contain;
    pointer-events: none;
    opacity: 0;
}

.pkm-sprite-hires--loaded {
    animation: sprite-fade-in 0.15s ease forwards;
}

.pkm-sprite-hires--sm {
    width: 50%;
}

.pkm-sprite-hires--lg {
    width: 100%;
}

@keyframes sprite-fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.shiny-indicator-icon {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.alpha-indicator-icon {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.battle-team-indicator-icon {
    position: absolute;
    bottom: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.battle-team-lock-icon {
    position: absolute;
    bottom: 2px;
    left: 18px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.party-slot-indicator-icon {
    position: absolute;
    bottom: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.legality-indicator-icon {
    position: absolute;
    top: 20px;
    right: 2px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
    /* Material's "Filled" CheckCircle / Cancel icons are cutouts — the symbol shows
       whatever is behind the icon. Instead of painting a disc behind them, the
       markup uses solid symbols (Check / PriorityHigh / Close) inside a coloured
       disc wrapper: the disc provides the circle, the glyph is drawn solid white. */
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
}

.legality-indicator-icon--legal { background: var(--mud-palette-success); }
.legality-indicator-icon--fishy { background: var(--mud-palette-warning); }
.legality-indicator-icon--illegal { background: var(--mud-palette-error); }

/* Trade tab: dim the sprite (but not the indicator badges) so the user sees at a glance
   which Pokémon can't be transferred to the other save, while keeping the legality
   indicator readable. The .transfer-block-indicator-icon sits in the bottom-left corner
   opposite the shiny/legality badges. */
.slot-ineligible-transfer .pkm-sprite,
.slot-ineligible-transfer .pkm-sprite-hires {
    opacity: 0.4;
    filter: grayscale(0.7);
}

.transfer-block-indicator-icon {
    position: absolute;
    bottom: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    border-radius: 50%;
    background: var(--mud-palette-dark);
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.egg-indicator-icon {
    position: absolute;
    bottom: 2px;
    right: 2px;
    width: 32px;
    height: 32px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.held-item-icon {
    position: absolute;
    bottom: 2px;
    right: 2px;
    width: 24px;
    height: 24px;
    background-color: rgba(255, 255, 255, 0.9);
    border-radius: 50%;
    border: 1px solid rgba(0, 0, 0, 0.2);
    padding: 2px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    pointer-events: none;
    z-index: 10;
}

.status-indicator-icon {
    position: absolute;
    bottom: 24px;
    left: 2px;
    width: 16px;
    height: 16px;
    pointer-events: none;
    z-index: 10;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

.item-sprite {
    object-fit: contain;
    align-self: center;
}

.bag-data-grid {
    max-width: 100%;
    overflow-x: hidden;
}

.bag-data-grid .mud-table-container {
    max-height: calc(100vh - 380px - var(--pkmds-safe-area-top));
    max-height: calc(100dvh - 380px - var(--pkmds-safe-area-top));
    overflow-y: auto;
    overflow-x: hidden;
}

/* Mystery Gifts grid: cap height so the page doesn't grow taller than the viewport
   and the action buttons below the grid stay reachable. The Razor template keeps
   overflow + bottom safe-area padding inline; height lives here so there's a
   single source of truth. */
.mystery-gift-grid {
    max-height: calc(100vh - 340px - var(--pkmds-safe-area-top));
    max-height: calc(100dvh - 340px - var(--pkmds-safe-area-top));
}

/* LGPE box grid: short height on narrow viewports (the alert says "the box grid is
   scrollable"), and on xl+ it expands to fill the viewport minus the surrounding
   chrome. Replaces a Tailwind arbitrary-value class so we can subtract the iOS
   safe-area inset cleanly via var(). */
.lets-go-box-grid {
    height: 398px;
}

@media (min-width: 1280px) {
    .lets-go-box-grid {
        height: calc(100vh - 319px - var(--pkmds-safe-area-top));
        height: calc(100dvh - 319px - var(--pkmds-safe-area-top));
    }
}

.legality-report-table,
.encounter-db-table {
    max-width: 100%;
    overflow-x: hidden;
}

.legality-report-table .mud-table-container,
.encounter-db-table .mud-table-container {
    overflow-x: hidden;
}

/* Virtualizer-safe alternating row stripe — driven by RowClassFunc on logical data
   index so the colour is stable during virtual scroll (nth-child is DOM-position-based
   and gets out of sync as rows are recycled). */
.mud-table-row.row-alt .mud-table-cell {
    background-color: rgba(0, 0, 0, 0.04);
}

.mud-dark .mud-table-row.row-alt .mud-table-cell {
    background-color: rgba(255, 255, 255, 0.04);
}

/* Desktop (lg breakpoint): side-by-side layout with independently scrolling edit form */
@media (min-width: 1280px) {
    html, body {
        overflow: hidden;
    }

    /* Cascade flex heights from MudMainContent down to the party/box grid.
       height:100vh + box-sizing:border-box means the AppBar padding-top is absorbed
       into the explicit height, giving all flex:1 / height:100% descendants a
       definite size to resolve against (so inner overflow-y:auto produces a scrollbar). */
    .mud-main-content {
        height: 100vh;
        height: 100dvh;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .mud-main-content > .mud-container {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        min-height: 0;
    }

    /* Outer save-file tabs fill remaining vertical space */
    .save-file-outer-tabs {
        flex: 1;
        min-height: 0;
        overflow: hidden;
    }

    .save-file-outer-tabs > .mud-tabs-panels {
        min-height: 0;
        overflow: hidden;
    }

    /* Each outer tab panel fills its available height; overflow managed by inner content */
    .save-file-outer-tabs > .mud-tabs-panels > .mud-tab-panel {
        height: 100%;
        overflow: hidden;
    }

    /* Party/Box tab content: overflow managed internally */
    .party-box-tab-content {
        height: 100%;
        overflow: hidden;
    }

    /* Trade tab content: the parent .mud-tab-panel has overflow:hidden, which would
       clip the last row of box slots when no Pokémon is selected (no info panel to
       compress the grid into place). Let this scroll so the full content is reachable. */
    .trade-tab-content {
        height: 100%;
        overflow-y: auto;
    }

    /* Feebas tab content: the route map can be taller than the tab panel
       (Route 119 native height is 1553px), so let the tab content scroll
       internally rather than getting clipped by the panel's overflow:hidden. */
    .feebas-tab-content {
        height: 100%;
        overflow-y: auto;
    }

    /* Bank tab: the tab panel becomes a flex column so the card grid can
       fill remaining vertical space without a fragile calc() offset. */
    .save-file-outer-tabs > .mud-tabs-panels > .mud-tab-panel:not(.mud-tab).mud-tab-panel-active:not(.mud-tab-panel-hidden):has(.bank-tab-content) {
        display: flex;
        flex-direction: column;
    }

    .bank-tab-content {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .bank-tab-root {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .bank-card-grid {
        flex: 1;
        min-height: 0;
        overflow-y: auto;
    }

    /* Teams tab content: simple vertical scroll for team cards */
    .teams-tab-content {
        height: 100%;
        overflow-y: auto;
        overflow-x: hidden;
    }

    /* Trainer tab content: long form grid (per-game sections + headers + buttons)
       can exceed the panel height on shorter desktops. Without this, the panel's
       overflow:hidden clips the bottom fields. */
    .trainer-tab-content {
        height: 100%;
        overflow-y: auto;
        overflow-x: hidden;
    }

    /* Search / EncounterDB filter column: flex column so the action buttons are always
       visible at the bottom and only the expansion panels scroll. Height is sized to
       fit within the tab panel's overflow:hidden boundary. */
    .search-filter-panel {
        display: flex;
        flex-direction: column;
        height: calc(100dvh - 160px - var(--pkmds-safe-area-top));
        overflow: hidden;
    }

    /* Scrollable region that holds the expansion panels. */
    .search-filter-panel-scroll {
        flex: 1;
        overflow-y: auto;
        min-height: 0;
    }

    /* Action buttons — always visible at the bottom, never scrolled out of view. */
    .search-filter-panel-actions {
        flex-shrink: 0;
        padding-top: 8px;
        padding-bottom: 4px;
    }

    .party-box-grid.mud-grid {
        height: 100%;
        overflow: hidden;
        flex-wrap: nowrap;
    }

    .edit-form-column {
        height: 100%;
        overflow: hidden;
        display: flex;
        flex-direction: column;
    }

    .edit-form-column > .mud-paper {
        display: flex;
        flex-direction: column;
        flex: 1;
        overflow: hidden;
    }

    .edit-form-outer {
        display: flex;
        flex-direction: column;
        flex: 1;
        overflow: hidden;
    }

    .edit-form-outer > .flex {
        flex-shrink: 0;
    }

    .edit-form-tabs-container {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        min-height: 0;
    }

    .edit-form-tabs {
        flex: 1;
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
    }

    .edit-form-tabs .mud-tabs-panels {
        flex: 1;
        overflow-y: auto;
        min-height: 0;
        margin-bottom: 8px;
    }
}

/* Narrow screens (< 1280px): lock the viewport and give each outer tab panel its
   own independent scroll — mirrors the desktop cascade but without the side-by-side
   layout rules.  The Pokédex tab is already handled by .pokedex-tab-content
   (height:100%; overflow:hidden), so its panel shows no extra scrollbar. */
@media (max-width: 1279px) {
    html, body {
        overflow: hidden;
    }

    .mud-main-content {
        height: 100dvh;
        /* iOS WKWebView with viewport-fit=cover counts the home-indicator zone in
           100dvh, so the inner tab panel's overflow:auto can't scroll to reveal
           content covered by the indicator. Reserve safe-area space at the bottom;
           env(...) is 0 on browsers without a safe area. */
        padding-bottom: env(safe-area-inset-bottom, 0px);
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .mud-main-content > .mud-container {
        flex: 1;
        min-height: 0;
        overflow: hidden;
        display: flex;
        flex-direction: column;
        max-width: 100%;
    }

    .save-file-outer-tabs {
        flex: 1;
        min-height: 0;
        overflow: hidden;
    }

    .save-file-outer-tabs > .mud-tabs-panels {
        min-height: 0;
        overflow: hidden;
    }

    /* Each panel fills the available height; non-Pokédex tabs scroll within
       the panel rather than scrolling the page. No horizontal scroll. */
    .save-file-outer-tabs > .mud-tabs-panels > .mud-tab-panel {
        height: 100%;
        overflow-y: auto;
        overflow-x: hidden;
    }

    /* Pokédex panel owns its own scroll via the inner virtualized table; suppress the
       outer panel scrollbar so the table-container has a definite, viewport-bounded
       height to size against (otherwise the Virtualize component cuts the list off
       at whatever fits in its initial computed height — ~106 rows on phone portrait). */
    .save-file-outer-tabs > .mud-tabs-panels > .mud-tab-panel:not(.mud-tab).mud-tab-panel-active:not(.mud-tab-panel-hidden):has(.pokedex-tab-content) {
        overflow: hidden;
        display: flex;
        flex-direction: column;
    }
}

/* Tablet (md–lg): edit form inner tab panels also scroll independently */
@media (min-width: 768px) and (max-width: 1279px) {
    .edit-form-tabs .mud-tabs-panels {
        overflow-y: auto;
    }
}

/* Pokédex grid flex cascade — applies on every viewport so the inner MudDataGrid's
   .mud-table-container always has a definite height for Virtualize to size against. */
.pokedex-tab-content {
    height: 100%;
    overflow: hidden;
}

.pokedex-grid-wrapper {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.pokedex-grid-wrapper .pokedex-species-grid {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.pokedex-grid-wrapper .pokedex-species-grid > .mud-table-container {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

.square-slot {
    aspect-ratio: 1 / 1;
    border: 1px solid var(--mud-palette-lines-default);
}

.slot-selected {
    background-color: var(--pkmds-slot-selected, #7C3AED);
}

.current-tera-type-image {
    align-self: center;
}

.plus-nature label,
.plus-nature .mud-input-slot {
    color: #dc2626 !important;
    font-weight: 700;
}

.minus-nature label,
.minus-nature .mud-input-slot {
    color: #2563eb !important;
    font-weight: 700;
}

.stat-maxed label,
.stat-maxed .mud-input-slot {
    font-weight: 700 !important;
}

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
    padding: 1rem;
}

.form-field {
    margin-bottom: 0.5rem;
}

/* Full-width header used to divide a .form-grid into logical sections
   (Identity, Location, Currency, Unlocks, Editors, …). */
.form-section-header {
    grid-column: 1 / -1;
    margin-top: 0.5rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--mud-palette-divider);
    font-size: 0.875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mud-palette-text-secondary);
}

.playtime-group {
    grid-column: 1 / -1;
}

.playtime-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.playtime-label {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
}

.playtime-readout {
    font-family: var(--mud-typography-monospace-family, ui-monospace, monospace);
    font-size: 0.875rem;
    color: var(--mud-palette-text-primary);
}

.playtime-inputs {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    flex-wrap: wrap;
}

.playtime-inputs > .mud-input-control {
    flex: 1 1 6rem;
    min-width: 5rem;
    max-width: 10rem;
}

/* begin pkmds-splash — pre-boot loading screen. MudBlazor's CSS variables don't
   exist yet at this point, so colors must be literal hexes mirroring
   AppTheme.Colors. ThemeSyncTests scans this marker-bounded section and fails
   if a hex here drifts from the C# palette. */
.pkmds-splash {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.5rem;
    background: #FAFAF9;
    color: #1C1917;
    padding: 2rem;
    z-index: 1000;
}

.pkmds-splash__icon {
    position: relative;
    width: 9rem;
    height: 9rem;
    display: grid;
    place-items: center;
}

.pkmds-splash__mark {
    width: 6rem;
    height: 6rem;
    display: block;
}

/* Splash ships paired light/dark logos (icon-light.svg / icon-dark.svg)
   and shows whichever matches the active theme. theme-init.js sets
   data-theme on <html> before render; prefers-color-scheme is the
   no-JS fallback. */
.pkmds-splash__mark--dark {
    display: none;
}

@media (prefers-color-scheme: dark) {
    .pkmds-splash__mark--light {
        display: none;
    }

    .pkmds-splash__mark--dark {
        display: block;
    }
}

[data-theme="light"] .pkmds-splash__mark--light {
    display: block;
}

[data-theme="light"] .pkmds-splash__mark--dark {
    display: none;
}

[data-theme="dark"] .pkmds-splash__mark--light {
    display: none;
}

[data-theme="dark"] .pkmds-splash__mark--dark {
    display: block;
}

/* Progress ring wraps the mark — original .loading-progress
   selector preserved so existing Blazor JS keeps working. */
.pkmds-splash .loading-progress {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    transform: rotate(-90deg);
}

.pkmds-splash .loading-progress circle {
    fill: none;
    stroke: #E7E5E4;
    stroke-width: 4;
    cx: 50;
    cy: 50;
    r: 46;
}

.pkmds-splash .loading-progress circle:last-child {
    stroke: #1E40AF;
    stroke-linecap: round;
    /* circumference of r=46 is ~289. Stay percentage-based: viewBox is
       100x100 so 1% resolves to 1 user unit, and 2.89 * percentage
       produces the correct filled length at every load value.
       (Mixing percentage * length would invalidate the calc.) */
    stroke-dasharray: calc(2.89 * var(--blazor-load-percentage, 0%)), 1000%;
    transition: stroke-dasharray 0.1s ease-in-out;
}

.pkmds-splash__wordmark {
    font-family: 'Inter', 'Roboto', system-ui, sans-serif;
    font-weight: 800;
    font-size: 2.25rem;
    letter-spacing: -0.04em;
    line-height: 1;
}

.pkmds-splash__wordmark span {
    color: #1E40AF;
}

.loading-progress-text {
    font-family: 'Inter', 'Roboto', system-ui, sans-serif;
    font-size: 0.8125rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: #57534E;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* System dark — no-JS path */
@media (prefers-color-scheme: dark) {
    .pkmds-splash {
        background: #0C0A09;
        color: #FAFAF9;
    }

    .pkmds-splash .loading-progress circle {
        stroke: #57534E;
    }

    .pkmds-splash .loading-progress circle:last-child {
        stroke: #60A5FA;
    }

    .pkmds-splash__wordmark span {
        color: #60A5FA;
    }

    .loading-progress-text {
        color: #A8A29E;
    }
}

/* Explicit dark override (set by inline script / Blazor JS interop) */
[data-theme="dark"] .pkmds-splash {
    background: #0C0A09;
    color: #FAFAF9;
}

[data-theme="dark"] .pkmds-splash .loading-progress circle {
    stroke: #57534E;
}

[data-theme="dark"] .pkmds-splash .loading-progress circle:last-child {
    stroke: #60A5FA;
}

[data-theme="dark"] .pkmds-splash__wordmark span {
    color: #60A5FA;
}

[data-theme="dark"] .loading-progress-text {
    color: #A8A29E;
}

/* Explicit light override (beats system dark when user chose light) */
[data-theme="light"] .pkmds-splash {
    background: #FAFAF9;
    color: #1C1917;
}

[data-theme="light"] .pkmds-splash .loading-progress circle {
    stroke: #E7E5E4;
}

[data-theme="light"] .pkmds-splash .loading-progress circle:last-child {
    stroke: #1E40AF;
}

[data-theme="light"] .pkmds-splash__wordmark span {
    color: #1E40AF;
}

[data-theme="light"] .loading-progress-text {
    color: #57534E;
}

/* end pkmds-splash */

/* Drag and drop styles */
.slot-dragging {
    opacity: 0.5;
    cursor: grabbing !important;
}

.slot-drop-target {
    outline: 2px dashed #1976d2;
    outline-offset: -2px;
}

.slot-file-drop {
    outline: 3px solid #4caf50;
    outline-offset: -3px;
    background-color: rgba(76, 175, 80, 0.1);
}

/* Suppress iOS Safari's native text selection / long-press callout on Pokémon slots
   so they don't interfere with drag-to-sort. The wrapper carries .square-slot but
   draggable="true" lives on the inner div, so target both — and apply user-select
   to descendants because Safari otherwise selects the inner sprite nodes. */
.square-slot,
.square-slot * {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

.square-slot > div[draggable="true"] {
    cursor: grab !important;
    -webkit-user-drag: element;
    touch-action: manipulation;
}

.square-slot > div[draggable="true"]:active {
    cursor: grabbing !important;
}

.update-badge {
    width: 14px;
    height: 14px;
    animation: update-pulse 1.8s ease-in-out infinite;
}

@keyframes update-pulse {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(33, 150, 243, 0.7);
    }
    50% {
        box-shadow: 0 0 0 6px rgba(33, 150, 243, 0);
    }
}

#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

/* ── Pokémon Bank tab ── */

/* Reserve a single line for the nickname so cards are uniform height
   regardless of whether a nickname is present. */
.bank-card-nickname {
    min-height: 1.25em;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* ── Info popover sizing + narrow-viewport bottom sheet ──
   InfoButton / BagItemInfoButton apply .info-popover to their MudPopover.
   On desktop the popover stays a standard anchored popover capped at
   320px wide; on narrow viewports (≤767px — same breakpoint the dialog
   layout uses) it switches to a fixed-position bottom sheet that never
   runs off-screen regardless of where the info button sits on the page.
   Overrides carry !important because MudPopover's JS writes top/left
   directly to the element's inline style, and content inside the
   popover can otherwise push the popover wider than max-width implies. */
.info-popover.mud-popover {
    max-width: min(320px, calc(100vw - 16px)) !important;
    max-height: min(400px, calc(100vh - 48px)) !important;
    overflow-y: auto;
    z-index: 1200;
}

@media (max-width: 767px) {
    .info-popover.mud-popover {
        position: fixed !important;
        top: auto !important;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 16px) !important;
        left: 8px !important;
        right: 8px !important;
        max-width: none !important;
        max-height: 70vh !important;
    }
}

/* ── App-bar theme toggle contrast ──
   The appbar MudToggleGroup uses Color.Inherit so the icons pick up the
   appbar's white text color. But MudToggleItem's selected state (Variant.Filled
   with Color.Inherit) renders without a background fill, making the selection
   indistinguishable from the unselected outlined items. Restore the previous
   button-group behavior: a semi-transparent white fill on the active item, and
   a slight opacity dim on inactive items so the selection stands out clearly. */
.appbar-theme-toggle .mud-toggle-item {
    color: var(--mud-palette-appbar-text);
}

.appbar-theme-toggle .mud-toggle-item:not(.mud-toggle-item-selected) {
    opacity: 0.7;
}

.appbar-theme-toggle .mud-toggle-item:not(.mud-toggle-item-selected):hover {
    opacity: 1;
    background-color: rgba(255, 255, 255, 0.1);
}

.appbar-theme-toggle .mud-toggle-item-selected {
    background-color: rgba(255, 255, 255, 0.32);
    opacity: 1;
}

/* ── Choose Evolution picker — high-contrast selected item ──
   MudBlazor's default selected-list-item styling pairs primary-colored
   text with a pale primary-hover background, which is near-unreadable
   on the branching evolution picker. Override with a full primary fill
   and the contrasting primary-text foreground so the highlighted
   evolution actually stands out. */
.evolution-picker-list .mud-list-item.mud-selected-item {
    background-color: var(--mud-palette-primary) !important;
    color: var(--mud-palette-primary-text) !important;
}

.evolution-picker-list .mud-list-item.mud-selected-item .mud-typography {
    color: var(--mud-palette-primary-text) !important;
}

/* ── Responsive full-screen dialog layout ──
   MudBlazor bakes FullScreen into the dialog at open-time and won't re-react
   to viewport changes. These rules force any open dialog into full-screen
   rendering when the viewport is ≤767px (phones + narrow iPad windows), so
   the layout stays right whether the dialog was opened narrow, opened wide,
   or the window is resized while it's open. Covers the nested-scrollbar
   cases from issue #740 while leaving moderately-narrow desktop browser
   windows (768–959px) as regular centered dialogs.

   Selector specificity (0,2,0) beats MudBlazor's own `.mud-dialog-width-*`
   and `.mud-dialog-fullscreen` rules (0,1,0), so these overrides win. */
@media (max-width: 767px) {
    .mud-dialog-container > .mud-dialog {
        width: 100%;
        height: 100%;
        margin: 0;
        max-width: 100%;
        max-height: none;
        border-radius: 0;
        overflow-y: hidden;
    }

    .mud-dialog-container > .mud-dialog .mud-dialog-content {
        overflow-y: auto;
        overflow-x: hidden;
        max-height: none;
        padding-bottom: env(safe-area-inset-bottom, 16px);
    }

    .mud-dialog-container > .mud-dialog .mud-dialog-actions {
        position: sticky;
        bottom: 0;
        background: var(--mud-palette-surface);
        z-index: 2;
        padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
    }

    .mud-dialog-container > .mud-dialog .search-filter-panel-scroll,
    .mud-dialog-container > .mud-dialog .mud-table-container,
    .mud-dialog-container > .mud-dialog .bag-data-grid .mud-table-container,
    .mud-dialog-container > .mud-dialog .legality-report-table .mud-table-container,
    .mud-dialog-container > .mud-dialog .encounter-db-table .mud-table-container {
        max-height: none;
    }
}

/* ── Issue #774 — accidental zoom mitigations ──
   Three independent fixes; none of them disable user-scalable so pinch-zoom
   for accessibility still works.

   1. Batch Editor script textarea: monospace 0.85rem (~13.6px) is below the
      16px threshold Android Chrome uses to suppress focus auto-zoom. Bump to
      16px on touch-only devices; keep the tighter 0.85rem on hover-capable
      desktops where it doesn't trigger zoom. */
.batch-script-editor textarea.mud-input-slot,
.batch-script-editor .mud-input-slot.mud-input-root {
    font-family: monospace;
    font-size: 0.85rem;
}

@media (hover: none) and (pointer: coarse) {
    .batch-script-editor textarea.mud-input-slot,
    .batch-script-editor .mud-input-slot.mud-input-root {
        font-size: 16px;
    }
}

/* 2. Hisuian Pokédex research-task table: MudSimpleTable doesn't wrap itself
      in a .mud-table-container, so its ~620px of min-width columns blew out
      the parent panel and got clipped by .mud-tab-panel { overflow-x: hidden }.
      Give it its own scroll container so the user can pan the table without
      having to zoom the whole page. */
.la-research-task-table-wrapper {
    overflow-x: auto;
}

/* 3. Tab scroll-arrows + pagination buttons + box navigation: rapid-tap to
      advance reads as a double-tap to Chrome's gesture detector and triggers
      zoom. touch-action: manipulation disables double-tap-zoom on the targeted
      element while leaving pinch-zoom intact.

      touch-action is NOT an inherited property, so the rule has to land on the
      actual <button> element receiving the tap — not just an ancestor. MudTabs
      renders scroll arrows as <div class="mud-tabs-scroll-button"><button
      class="mud-icon-button">…</button></div>, and MudPagination renders each
      page control as <li><button class="mud-icon-button|mud-button">…</button></li>.
      Without explicit selectors on those inner buttons, taps that land on the
      button itself (most of them — the icon fills the button) zoom; only taps
      that hit the wrapper's padding advance cleanly. That's the source of the
      "only sometimes" zoom on the main tab strip's scroll arrows. */
.mud-tabs .mud-tabs-toolbar,
.mud-tabs .mud-tab,
.mud-tabs .mud-tabs-scroll-button,
.mud-tabs .mud-tabs-scroll-button .mud-icon-button,
.mud-pagination,
.mud-pagination .mud-icon-button,
.mud-pagination .mud-button,
.box-navigation .mud-icon-button {
    touch-action: manipulation;
}

/* 4. Appbar overflow menu: cap height at viewport-minus-appbar so it only
      scrolls on short viewports, not on every desktop where the full list
      fits comfortably. MudMenu's MaxHeight parameter takes a fixed pixel
      int and adds an inline style + overflow-y-auto regardless of content
      fit; using PopoverClass with a calc() max-height lets the popover
      grow to its natural height when there's room, and only scroll when
      there genuinely isn't.

      The popover is portal-mounted at the body level, so this rule lives in
      the global stylesheet rather than MainLayout's scoped CSS. */
.appbar-menu-popover {
    max-height: calc(100vh - 64px);
    overflow-y: auto;
}
