/* ---- */
/* Base */
/* ---- */

:root {
    /* Colors */
    --bg: #0d0d0f;
    --blackText: #0d0d0f;
    --surface: #16161a;
    --card: #1e1e24;
    --border: #2a2a35;
    --accent: #f0c040;
    --text: #f0ede8;
    --muted: #7a7a8a;
    --success: #4caf7d;
    --error: #e05c5c;

    /* Fonts */
    --primaryFont: 'DM Mono', monospace;
    --titleFont: 'Playfair Display', serif;
}

:root[data-theme="light"] {
    --bg: #eeeeec;
    --surface: #eceae3;
    --card: #ffffff;
    --border: #d6d2c8;
    --text: #1c1b19;
}

* {
    /* Include borders for all measurements */
    box-sizing: border-box;
    margin: 0;
    padding: 0;

    /* Disable all highlighting/selection */
    user-select: none;
    -webkit-tap-highlight-color: transparent; /* Kill the blue tap flash on touch */

    /* Form controls don't inherit the page font by default */
    font-family: inherit;
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--primaryFont);
    height: 100vh;  /* fallback for browsers without dvh */
    height: 100dvh; /* page never exceeds one screen */
    display: flex;
    flex-direction: column;
    align-items: center;
    overflow: hidden auto; /* auto allows mobile pull-down refresh */
}

/* Noise texture overlay */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg width='512' height='512' viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.04'/%3E%3C/svg%3E");
    background-size: 256px 256px;
    pointer-events: none;
}

img {
    pointer-events: none; /* Prevent image dragging */
    background: #fff; /* For transparent diagrams (e.g. Rhythm) */
    border-radius: 6px;
}

/* Color classes */
.skip {color: var(--accent)  !important}
.good {color: var(--success) !important}
.bad  {color: var(--error)   !important}

/* Shared pill button style */
.pill {
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    padding: 0.5rem 1rem;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--muted);
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.2s;
}
button.pill.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--blackText);
}
button.pill:not(.active):hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* Bare icon button (dismiss x's) */
.iconBtn {
    display: flex;
    background: none;
    border: none;
    color: var(--muted);
    cursor: pointer;
}

/* Every summary is a custom-styled toggle - hide the default details marker */
summary {
    list-style: none;
    &::-webkit-details-marker {display: none}
}

/* Floating menu panel (view switch, download list) */
.menuPanel {
    position: absolute;
    top: 100%;
    margin-top: 0.4rem;
    margin-bottom: 0.4rem; /* If list ever inverts */
    z-index: 50;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 10px 28px rgb(0 0 0 / 0.35);
}

/* Utility */
.hidden {display: none !important}

/* Segmented pill group */
.segmented {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;

    button.active {pointer-events: none}
}

/* Slider toggle (settings panels, landing collegiate) */
.toggleSetting {
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;

    input {display: none}

    .toggle {
        width: 36px;
        height: 20px;
        background: var(--border);
        border-radius: 10px;
        position: relative;
        transition: background 0.2s;
        flex-shrink: 0;

        &::after {
            content: "";
            position: absolute;
            top: 2px;
            left: 2px;
            width: 16px;
            height: 16px;
            background: var(--muted);
            border-radius: 50%;
            transition: all 0.2s;
        }
    }

    input:checked + .toggle {
        background: var(--accent);

        &::after {
            left: 18px;
            background: var(--bg);
        }
    }
}
/* Override to show native title tooltip */
#strategyRow button.active {pointer-events: auto}

/* ------ */
/* Shared */
/* ------ */

header {
    width: 100%;
    padding: 1rem;
    display: flex;
    gap: 1rem;
    border-bottom: 1px solid var(--border);
    position: relative;

    #headerTitle {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: 1rem;
        /* Make the tagline disappear on smaller devices */
        font-size: 1.8rem;
        max-height: 1lh;
        overflow: hidden;

        /* View selector */
        #viewSwitch {
            flex-shrink: 0;

            /* The logo is the menu button */
            #logo {
                font-family: var(--titleFont);
                font-size: 1.8rem;
                font-weight: 900;
                color: var(--text);
                width: fit-content;
                cursor: pointer;

                span {color: var(--accent)}

                /* FS wordmark links home */
                a {
                    color: inherit;
                    text-decoration: none;

                    @media (hover: hover) {
                        &:hover {color: var(--accent)}
                    }
                }

                &:focus-visible {
                    outline: 2px solid var(--accent);
                    outline-offset: 3px;
                    border-radius: 3px;
                }
            }

            .switchCaret {
                margin-left: 0.4rem;
                color: var(--muted);
                vertical-align: middle;
                transition: transform 0.2s;
            }
            &[open] .switchCaret {transform: rotate(180deg)}

            .viewMenu {
                /* Width is set in HTML script, rest comes from .menuPanel */
                left: 1rem;
                display: flex;
                flex-direction: column;
                gap: 0.15rem;
                padding: 0.4rem;

                a {
                    display: flex;
                    flex-direction: column;
                    gap: 0.1rem;
                    padding: 0.55rem 0.7rem;
                    border-radius: 5px;
                    text-decoration: none;
                    color: var(--text);

                    strong {
                        font-size: 0.8rem;
                        font-weight: 500;
                    }

                    &:hover {background: var(--card)}

                    /* Current view */
                    &[aria-current="page"] strong {color: var(--accent)}
                }
            }
            &:not([open]) .viewMenu {display: none}
        }

        #tagline {
            font-size: 0.7rem;
            color: var(--muted);
            letter-spacing: 0.15em;
            text-transform: uppercase;
            white-space: nowrap;
        }
    }

    #settingsToggle {
        margin-left: auto;
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        gap: 0.4em;

        .cog {
            /* Largest scale that fills the pill height without growing it */
            font-size: 1.7em;
            line-height: 1;
        }
    }
}

/* Mode selector */
#modeBar {
    display: flex;
    gap: 0.5rem;
    padding: 1rem 1rem 0;
    width: 100%;
    /* HTML Script collapses buttons on overflow */
    flex-wrap: nowrap;
    overflow: hidden;

    button {
        flex-shrink: 0;
        white-space: nowrap;

        /* Selected discipline is inert while a state toggle (e.g. gallery view switch) stays clickable */
        &[data-mode].active {pointer-events: none}
    }

    /* Collapsed mode bar swaps the button row for dropdown (style in .modeDropdown) */
    #modeSelect {display: none}

    &.collapsed {
        button[data-mode] {display: none}
        #modeSelect {display: inline-block}
        /* Swaps to dropdown instead */
        body:not(.gallery) & {display: none}
    }
}

/* Match pill style */
.modeDropdown {
    appearance: none;
    field-sizing: content;
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--blackText);
    max-width: 100%;
    padding-right: 2.2rem; /* space for dropdown caret */
    /* dark caret to match text */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%230d0d0f' stroke-width='1.5' d='M2.5 4.5 6 8l3.5-3.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.85rem center;

    &:focus {outline: none} /* no white focus ring */

    /* field-sizing so it grows to fit every option while open */
    &:open {field-sizing: fixed}

    /* The OS-rendered option list can't be themed */
    option {
        color: CanvasText;
        background-color: Canvas;
    }

    /* Where supported, render the option list in-page, styled like the nav .viewMenu */
    @supports (appearance: base-select) {
        appearance: base-select;
        &::picker-icon {display: none} /* The pill keeps its own caret */

        &::picker(select) {
            appearance: base-select;
            margin-top: 0.4rem;
            padding: 0.4rem;
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: 8px;
            box-shadow: 0 10px 28px rgb(0 0 0 / 0.35);
        }
        /* Display is how the UA hides the closed picker, so only touch it while open */
        &:open::picker(select) {
            display: flex;
            flex-direction: column;
            gap: 0.15rem;
        }

        option {
            padding: 0.55rem 0.7rem;
            border-radius: 5px;
            background: none; /* Undo the Canvas fallback */
            color: var(--text);
            font-size: 0.8rem;
            font-weight: 500;
            text-transform: none; /* Undo the pill's all-caps inside the list */
            letter-spacing: normal;

            &::checkmark {display: none} /* The current option goes gold instead */
            &:hover, &:focus-visible {background: var(--card)}
            &:checked {color: var(--accent)}
        }
    }
}

/* Settings panel */

#settingsPanel {
    width: 100%;
    padding: 1rem;
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1rem;
    animation: slideDown 0.2s ease;

    .settingsGroup {flex: 1 1 220px}

    .settingsLabel {
        font-size: 0.6rem;
        text-transform: uppercase;
        letter-spacing: 0.12em;
        color: var(--muted);
        display: flex;
        justify-content: space-between;
        margin-bottom: 0.75rem;
    }

    .toggleGroup {
        display: flex;
        flex-direction: column;
        align-self: flex-end;
        gap: 0.85rem;
    }

    /* For toggle info */
    .toggleRow {
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .infoTip {
        position: relative;
        display: inline-flex;
        padding: 0;
        border: none;
        background: none;
        color: var(--muted);
        cursor: help;

        &:hover, &:focus-visible {color: var(--accent)}

        .infoIcon {
            width: 15px;
            height: 15px;
            display: block;
        }

        .tipBubble {
            position: fixed;
            width: max-content;
            max-width: min(240px, calc(100vw - 2rem));
            padding: 0.55rem 0.7rem;
            background: var(--card);
            color: var(--text);
            border: 1px solid var(--border);
            border-radius: 6px;
            font-size: 0.62rem;
            line-height: 1.45;
            letter-spacing: 0.03em;
            text-transform: none;
            box-shadow: 0 6px 22px rgb(0 0 0 / 0.35);
            opacity: 0;
            visibility: hidden;
            transition: opacity 0.15s ease;
            pointer-events: none;
            z-index: 100;
        }

        &:hover .tipBubble,
        &:focus .tipBubble,
        &:focus-visible .tipBubble {
            opacity: 1;
            visibility: visible;
        }
    }
}
@keyframes slideDown {
    from {
        opacity: 0;
        translate: 0 -15px;
    }
}

/* Open panels grow page by their own height (--panelH/--drawerH), keeping arena size locked */
body:not(.gallery):has(#settingsPanel:not(.hidden), #endlessDrawer:not(.hidden) #drawerContent:not(.hidden)) {
    height: calc(100dvh + var(--panelH, 0px) + var(--drawerH, 0px));
    overflow-y: auto;
}

/* Page only grows by the leftover */
@media (min-width: 601px) and (min-height: 601px) {
    body:not(.gallery):has(#settingsPanel:not(.hidden), #endlessDrawer:not(.hidden) #drawerContent:not(.hidden)) {
        height: auto;
        min-height: 100dvh;

        #arena {flex-basis: auto} /* Floors the arena at its content height, card never squeezed */
    }
}

/* Stats bar */
#statsBar {
    --statH: calc(0.8125rem + 0.15rem + 1.3125rem); /* Stat height: 13px label + gap + 21px value */
    display: flex;
    /* Wrap the progress meter */
    column-gap: clamp(0.75rem, 5vmin - 0.5rem, 2rem); /* Scales the width with the viewport size */
    row-gap: 0.5rem;

    padding: 1rem;
    width: 100%;
    align-items: center;
    font-size: 0.75rem;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
    text-transform: uppercase;

    /* Split from bar so it can overflow and hide on narrow widths */
    #statsRow {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        column-gap: clamp(0.75rem, 5vmin - 0.5rem, 2rem); /* Scales the width with the viewport size */
        row-gap: 2rem;
        max-height: var(--statH); /* Prevents row from growing */
        overflow: hidden;
    }

    /* Endless leaves only the toggle, so it shares the stats row instead of wrapping under it */
    &:has(#endlessToggle.active) {
        column-gap: 0.5rem; /* Clears the now unnecessary 2rem gap between toggle and stats */
        #statsRow {flex: 1}
        #progressContainer {
            flex: 0 0 auto;
            gap: 0;
        }

        /* Endless drops progress bar and the stopwatch */
        #progressMain {display: none}
        .stat:has(#statTime) {display: none}
    }

    /* Hides endless strategy if it doesn't fit*/
    #strategyRow {
        flex-wrap: nowrap;
        flex-shrink: 0;
    }

    .stat {
        display: flex;
        flex-direction: column;
        gap: 0.15rem;

        label {
            font-size: 0.6rem;
            letter-spacing: 0.12em;
        }

        p {
            font-size: 1rem;
            color: var(--text);
            font-weight: 500;
        }
    }

    #progressContainer {
        flex: 1;
        display: grid;
        grid-template-columns: 1fr auto;
        gap: 0.6rem; /* Gap between progress bar and toggle */

        #progressMain {
            min-width: 150px;
            justify-content: center;
        }

        #progressBar {
            height: 3px;
            background: var(--border);
            border-radius: 2px;
            margin-top: 0.4rem;

            #progressFill {
                width: 0;
                height: 100%;
                background: var(--accent);
                border-radius: 2px;
                transition: width 0.4s ease;
            }
        }

        #endlessToggle {
            padding: 0;
            position: relative;
            container-type: inline-size;
            min-height: var(--statH); /* Pinned to stat height */

            /* Circular */
            aspect-ratio: 1;
            border-radius: 50%;

            /* Softly pulse when active */
            &.active {
                background: transparent;
                border-color: currentColor;
                color: var(--accent);
                animation: pulse 2.4s ease-in-out infinite;

                @media (prefers-reduced-motion: reduce) {animation: none}
            }

            span {
                position: absolute;
                inset: 0;
                display: grid;
                place-items: center;

                svg {width: 70cqi}
            }
        }
    }
}

/* Softly pulse when active */
@keyframes pulse {
    50% {opacity: 0.55}
}

/* Endless drawer: collapsible card-order pill row under the stats bar */
#endlessDrawer {
    position: relative;
    width: 100%;

    #drawerContent {
        padding: 1rem;
        border-bottom: 1px solid var(--border);
        animation: slideDown 0.2s ease;
    }

    /* Collapse x pinned to the panel's top-right corner (base recipe from .iconBtn) */
    #drawerClose {
        position: absolute;
        top: 0.5rem;
        right: 0.5rem;
        padding: 0.25rem;
        transition: color 0.2s;

        &:hover {color: var(--accent)}
    }

    /* Pull button: a circle straddling the bar border when collapsed, the drawer border when open */
    #drawerCaret {
        position: absolute;
        top: 100%;
        left: 50%;
        translate: -50% -50%; /* Center the circle on the border line */
        z-index: 10; /* Above the card's top edge on tight mobile spacing */
        display: flex;
        align-items: center;
        justify-content: center;
        height: 1.5rem; /* Bottom half (0.75rem) stays inside the arena's 1rem mobile padding, clear of the card */
        aspect-ratio: 1;
        padding: 0;
        background: var(--bg); /* Masks the border line passing behind */
        border: 1px solid var(--border);
        border-radius: 50%;
        color: var(--muted);
        cursor: pointer;
        transition: color 0.2s, border-color 0.2s;

        &:hover {
            color: var(--accent);
            border-color: var(--accent);
        }

        svg {transition: transform 0.2s}
    }

    /* Caret points down to open, up to collapse */
    &:has(#drawerContent:not(.hidden)) #drawerCaret svg {transform: rotate(180deg)}
}

/* Formation diagram */
.diagram {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* Thumbnail matches size of original (no swap jitter) */
.cardContent > .diagram {
    width: auto;
    height: 100%;
}

/* Transparent line-art SVGs (the CF USPA sets) float on the card; without this the
   global white img background boxes them in (and inverts to a black box in dark) */
:root[data-diagram="dark"] :is(.diagram, #gallery img) {background: transparent}

:root[data-theme="dark"][data-diagram="dark"] :is(.diagram, #gallery img) {
    filter: invert(1);
}

:root[data-theme="dark"][data-diagram="light"] :is(.diagram, #gallery img) {
    box-shadow: 0 4px 14px rgb(0 0 0 / 0.5); /* Softens the border */
}

/* Add captions to images that need them */
figure {
    margin: 0;
    display: flex;
    flex-direction: column;
    border-radius: 6px;
    overflow: hidden;
    container-type: inline-size; /* lets the caption size off the diagram width (cqw) */

    img {
        width: 100%;
        height: auto;
        border-radius: 0;
    }

    figcaption {
        background: #fff;
        color: var(--blackText);
        font-family: 'Open Sans', sans-serif;
        font-weight: 700;
        font-size: 6cqw; /* matches the USPA diagrams' baked labels at any display size */
        line-height: 1;
        text-align: center;
        padding-bottom: 0.4rem;
    }
}

.captionedDiagram {
    width: 100%;
    max-height: 100%;
    min-height: 0;
    background: #fff;

    img {min-height: 0} /* Allow scaling */

    figcaption.captionHidden {visibility: hidden} /* Hide on card front */
}

/* Split blocks into 3rds horizontally (index.html only emits the markup on wide cards) */
.splitDiagram {
    display: flex;
    max-width: 100%;
    max-height: 100%;
    min-height: 0;
    border-radius: 6px;
    overflow: hidden; /* Allows for rounded border */

    img {
        flex: 1;
        min-width: 0;
        min-height: 0;
        object-fit: cover;
        border-radius: 0;
    }

    /* Divider */
    img + img {border-left: 2px solid #000}
}

:root[data-theme="dark"][data-diagram="light"] :is(.captionedDiagram, #gallery figure, .splitDiagram:not([data-diagram="dark"])) {
    box-shadow: 0 4px 14px rgb(0 0 0 / 0.5);
}

:root[data-theme="dark"][data-diagram="light"] :is(.captionedDiagram .diagram, #gallery figure img, .splitDiagram .diagram) {
    padding: 0;
    box-shadow: none;
}

:root[data-theme="dark"] .splitDiagram[data-diagram="dark"] .diagram {filter: invert(1)}

/* Toast messages */
.toast {
    width: 100%;
    border: 1px solid var(--accent);
    color: var(--accent);
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-align: center;
    border-radius: 4px;
    max-width: 180px;
    padding: 12px 16px;
    position: fixed;
    bottom: 30px;
    left: 50%;
    transform: translateX(-50%);
    pointer-events: none;
    animation: slideToastUp 0.5s ease-out, hideToast 1.25s forwards 1s;
    background: var(--card);

    /* For larger messages */
    &.long {
        max-width: 300px;
        animation-delay: 0s, 4s;
    }
}
@keyframes slideToastUp {from {translate: 0 15px}}
@keyframes hideToast {to {opacity: 0}}

/* Floating round buttons, stacked bottom-left */
#cornerButtons {
    position: fixed;
    bottom: 1rem;
    left: 1rem;
    z-index: 100;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;

    a {
        display: flex;
        align-items: center;
        justify-content: center;
        width: clamp(2.5rem, 6vmin, 4rem);
        height: clamp(2.5rem, 6vmin, 4rem);
        border: 1px solid var(--border);
        border-radius: 50%;
        background: var(--bg);
        color: var(--muted);
        cursor: pointer;
        opacity: 0.75;
        transition: opacity 0.15s ease, color 0.15s ease, border-color 0.15s ease;

        /* Let the report flag scale */
        svg {
            width: 45%;
            height: 45%;
        }

        /* Gate hover to real-pointer devices so a tap doesn't leave it stuck-highlighted on touch */
        @media (hover: hover) {
            &:hover {
                opacity: 1;
                color: var(--accent);
                border-color: var(--accent);
            }
        }
    }

    #sourceLink {
        overflow: hidden;
        padding: 8px;

        svg {
            width: 100%;
            height: 100%;
        }
    }
}

/* -------------- */
/* Flashcard View */
/* -------------- */

/* Main arena */
#arena {
    flex: 1;
    min-height: 0; /* allow the arena (and its card) to shrink below content height instead of overflowing */
    width: 100%;
    max-width: 700px;
    padding: 2.5rem 1.5rem 1rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
}

/* Flashcard */
#card {
    animation: slideUp 0.35s ease;
    position: relative;
    width: 100%;
    aspect-ratio: 20 / 17;
    flex: 0 1 auto;
    min-height: 200px;
    transform-style: preserve-3d;
    transition: transform 0.6s cubic-bezier(0.23, 1, 0.32, 1);
    transform: perspective(1200px); /* 3D flip effect. A transform so the script easily override it */

    &.flipped {transform: perspective(1200px) rotateY(180deg)}
}
/* Animate card entering the page */
@keyframes slideUp {
    from {
        opacity: 0;
        translate: 0 20px;
    }
}

.cardFace {
    position: absolute;
    inset: 0;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 12px;
    outline: 1px solid transparent; /* Smoothes out edges when card moving */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    backface-visibility: hidden;
    overflow: hidden;
    padding: 1rem 1.2rem;

    &.back {transform: rotateY(180deg)}
}

/* Wraps card content */
.cardContent {
    flex: 1;
    min-height: 0;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;

    /* Loading indicators anchor to the positioned .cardFace: spinner dead-center, bar along the bottom edge.
       loaderReveal delays them so a cached/instant paint never flashes them */
    &.thumbLoading::after {
        content: "";
        position: absolute;
        inset: 0;
        margin: auto;
        width: 2.4rem;
        height: 2.4rem;
        border: 3px solid var(--muted);
        border-top-color: var(--accent);
        border-radius: 50%;
        animation: spin 0.7s linear infinite, loaderReveal 0.2s linear 0.15s both;
    }

    &.fullLoading::before {
        content: "";
        position: absolute;
        bottom: 0;
        left: -35%;
        width: 35%;
        height: 3px;
        background: linear-gradient(90deg, transparent, var(--accent), transparent);
        animation: barSlide 1.1s ease-in-out infinite, loaderReveal 0.2s linear 0.12s both;
    }
}
@keyframes spin {to {transform: rotate(360deg)}}
@keyframes barSlide {from {left: -35%} to {left: 100%}}
@keyframes loaderReveal {from {opacity: 0} to {opacity: 1}}

/* Letter / number prompt */
.formationLabel {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;

    .labelKey {
        font-weight: 900;
        font-size: clamp(3.5rem, 20vw, 8rem);
        line-height: 1;
        color: var(--text);
    }
}

/* Name prompt */
.formationName {
    font-family: var(--titleFont);
    font-weight: bold;
    text-align: center;
    text-transform: capitalize;
    font-size: clamp(1.8rem, 7vw, 3.5rem);
    line-height: 1.2;
    padding: 0 1rem;
}

/* Swipe overlays */
.skipOverlay    {border-color: var(--accent)}
.correctOverlay {border-color: var(--success)}
.wrongOverlay   {border-color: var(--error)}
.skipOverlay::after,
.correctOverlay::after,
.wrongOverlay::after {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0.25;
}
.skipOverlay::after    {background: linear-gradient(135deg, var(--accent),  var(--card))}
.correctOverlay::after {background: linear-gradient(135deg, var(--success), var(--card))}
.wrongOverlay::after   {background: linear-gradient(135deg, var(--error),   var(--card))}

/* Keyboard hints */
#keyboardHints {
    display: grid;
    width: 100%;
    grid-template-columns: 1fr auto 1fr;
    font-size: 0.6rem;
    color: var(--muted);
    letter-spacing: 0.08em;

    span:last-child {direction: rtl} /* Keeps the arrow on the edge of the card */

    span::first-letter {
        padding: 0.1rem 0.35rem;
        margin: 0 1ch;
        border: 1px solid var(--border);
        border-radius: 3px;
        color: var(--muted); /* Keycap stays muted when the word is tinted (.skip/.good/.bad) */
    }
}

/* Action buttons */
#actionBar {
    display: flex;
    gap: 1rem;
    width: 100%;
    justify-content: center;

    button {
        font-size: 0.75rem;
        background: transparent;
        letter-spacing: 0.1em;
        text-transform: uppercase;
        padding: 0.85rem 2rem;
        border-radius: 4px;
        border: 1px solid;
        cursor: pointer;
        transition: all 0.15s;
        flex: 1;
    }

    #flip {
        border-color: var(--border);
        color: var(--text);
        max-width: 220px;

        &:hover {
            border-color: var(--accent);
            color: var(--accent);
        }
    }

    #correct, #wrong, #restart, #replay {
        max-width: 180px;

        &:hover {color: var(--text)}
    }

    #correct, #restart {
        border-color: var(--success);
        color: var(--success);

        &:hover {background: var(--success)}
    }

    #wrong, #replay {
        border-color: var(--error);
        color: var(--error);

        &:hover {background: var(--error)}
    }
}

#watchBar {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 1.4rem;

    #watchLink {
        display: inline-flex;
        align-items: center;
        gap: 0.5em;
        font-size: 0.7rem;
        letter-spacing: 0.1em;
        text-transform: uppercase;
        color: var(--muted);
        text-decoration: none;
        cursor: pointer;
        transition: color 0.15s ease;

        svg {
            height: 1.3em;
            width: auto;
        }

        &:hover {color: var(--accent)}
    }
}

/* ----------- */
/* Done Screen */
/* ----------- */

#done {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
}


/* ------------- */
/* Card Carousel */
/* ------------- */
#carouselSection {
    /* Used by results screens + gallery carousel */
    --carouselH: clamp(150px, 34dvh, 280px); /* Bounded so the results screen scrolls on mobile */
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;

    .label {
        font-size: 0.75rem;
        text-transform: uppercase;
        letter-spacing: 0.15em;
        color: var(--muted);
    }
}

/* Desktop leaves the cards uncapped */
@media (min-width: 601px) {
    #carouselSection {--carouselH: min(46vh, 460px)}
}

/* Chips for each card in the carousel */
#carouselChips {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
}

/* Chip button, shared by the key chips and the auto-scroll toggle */
.carouselChip {
    font-size: 0.72rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--muted);
    border-radius: 4px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;

    /* Highlight the in-focus chip */
    &.lit {
        border-color: var(--accent);
        background: var(--accent);
        color: var(--blackText);
    }
}

/* Holds the strip plus its overlaid carets */
#carouselViewport {
    position: relative; /* relative so the carets miss no width */
    width: 100%;
}

/* Prev/next carets overlay the strip edges, shown only while it scrolls */
.carouselCaret {
    position: absolute;
    top: 50%;
    translate: 0 -50%;
    z-index: 2; /* above the edge cards */
    display: none; /* revealed when the strip overflows */
    align-items: center;
    justify-content: center;
    height: 2.4rem;
    aspect-ratio: 1;
    padding: 0;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--muted);
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;

    &.prev {
        left: 0.5rem;
        svg {transform: scaleX(-1)} /* One chevron path in the markup, mirrored here */
    }
    &.next {right: 0.5rem}

    /* Pressed feedback that clears on release, so touch taps light up without sticking */
    &:active {
        color: var(--accent);
        border-color: var(--accent);
    }

    /* Gate hover to real-pointer devices so a tap doesn't leave it stuck/highlighted on touch/focus */
    @media (hover: hover) {
        &:hover {
            color: var(--accent);
            border-color: var(--accent);
        }
    }
}

/* Auto-scroll toggle below the strip, a .carouselChip with an icon */
#driftToggle {
    display: none; /* revealed when the strip overflows */
    align-items: center;
    gap: 0.25rem; /* gap between SVG and text */
    padding: 0.35rem 0.7rem;

    svg {width: 0.8rem; height: 0.8rem}
}

/* One-line tap hint between the strip and the auto-scroll toggle */
.carouselHint {
    display: none; /* revealed when the strip overflows */
    font-size: 0.72rem;
    color: var(--muted);
}

/* Reveal the scroll chrome only while the strip overflows (edge carets still yield to .hidden) */
#carouselSection:has(.overflowing) {
    .carouselCaret {display: flex}
    #driftToggle {display: inline-flex}
    .carouselHint {display: block}
}

/* Card pop-out */
#cardPopout {
    position: fixed;
    inset: 0;
    z-index: 1000; /* highest z-index to appear above all */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 5vmin;
    background: rgba(0, 0, 0, 0.82);
    cursor: zoom-out;
    animation: popFade 0.15s ease;

    .diagram {
        max-width: 90vw;
        max-height: 88vh;
        width: auto;
        height: auto;
        border-radius: 8px;
    }

    .captionedDiagram {width: min(88vw, 640px)}
}
@keyframes popFade {from {opacity: 0} to {opacity: 1}}

/* The strip: centered while it fits, left-aligned and scrollable once it overflows */
#carouselStrip {
    width: 100%;
    overflow: hidden;
    display: flex;
    justify-content: center;
    touch-action: pan-y; /* vertical gestures still scroll the page */

    &.overflowing {
        justify-content: flex-start;
        cursor: grab;

        &.grabbing {cursor: grabbing}
    }
}

#carouselTrack {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    will-change: transform;

    .carouselCard {
        height: var(--carouselH);
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        justify-content: center;

        .diagram {
            height: 100%;
            width: auto;
        }

        /* Captioned (CP Freestyle): width-bound, height follows the diagram plus caption */
        &:has(.captionedDiagram) {
            height: auto;
            width: min(70vw, calc(var(--carouselH) * 1.5));
        }

        .captionedDiagram {
            width: 100%;
            height: auto;
        }
    }
}

/* Results screen with the card carousel scrolls instead of cramming one screen */
body:not(.gallery):has(#carouselSection) {
    height: auto;
    min-height: 100dvh;
    overflow-y: auto;

    #arena {flex-basis: auto} /* Floor the arena at content height so the carousel is not squeezed */
}

/* ------------ */
/* Gallery View */
/* ------------ */

/* Re-enable scroll */
body.gallery {
    height: auto;
    min-height: 100dvh;
    overflow: visible;
}

/* No stats, so mode needs to carry the border */
body.gallery #modeBar {
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--border);

    /* Left justify the Gallery carousel toggle on the mode bar */
    #viewToggle {margin-left: auto}
}

/* Grid and carousel view shells share the same 10% side insets */
#gallery, #galleryCarousel {
    width: 100%;
    padding: 1.5rem 10%;
}

#gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(max(200px, (100% - 3rem) / 4), 1fr));
    gap: 1rem;
    align-items: start;

    img {
        width: 100%;
        height: auto;
        display: block;
    }
}

/* --------- */
/* Quiz View */
/* --------- */

/* Card fills the arena like Flashcards; the board sits below in the same one-screen layout */
body.quiz #arena {
    padding-top: 1rem;
    gap: 0.5rem;
    position: relative; /* Anchor for the expand toggle */
}

/* Card yields before the board: it shrinks first so the board only scrolls once the card hits its 200px floor */
body.quiz #card {flex-shrink: 1000}

/* Tighter face gutter so the diagram fills the smaller quiz card */
body.quiz .cardFace {padding: 0.25rem}

/* Expand toggle: pinned to the card's top-right, anchored to the arena padding so it sits on the card corner */
#expandToggle {
    position: absolute;
    top: 1rem; /* = arena padding-top, card's top edge */
    right: 1.5rem; /* = arena padding-right, card's right edge */
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    width: clamp(2.25rem, 5vmin, 3rem); /* same box + 45% glyph as the FABs, so equal corner inset */
    height: clamp(2.25rem, 5vmin, 3rem);
    border: none;
    background: none;
    color: var(--muted);
    cursor: pointer;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.9)); /* lift the naked icon off the card so it doesn't blend in */

    svg {width: 45%; height: 45%}
    .iconCompress {display: none} /* expand (outward) arrows show by default */
    /* Gate hover to real-pointer devices so a tap doesn't leave it stuck-highlighted on touch */
    @media (hover: hover) {
        &:hover {color: var(--accent)}
    }
}

/* Expanded view: strip the chrome down to the arena and the corner FABs */
body.quiz.expand {
    header, #modeBar, #statsBar, #endlessDrawer {display: none}

    /* Swap to compress (inward) arrows instead of highlighting the button */
    #expandToggle {
        .iconExpand {display: none}
        .iconCompress {display: block}
    }
}

/* Each control positions itself against the arena, so the container is just a passthrough */
body.quiz #cornerButtons {display: contents}

/* Report flag: naked icon at the card's bottom-right corner, matching the expand toggle */
body.quiz #reportIssue {
    position: absolute;
    z-index: 5;
    width: clamp(2.25rem, 5vmin, 3rem);
    height: clamp(2.25rem, 5vmin, 3rem);
    border: none;
    background: none;
    opacity: 1;
    right: 1.5rem; /* = arena padding-right, card's right edge */
    top: calc(1rem + var(--cardH, 0px)); /* arena padding-top + card height = card's bottom edge */
    transform: translateY(-100%); /* lift the flag just inside the card bottom */
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.9)); /* lift the naked icon off the card so it doesn't blend in */
}

/* The card-anchored icons would hover in empty space as the card flies off - hide them instantly with it */
#arena.swiping #expandToggle,
#arena.swiping #reportIssue {
    opacity: 0;
    pointer-events: none;
}

/* Source moves to bottom-right (less chance of overlap) */
body.quiz #sourceLink {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 100;
}

/* Results screen has no card: stack source and the card-anchored report at the fixed bottom-right */
body.quiz:has(#done) #cornerButtons {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    position: fixed;
    right: 1rem;
    bottom: 1rem;
}
body.quiz:has(#done) #sourceLink,
body.quiz:has(#done) #reportIssue {
    position: static;
    inset: auto;
    transform: none;
}

#answerGrid {
    width: 100%;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
    gap: 0.5rem;

    /* Board hugs its content (grow 0, no row stretch); shrinks and scrolls only when it can't fit after the card yields */
    flex: 0 1 auto;
    min-height: 100px;
    overflow: hidden auto; /* hidden x avoids the y-scroll coercing x to auto */

    /* Symbols fixed to 4-columns */
    &[data-answer="symbol"] {grid-template-columns: repeat(4, 1fr)}

    /* Freeze the board after a pick */
    &.answered {pointer-events: none}
}

.answerOption {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    text-transform: capitalize;
    text-align: center;
    padding: 0.5rem 2px;
    min-height: 2.5rem;
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;

    /* Block names split start/end across a dividing rule that bisects the button */
    hr {
        align-self: stretch;
        margin: 0.25rem -2px; /* Bleed into the padding so the rule meets the edges */
        border: 0;
        border-top: 1px solid currentColor;
        opacity: 0.25;
    }

    /* Bisect block answer buttons */
    &:has(hr) {min-height: calc(2lh + 1rem + 0.5rem + 3px)} /* 2 lines + rule margins + padding + borders */

    /* Gate hover to real-pointer devices so a tap-scroll doesn't leave a button stuck-highlighted on touch */
    @media (hover: hover) {
        &:hover {
            border-color: var(--accent);
            color: var(--accent);
        }
    }

    /* Color the verdict after a pick */
    &.correct,
    &.revealCorrect {
        border-color: var(--success);
        color: var(--success);
        background: color-mix(in srgb, var(--success) 14%, var(--card));
    }

    &.wrong {
        border-color: var(--error);
        color: var(--error);
        background: color-mix(in srgb, var(--error) 14%, var(--card));
    }
}

/* ----------------- */
/* Landing/404 Pages */
/* ----------------- */

/* Standalone menu pages (root index + 404): centered grid floored at the viewport with a
   sticky footer - fills the screen when content fits, scrolls the whole page when too short */
/* min-height 100% (via html) floors at the viewport, height auto overrides base body 100dvh - dvh overshoots an installed Android PWA, scrolling even when content fits */
html:has(body.landing) {height: 100%}
body.landing {
    min-height: 100%;
    height: auto; /* Not the base body 100dvh */
    display: grid;
    grid-template-rows: 1fr auto; /* Push the footer to the bottom, or trailing the content when the page scrolls */
    place-items: center;
    padding: 2rem;
    text-align: center;

    main {
        display: flex;
        flex-direction: column;
        align-items: center;
    }

    h1 {
        font-family: var(--titleFont);
        font-weight: 900;
        font-size: clamp(2.4rem, 12vmin, 3.6rem);

        span {
            display: inline-block;
            color: var(--accent);
            transform-origin: 50% 50% -0.55em; /* Pivot behind the text so it rolls on a cylinder, not flips on its midline */
        }
    }

    #tagline {
        margin-top: 0.6rem;
        font-size: 0.7rem;
        letter-spacing: 0.15em;
        text-transform: uppercase;
        color: var(--muted);
    }

    #appVersion {
        margin-top: 0.35rem;
        font-size: 0.6rem;
        letter-spacing: 0.2em;
        text-transform: uppercase;
        color: var(--muted);
        opacity: 0.55;

        a {
            color: inherit;
            text-decoration: none;
            display: inline-flex;
            align-items: center;
            gap: 0.5em;
            &:hover {color: var(--accent)}

            svg {
                width: 0.9em;
                height: 0.9em;
            }
        }
    }

    nav {
        margin-top: 2.5rem;
        margin-bottom: 1.5rem;
        display: flex;
        flex-direction: column;
        gap: 0.75rem;
        width: min(20rem, 80vw);

        a {
            padding: 0.95rem 1rem;
            border: 1px solid var(--border);
            border-radius: 4px;
            color: var(--text);
            text-decoration: none;
            font-size: 0.8rem;
            letter-spacing: 0.12em;
            text-transform: uppercase;
            transition: all 0.2s;

            &:hover {
                background: var(--accent);
                border-color: var(--accent);
                color: var(--blackText);
            }
        }
    }

    /* Muted accessory controls: press feedback + pointer-only hover so a tap never sticks highlighted */
    :is(#collegiate, #drawGen, #installGroup button, footer a) {
        color: var(--muted);
        transition: color 0.2s;

        &:active {color: var(--accent)}
        @media (hover: hover) {
            &:hover {color: var(--accent)}
        }
    }

    /* Install to home screen, third accessory row when installable */
    #installGroup {
        margin-bottom: 1.5rem;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 0.75rem;

        button {
            background: none;
            border: none;
            font-family: inherit;
            cursor: pointer;
        }

        #installHint {
            font-size: 0.7rem;
            letter-spacing: 0.05em;
            color: var(--muted);
            text-align: left; /* Landing centers text, list reads left */

            ol {
                margin-top: 0.25rem;
                padding-left: 1.5em; /* Reset zeroes it, markers would clip */
            }

            li {
                margin-top: 0.15rem;
            }
        }
    }

    /* Small-caps accessory rows under the nav */
    #collegiate, #drawGen, #installGroup button {
        font-size: 0.7rem;
        letter-spacing: 0.15em;
        text-transform: uppercase;
    }

    #collegiate, #drawGen {
        margin-bottom: 1.5rem;
        display: inline-flex;
        align-items: center;
    }

    /* External companion-tool link */
    #drawGen {
        gap: 0.4em;
        text-decoration: none;

        svg {
            width: 0.85em;
            height: 0.85em;
        }
    }

    /* Color theme toggle */
    #themeToggle {
        position: fixed;
        top: 1rem;
        right: 1rem;
        width: 2.5rem;
        height: 2.5rem;
        display: grid;
        place-items: center;
        border: 1px solid var(--border);
        border-radius: 50px;
        background: transparent;
        color: var(--text);
        cursor: pointer;
        transition: all 0.2s;

        /* Light up on press (touch + mouse), releasing on lift so it never sticks */
        &:active {
            background: var(--accent);
            border-color: var(--accent);
            color: var(--blackText);
        }

        /* Gate hover to real-pointer devices so a tap doesn't leave it stuck/highlighted on touch/focus */
        @media (hover: hover) {
            &:hover {
                background: var(--accent);
                border-color: var(--accent);
                color: var(--blackText);
            }
        }

        svg {
            display: none;
            width: 1.1rem;
            height: 1.1rem;
        }
    }

    /* Social footer */
    footer {
        display: flex;
        gap: 1rem;

        a {
            padding: 0.35rem; /* Bigger tap target than the bare icon */

            svg {
                display: block;
                width: 1.3rem;
                height: 1.3rem;
                overflow: visible; /* Brand glyphs touch x=24, so the SVG viewport clips the last column without this */
            }
        }
    }
}

/* Show the icon of the theme the tap switches to */
:root[data-theme="dark"] .landing #themeToggle .sun {display: block}
:root[data-theme="light"] .landing #themeToggle .moon {display: block}

/* Not-found page (body.landing.notFound) */
.notFound main {
    p {
        color: var(--muted);
        margin: 0.75rem 0 1.5rem;
    }

    a {color: var(--accent)}
}

/* ----------------- */
/* Offline & Updates */
/* ----------------- */

/* Absent art (offline, bad link) fails quietly instead of a broken-image glyph */
img.unavailable {visibility: hidden}

/* Update banner (offline.js): non-modal, floats over the header. Inline layout for the
   short update lanes, stacked (title + detail + action row) when given a title */
#updateBanner {
    position: fixed;
    top: 0.75rem;
    inset-inline: 0.75rem; /* Left 50% + translate caps shrink-to-fit at half the viewport, insets + auto margins don't */
    margin-inline: auto;
    width: fit-content;
    max-width: 480px;
    z-index: 1001; /* Above the card pop-out, a pending update outranks everything */
    background: var(--card);
    border: 1px solid var(--accent);
    border-radius: 4px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.38);
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text);
    animation: slideDown 0.3s ease;

    .bannerActions {display: flex; align-items: center; gap: 0.5rem}

    /* Buttons are pills (active = the primary action), just tighter */
    .bannerAction {padding: 0.35rem 0.75rem}

    .bannerDismiss {
        padding: 0.35rem;

        &:hover {color: var(--text)}
    }

    /* Major lane: short text + actions on one row */
    &.inline {
        display: flex;
        flex-wrap: wrap; /* Actions drop below only if the text ever overflows */
        align-items: center;
        gap: 0.55rem 0.7rem;
        padding: 0.5rem 0.5rem 0.5rem 0.8rem;

        .bannerText {flex: 1 1 auto; min-width: 0}
        .bannerActions {margin-left: auto}
    }

    /* Stale-sets lane: kicker title, detail, then the action row */
    &.stacked {
        display: flex;
        flex-direction: column;
        padding: 0.7rem 0.8rem;

        .bannerTitle {
            display: flex;
            align-items: center;
            gap: 0.45rem;
            color: var(--accent);
            padding-right: 1.4rem; /* Clear the corner dismiss */
        }

        .bannerText {
            margin-top: 0.45rem;
            color: var(--muted);
            line-height: 1.55;
        }

        .bannerActions {
            justify-content: flex-end;
            margin-top: 0.7rem;
            padding-top: 0.6rem;
            border-top: 1px solid var(--border);
        }

        .bannerDismiss {position: absolute; top: 0.4rem; right: 0.4rem}
    }
}

/* Download split-control: primary action + manage caret */
#downloadSplit {
    position: relative; /* Anchors the floating manage list */
    display: flex;
    width: fit-content; /* Hug content like every other pill */
    margin-top: 0.5rem;

    #downloadAction {
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 0.4em;
        border-radius: 4px 0 0 4px;
    }

    .btnIcon {width: 1.1em; height: 1.1em; flex-shrink: 0}

    #downloadMenu {display: flex}

    #downloadMenuToggle {
        display: flex;
        align-items: center;
        border-radius: 0 4px 4px 0;
        border-left: none;
        padding: 0.5rem 0.6rem;

        svg {transition: transform 0.2s}
    }
    #downloadMenu[open] #downloadMenuToggle svg {transform: rotate(180deg)}

    /* Caret can't take disabled (summary), match the action's dim so the control fades as one */
    &:has(#downloadAction:disabled) #downloadMenuToggle {opacity: 0.5}

    /* Hover or press on either segment rings the whole control, divider included */
    &:has(#downloadAction:is(:hover, :active), #downloadMenuToggle:is(:hover, :active)) .pill {
        border-color: var(--accent);
        color: var(--accent);
    }

    /* Saved state: the main pill mirrors the caret, so no divider */
    &.unified #downloadAction {border-right: none}

    /* Disabled controls dim the same in the split control and its menu list */
    button:disabled {
        opacity: 0.5;
        cursor: default;
    }
}

/* Saved-sets manage list, floating below the control (panel recipe from .menuPanel) */
#downloadList {
    left: 0;
    width: max-content; /* Own width, the control now hugs its content */
    max-width: min(360px, calc(100vw - 1rem)); /* Guarantees clampDownloadList's shift can always fit it */
    overflow: hidden; /* Keep row hover fills inside the radius */

    > :not(hr) + :not(hr) {border-top: 1px solid var(--border)}

    /* Zone break between the action rows and the saved list */
    hr {
        border: none;
        border-top: 1px solid var(--border);
    }

    /* Contextual current-set action rows heading the list */
    .menuAction {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        width: 100%;
        padding: 0.45rem 0.6rem;
        background: none;
        border: none;
        font-size: 0.72rem;
        font-family: inherit;
        color: var(--text);
        text-align: left;
        cursor: pointer;

        span {
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .btnIcon {color: var(--muted)}

        &:hover {
            background: var(--card);
            .btnIcon {color: inherit}
        }
        &.remove:hover {color: var(--error)}
        &.update:hover {color: var(--accent)}
    }

    /* Shared row parts (action rows, saved rows, group summaries) */
    .rowLabel {
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .rowSize {color: var(--muted)}

    .downloadRow {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.45rem 0.6rem;
        font-size: 0.72rem;

        /* Full row height: negative margins cancel the row padding, side borders only */
        .rowUpdate {
            padding: 0.3rem 0.6rem;
            margin: -0.3rem 0;
        }

        /* Negative margins cancel the button's padding so text sets the row height (matches summaries) */
        .rowRemove {
            padding: 0.3rem;
            margin: -0.3rem 0;

            &:hover {color: var(--error)}
        }
    }

    /* Multi-set discipline collapsed into an expandable group of set rows */
    .setGroup {
        summary {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            padding: 0.45rem 0.6rem;
            font-size: 0.72rem;
            cursor: pointer;

            .btnIcon {
                color: var(--muted);
                transition: rotate 0.15s;
            }

            /* Badge hugs the label text, the size takes over pushing right */
            .rowLabel {flex: initial}
            .rowSize {margin-left: auto}

            /* A child needs an update */
            .staleBadge {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                width: 1.2em;
                height: 1.2em;
                border: 1px solid var(--accent);
                border-radius: 50%;
                color: var(--accent);
                flex-shrink: 0;
            }

            &:hover {background: var(--card)}
        }

        &[open] summary .btnIcon {rotate: 90deg}

        > * + * {border-top: 1px solid var(--border)}

        .downloadRow {padding-left: 1.9rem} /* Align labels past the caret: 0.6 pad + 1.1em icon + 0.5 gap */
    }

    .menuTotal {
        padding: 0.45rem 0.6rem;
        font-size: 0.6rem;
        letter-spacing: 0.12em;
        text-transform: uppercase;
        color: var(--muted);
    }
}

/* -------------- */
/* Mobile support */
/* -------------- */

/* Mode + Stats unity */
body:not(:has(#modeBar.collapsed)) #statsModeSelect {display: none} /* Hide mode dropdown until collapsed */
#progressCount {display: none} /* Folded card-count */

/* On mobile devices */
@media (max-width: 600px), (max-height: 600px) { /* Height = Landscape catch */
    /* Reduce spacing */
    #arena, #gallery, #galleryCarousel {padding: 1rem}
    #arena {gap: 1rem}
}

/* On narrow devices */
@media (max-width: 430px) { /* Height = Landscape catch */
    /* Replace the standalone Card stat */
    #statsBar .stat:has(#statCard) {display: none}
    #progressCount {display: inline}
}

/* On short devices */
@media (max-height: 600px) {
    /* Allow for shorter devices to scroll past the browser bar */
    body {
        height: 100lvh;
        overflow-y: auto;
    }
    body:not(.gallery):has(#settingsPanel:not(.hidden), #endlessDrawer:not(.hidden) #drawerContent:not(.hidden)) {height: calc(100lvh + var(--panelH, 0px) + var(--drawerH, 0px))}

    /* Hide elements */
    #keyboardHints {display: none}
    #watchBar {display: none}
    #arena > #actionBar {display: none} /* Only on play screen */
}

/* Landscape keeps progress inline */
@media (max-width: 600px) and (min-height: 481px) {
    #statsBar #progressContainer:not(:has(#endlessToggle.active)) {flex-basis: 100%}
}

/* Mobile: card grows to fill the leftover (board hugs its content via the shared rule) */
@media (max-width: 600px) {
    #card {
        aspect-ratio: auto;
        flex: 1;
    }

    /* Card-anchored controls follow the arena padding (1rem here); source FAB is already viewport-fixed */
    #expandToggle {right: 1rem}
    body.quiz #reportIssue {right: 1rem}
}