/* Betatron ships one weight; it covers 300-500 so 700 requests get synthetic bold.
   Nothing asks the mono family for a lighter weight any more. */
@font-face {
    font-family: 'Slider Supply Mono';
    src: local('Betatron Regular'), local('Betatron-Regular'),
         url('fonts/Betatron-Regular.woff2') format('woff2');
    font-style: normal;
    font-weight: 300 500;
    font-display: swap;
}

/* JetBrains Mono: the middle-ground face between the ultralight chrome and
   the display mono. Two weights shipped, so the bold value chips are real
   rather than faux. */
@font-face {
    font-family: 'Slider Alt Mono';
    src: local('JetBrains Mono'), local('JetBrainsMono-Regular'),
         url('fonts/JetBrainsMono-Regular.woff2') format('woff2');
    font-style: normal;
    font-weight: 200 500;
    font-display: swap;
}

@font-face {
    font-family: 'Slider Alt Mono';
    src: local('JetBrains Mono Bold'), local('JetBrainsMono-Bold'),
         url('fonts/JetBrainsMono-Bold.woff2') format('woff2');
    font-style: normal;
    font-weight: 600 900;
    font-display: swap;
}

/* Chrome type: kickers, section headers and the diag strips are sans, set
   ultralight; setting/bind rows and the scope telemetry are JetBrains; everything
   else stays on the mono face. Weight 200 picks Helvetica Neue UltraLight. */
:root {
    /* The highlight a menu row takes under a pointer or a stick. One token so
       the inverted pause card can flip it to ink without restating a rule. */
    --slider-menu-wash: rgba(255, 255, 255, 0.08);
    --slider-sans: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --slider-sans-weight: 200;
    --slider-mid: 'Slider Alt Mono', ui-monospace, Menlo, Consolas, monospace;
    /* Extra tracking added to EVERY JetBrains (--slider-mid) rule on top of
       its own base value — one knob to widen the face without stretching
       glyphs. 0em restores the old look. */
    --slider-mid-track: 0.15em;
}

html, body {
    margin: 0;
    padding: 0;
    overflow: hidden;
    background: #050608;
    color: #e8e8ea;
    font-family: 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace;
    width: 100%;
    height: 100%;
}

#sliderCanvas {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    background: #050608;
}

/* Run clock, top left — the score total's type at readout scale. */
.slider-run-clock {
    position: fixed;
    top: 22px;
    left: 24px;
    z-index: 12;
    color: #ffffff;
    font-family: 'Slider Supply Mono', monospace;
    font-size: clamp(24px, 2.6vw, 40px);
    font-weight: 500;
    line-height: 0.9;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}

/* Dev readout (toggle: B), fed per frame by slider-main.js. */
.slider-hud {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 10;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    font-size: 13px;
    letter-spacing: 0.08em;
}

.slider-hud[hidden] {
    display: none;
}

.slider-hud-row {
    display: flex;
    gap: 8px;
    color: #c8c8cc;
}

.slider-hud-label {
    opacity: 0.6;
}

.slider-hud-value {
    color: #ffffff;
    min-width: 64px;
    text-align: right;
}

.slider-hud-restart {
    margin-top: 4px;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.35);
    color: #e8e8ea;
    font: inherit;
    letter-spacing: 0.1em;
    padding: 4px 10px;
    cursor: pointer;
}

.slider-hud-restart:hover {
    background: rgba(255, 255, 255, 0.08);
}

/* Physics debug readout (toggle: B), fed per frame by slider-main.js. */
.slider-debug-hud {
    position: fixed;
    bottom: 16px;
    left: 16px;
    z-index: 10;
    margin: 0;
    padding: 8px 12px;
    background: rgba(0, 0, 0, 0.55);
    color: #b8f0c0;
    font-family: 'Slider Supply Mono', monospace;
    font-size: 12px;
    line-height: 1.5;
    letter-spacing: 0.04em;
    pointer-events: none;
}

.slider-score-total {
    position: fixed;
    top: 22px;
    right: 24px;
    z-index: 12;
    transform: scale(var(--score-total-scale, 1));
    /* Anchored on the right edge so the reward pulse grows inward, never off
       the side of the screen. */
    transform-origin: 100% 50%;
    color: #ffffff;
    font-family: 'Slider Supply Mono', monospace;
    font-size: clamp(48px, 6vw, 92px);
    font-weight: 500;
    line-height: 0.9;
    letter-spacing: -0.04em;
    white-space: nowrap;
    pointer-events: none;
}

/* Boost meter, bottom centre: ten chamfered cells (images/EnergyBar.svg), one
   per 1000 points, so a 1000-point trick reads as exactly one cell. Each fill
   is a transform (scaleX), never a width, so updating it costs no layout.
   is-armed = a burst is affordable; is-draining = the held drive is live. */
/* Bottom-right stack: speed readout over the meter. The wrapper owns the
   corner and the width, so neither child needs a hand-measured offset. */
.slider-hud-corner {
    position: fixed;
    right: 24px;
    bottom: 26px;
    z-index: 11;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    width: min(380px, 34vw);
    pointer-events: none;
}

.slider-speed-readout {
    display: flex;
    align-items: baseline;
    gap: 6px;
    color: #ffffff;
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 500;
    font-size: clamp(28px, 3vw, 48px);
    line-height: 0.9;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

.slider-speed-unit {
    font-size: 0.36em;
    letter-spacing: 0.16em;
    opacity: 0.7;
}

.slider-boost-meter {
    display: flex;
    gap: 5px;
    width: 100%;
    pointer-events: none;
}

/* Course intro: the whole corner is masked away during the cutscene and comes
   back when the countdown starts — the meter unfolding from its centre, the
   speed readout fading in with it. The meter is masked with clip-path rather
   than a transform because the cells' own chamfers are clip-paths — a scaleX
   here would skew them. */
.slider-hud-corner.is-folded .slider-boost-meter {
    clip-path: inset(0 50% 0 50%);
}

.slider-hud-corner.is-folded .slider-speed-readout {
    opacity: 0;
}

.slider-hud-corner.is-unfolding .slider-boost-meter {
    animation: slider-meter-unfold 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.slider-hud-corner.is-unfolding .slider-speed-readout {
    animation: slider-readout-fade 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes slider-meter-unfold {
    from { clip-path: inset(0 50% 0 50%); }
    to   { clip-path: inset(0 0 0 0); }
}

@keyframes slider-readout-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* The cell IS the SVG outline: the polygon is EnergyBar.svg's points mapped to
   percentages of its 900x400 viewBox, so the aspect ratio must match it or the
   chamfers skew. clip-path (not a border) because the fill has to stop at the
   same chamfer the cell does. */
.slider-boost-cell {
    flex: 1;
    aspect-ratio: 900 / 400;
    clip-path: polygon(100% 100%, 16.67% 100%, 0 62.5%, 0 0, 83.33% 0, 100% 37.5%);
    background: rgba(120, 220, 255, 0.18);
}

.slider-boost-cell > i {
    display: block;
    height: 100%;
    transform-origin: 0 50%;
    transform: scaleX(0);
    background: rgba(120, 220, 255, 0.5);
}

.slider-boost-meter.is-armed .slider-boost-cell > i {
    background: rgba(120, 220, 255, 0.95);
}

.slider-boost-meter.is-armed .slider-boost-cell {
    filter: drop-shadow(0 0 6px rgba(120, 220, 255, 0.55));
}

.slider-boost-meter.is-draining .slider-boost-cell > i {
    background: rgba(190, 240, 255, 1);
}

.slider-boost-meter.is-draining .slider-boost-cell {
    filter: drop-shadow(0 0 9px rgba(120, 220, 255, 0.8));
}

/* Connector lines only. Below the bank pixels (10) and therefore below the
   boxes too: the squares travel ALONG these lines, and a 2 px line drawn over
   a 3.6 px square runs straight down its middle for the whole journey, which
   is what made the connector particles read as faint slivers. */
.slider-callout-lines {
    position: fixed;
    inset: 0;
    z-index: 9;
    pointer-events: none;
}

.slider-combo-callout {
    position: fixed;
    inset: 0;
    z-index: 11;
    pointer-events: none;
}

.slider-combo-line {
    position: absolute;
    left: 0;
    top: 0;
    height: 2px;
    margin-top: -1px;
    background: #ffffff;
    transform-origin: 0 50%;
}

.slider-combo-box {
    position: absolute;
    left: 0;
    top: 0;
    transform: translate(-50%, -50%) perspective(var(--hud-warp-depth, 620px))
        rotateY(var(--hud-warp-angle, 0deg)) scale(var(--combo-bank-scale, 1));
    /* Grows away from the rider: the box sits right of the player, so it is
       pinned by its left edge and every scale runs outward from there. */
    transform-origin: 0% 50%;
    box-sizing: border-box;
    padding: 0.32em 0.55em 0.28em;
    border: 1px solid #050608;
    background: #ffffff;
    color: color(srgb 0 0.1 0.9);
    font-family: var(--slider-mid);
    font-weight: 500;
    line-height: 1;
    letter-spacing: calc(-0.03em + var(--slider-mid-track));
    white-space: nowrap;
}

.slider-combo-hold {
    /* A CHILD of the combo box: the box's own warp lean and bank scale carry
       it, so the two cannot drift apart at speed. Pinned by its BOTTOM edge,
       so the window drains downward rather than shrinking about its middle,
       and by its height PERCENTAGE, so full height is the box's height. */
    position: absolute;
    left: calc(100% + var(--combo-hold-gap, 5px));
    bottom: 0;
    width: var(--combo-hold-width, 5px);
    background: #ffffff;
}

.slider-combo-multiplier {
    position: absolute;
    left: 0;
    top: 0;
    transform: translateY(-50%) perspective(var(--hud-warp-depth, 620px))
        rotateY(var(--hud-warp-angle, 0deg)) scale(var(--combo-bank-scale, 1));
    transform-origin: 0 50%;
    color: #ffffff;
    font-family: var(--slider-mid);
    font-weight: 500;
    line-height: 1;
    letter-spacing: calc(-0.03em + var(--slider-mid-track));
    text-align: left;
    white-space: nowrap;
}

.slider-trick-callout {
    position: fixed;
    inset: 0;
    z-index: 11;
    pointer-events: none;
}

.slider-trick-line {
    position: absolute;
    left: 0;
    top: 0;
    height: 1px;
    margin-top: -0.5px;
    background: #ffffff;
    transform-origin: 0 50%;
}

.slider-trick-box {
    position: absolute;
    left: 0;
    top: 0;
    transform: translate(-50%, -50%) perspective(var(--hud-warp-depth, 620px))
        rotateY(var(--hud-warp-angle, 0deg)) scale(var(--trick-readout-scale, 1));
    /* Mirror of the combo box: this one sits LEFT of the player, so its right
       edge is the near one and growth runs left, away from the rider. */
    transform-origin: 100% 50%;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.16em;
    padding: 0.3em 0.52em 0.27em;
    border: 1px solid #050608;
    background: #ffffff;
    color: color(srgb 0 0.1 0.9);
    font-family: var(--slider-mid);
    font-size: clamp(14px, 1.8vw, 22px);
    font-weight: 500;
    line-height: 1;
    letter-spacing: calc(-0.02em + var(--slider-mid-track));
    white-space: nowrap;
}

.slider-trick-name {
    line-height: 1;
}

.slider-trick-value {
    font-size: 0.82em;
    line-height: 1;
    letter-spacing: 0.02em;
}

.slider-trick-box[data-reward-tier="strong"] {
    box-shadow: 4px 4px 0 var(--reward-accent, #ffffff);
}

.slider-trick-box[data-reward-tier="exceptional"] {
    box-shadow: 4px 4px 0 var(--reward-accent, #ffffff), 8px 8px 0 #ffffff;
}

/* Above everything the bank touches (13), and difference-blended so the word
   inverts the course rather than sitting on top of it. Sized by scaling a
   measured line, not by a font-size guess: the exceptional flash has to reach
   the window edges exactly. */
.slider-tier-flash {
    position: fixed;
    inset: 0;
    z-index: 13;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    mix-blend-mode: difference;
    opacity: var(--tier-flash-opacity, 0);
    pointer-events: none;
}

.slider-tier-flash-text {
    /* Scale first, then the ink-centring nudge: the offset is authored in
       unscaled px and must grow with the word. */
    transform: scale(var(--tier-flash-scale-x, 1), var(--tier-flash-scale-y, 1))
        translate(var(--tier-flash-offset-x, 0px), var(--tier-flash-offset-y, 0px));
    transform-origin: 50% 50%;
    color: #ffffff;
    font-family: var(--slider-mid);
    font-size: 100px;
    font-weight: 500;
    line-height: 1;
    letter-spacing: calc(-0.04em + var(--slider-mid-track));
    white-space: nowrap;
}

/* Course intro countdown. Above the callouts and the tier flash: nothing else
   is on screen while it runs, and it must not be tinted by a difference blend
   the way the flash text is. */
.slider-countdown {
    position: fixed;
    inset: 0;
    z-index: 14;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.slider-countdown-text {
    color: #ffffff;
    font-family: 'Slider Supply Mono', monospace;
    font-size: 256px;
    font-weight: 500;
    line-height: 1;
    letter-spacing: -0.04em;
    white-space: nowrap;
    opacity: 0;
}

/* The class is removed and re-added per card, which restarts the animation —
   one authored punch-in serves 3, 2, 1 and BEGIN alike. */
.slider-countdown-text.is-on {
    animation: slider-countdown-punch 0.62s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes slider-countdown-punch {
    0%   { opacity: 0; transform: scale(2.4); letter-spacing: 0.5em; }
    22%  { opacity: 1; transform: scale(1); letter-spacing: -0.04em; }
    70%  { opacity: 1; transform: scale(1); letter-spacing: -0.04em; }
    100% { opacity: 0; transform: scale(0.94); letter-spacing: -0.04em; }
}

.slider-score-bank-pixels {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    /* BELOW the callouts (11) and the score total (12): the squares travel up
       the connector and into the number, and a square crossing the face of a
       text box reads as a glitch rather than as currency in flight. Under the
       boxes they slide behind and are absorbed. */
    z-index: 10;
    overflow: hidden;
    pointer-events: none;
}

.slider-fallback {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 20;
    text-align: center;
    color: #e8e8ea;
    font-size: 14px;
    line-height: 1.6;
    max-width: 320px;
}

/* ---- Pause menu ---------------------------------------------------------
   Menu chrome, not HUD: it sits above everything, dims the frozen frame
   behind it, and keeps the emissive-on-dark language of the readouts. */

/* No scrim and no backdrop blur: the frame behind a menu is the game — the
   frozen course under the pause menu, the wireframe showcase under the main
   menu — and dimming or blurring it hides the thing worth looking at. The
   panel carries its own background, so readability does not depend on one. */
.slider-pause {
    position: fixed;
    inset: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: center;
}

.slider-pause[hidden] {
    display: none;
}

/* The one overlay whose frame is drawn FOR it: drawLoading paints the subject
   being loaded as a light-grey wireframe on the dark field this card sits on,
   so the overlay itself carries no background. Above the menus so nothing can
   peek through. */
.slider-loading {
    z-index: 50;
}

/* The load meter IS the background: a blue column rising out of the black
   field from empty to full, driven by --load (0..1, set by the boot gate).
   Translucent, so the wireframe subject still reads through the filled part.
   Behind the card but inside this layer's stacking context, so it never
   covers the canvas above the fill line. */
.slider-loading::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: rgba(22, 51, 200, 0.55);
    transform: scaleY(var(--load, 0));
    transform-origin: bottom;
    transition: transform 0.35s cubic-bezier(0.25, 0.1, 0.25, 1);
}

/* The loading card wears the pause menu's inverted grey. Stated apart from
   #pauseMenu's block because it keeps the unzoomed size. */
#loadingScreen .slider-pause-panel {
    background: #cccccc;
    border-color: rgba(0, 0, 0, 0.4);
}

#loadingScreen .slider-pause-title,
#loadingScreen .slider-menu-diag-strong {
    color: #000000;
}

#loadingScreen .slider-pause-hint,
#loadingScreen .slider-menu-window-head {
    color: rgba(0, 0, 0, 0.7);
}

#loadingScreen .slider-menu-window-head {
    border-bottom-color: rgba(0, 0, 0, 0.25);
}

/* And nothing else is in that frame. Everything the page would otherwise show
   behind the gate — the HUD, the main menu's own panels and scan lines — is a
   half-built run or an inert menu, so the card and the canvas are the only two
   layers left up. The fallback banner is exempt: a WebGPU failure has to be
   readable from under the screen that never got dismissed. */
body:has(#loadingScreen:not([hidden])) > *:not(#loadingScreen, canvas, #sliderFallback) {
    visibility: hidden;
}

/* Pause chrome speaks the same language as the keybind window: a panel-blue
   card with the diagnostic head strip, ledger rows, and //-prefixed exits. */
.slider-pause-panel {
    width: min(420px, 92vw);
    max-height: 84vh;
    overflow-y: auto;
    padding: 20px 26px 22px;
    background: #1633c8;
    border: 1px solid rgba(255, 255, 255, 0.4);
    display: flex;
    flex-direction: column;
}

.slider-pause-page {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}

.slider-pause-page[hidden] {
    display: none;
}

.slider-pause-title {
    margin: 0 0 10px;
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 500;
    font-size: clamp(22px, 2vw, 30px);
    letter-spacing: 0.2em;
    text-align: left;
    color: #ffffff;
}

.slider-pause-button {
    display: block;
    padding: 8px 8px;
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.14);
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 500;
    font-size: 14px;
    letter-spacing: 0.16em;
    text-align: left;
    color: #ffffff;
    cursor: pointer;
}

.slider-pause-button:hover:not(:disabled),
.slider-pause-button:focus-visible:not(:disabled) {
    background: rgba(255, 255, 255, 0.08);
    outline: none;
}

/* The stage being played: listed, marked, unclickable. */
.slider-pause-button:disabled {
    color: rgba(255, 255, 255, 0.38);
    cursor: default;
}

.slider-pause-hint {
    margin: 8px 0 0;
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 400;
    font-size: 11px;
    letter-spacing: 0.2em;
    text-align: left;
    color: rgba(255, 255, 255, 0.6);
}

.slider-mix-row {
    display: grid;
    /* Sized in em so the columns track the row's type: a fader row reads as
       one of the setting rows above it, and both ends stay legible. */
    grid-template-columns: 5.2em 1fr 2.6em;
    align-items: center;
    gap: 10px;
    font-family: var(--slider-mid);
    /* Same type as .slider-menu-setting: a fader row IS a setting row. */
    font-weight: 500;
    font-size: clamp(13px, 1.15vw, 18px);
    letter-spacing: calc(0.14em + var(--slider-mid-track));
}

.slider-mix-label {
    color: #ffffff;
}

.slider-mix-value {
    text-align: right;
    font-weight: 700;
    color: #ffffff;
}

.slider-mix-fader {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 2px;
    background: rgba(255, 255, 255, 0.3);
    cursor: pointer;
}

.slider-mix-fader:focus-visible {
    outline: 1px solid rgba(207, 228, 255, 0.9);
    outline-offset: 6px;
}

.slider-mix-fader::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 10px;
    height: 18px;
    background: #ffffff;
}

.slider-mix-fader::-moz-range-thumb {
    width: 10px;
    height: 18px;
    border: none;
    background: #ffffff;
}

/* The pause card is INVERTED relative to the main menu: the menu's light grey
   as the ground, panel ink as the text. Scoped to #pauseMenu (and the
   run-complete card, which shares the look), not to .slider-pause-panel —
   the loading card is built from the same classes and stays panel-blue.
   Every white-on-blue token the card uses is re-stated. */
#pauseMenu .slider-pause-panel,
#runComplete .slider-pause-panel {
    background: #cccccc;
    border-color: rgba(0, 0, 0, 0.4);
    /* Half again the size of the card the loading screen uses: `zoom`, so every
       px inside scales as one unit and the text stays crisp. Zoom multiplies
       viewport units too, so the two caps are restated pre-multiplied — they
       still land at the same 92vw / 84vh of the window. */
    zoom: 1.5;
    width: min(420px, 61.33vw);
    max-height: 56vh;
}

#pauseMenu,
#runComplete {
    --slider-menu-wash: rgba(0, 0, 0, 0.1);
}

#pauseMenu .slider-pause-title,
#pauseMenu .slider-pause-button,
#pauseMenu .slider-menu-diag-strong,
#pauseMenu .slider-menu-option,
#pauseMenu .slider-menu-setting,
#pauseMenu .slider-menu-select,
#pauseMenu .slider-mix-label,
#pauseMenu .slider-mix-value,
#runComplete .slider-pause-title,
#runComplete .slider-pause-button,
#runComplete .slider-menu-diag-strong {
    color: #000000;
}

#pauseMenu .slider-pause-hint,
#pauseMenu .slider-menu-window-head,
#pauseMenu .slider-menu-back,
#runComplete .slider-menu-window-head {
    color: rgba(0, 0, 0, 0.7);
}

#pauseMenu .slider-menu-section {
    color: rgba(0, 0, 0, 0.55);
}

#pauseMenu .slider-pause-button:disabled,
#pauseMenu .slider-menu-option:disabled {
    color: rgba(0, 0, 0, 0.38);
}

#pauseMenu .slider-pause-button:hover:not(:disabled),
#pauseMenu .slider-pause-button:focus-visible:not(:disabled),
#runComplete .slider-pause-button:hover:not(:disabled),
#runComplete .slider-pause-button:focus-visible:not(:disabled) {
    background: rgba(0, 0, 0, 0.1);
}

/* The pale blue accent reads as nothing on light grey; the panel blue does. */
#pauseMenu .slider-menu-option:hover:not(:disabled),
#pauseMenu .slider-menu-option:focus-visible:not(:disabled),
#pauseMenu .slider-menu-back:hover:not(:disabled),
#pauseMenu .slider-menu-back:focus-visible:not(:disabled),
#pauseMenu .slider-menu-setting:hover,
#pauseMenu .slider-menu-setting:focus-visible,
#pauseMenu .slider-menu-setting:focus-within {
    color: #1633c8;
}

#pauseMenu .slider-pause-button,
#runComplete .slider-pause-button {
    border-bottom-color: rgba(0, 0, 0, 0.14);
}

#pauseMenu .slider-menu-window-head,
#pauseMenu .slider-menu-setting,
#runComplete .slider-menu-window-head {
    border-bottom-color: rgba(0, 0, 0, 0.25);
}

#pauseMenu .slider-menu-setting-value,
#pauseMenu .slider-menu-select {
    border-color: rgba(0, 0, 0, 0.35);
}

#pauseMenu .slider-menu-select option {
    background: #cccccc;
    color: #000000;
}

/* Run-complete stat rows: the settings-row ledger voice, label left,
   value right. */
.slider-run-stats {
    margin: 0 0 14px;
}

.slider-run-stats div {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 6px 2px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.14);
    font-family: var(--slider-mid);
    letter-spacing: var(--slider-mid-track);
    font-size: 11px;
}

.slider-run-stats dt {
    color: rgba(0, 0, 0, 0.55);
    font-weight: 400;
}

.slider-run-stats dd {
    margin: 0;
    color: #000000;
    font-weight: 700;
}

/* The styled popup is a real element, so the card's inversion reaches it too. */
@supports (appearance: base-select) {
    #pauseMenu .slider-menu-select::picker(select) {
        border-color: rgba(0, 0, 0, 0.35);
        background: #cccccc;
    }

    #pauseMenu .slider-menu-select option {
        background: transparent;
    }

    #pauseMenu .slider-menu-select option:checked {
        background: rgba(0, 0, 0, 0.14);
    }

    #pauseMenu .slider-menu-select option:hover,
    #pauseMenu .slider-menu-select option:focus {
        background: rgba(0, 0, 0, 0.22);
    }
}

#pauseMenu .slider-mix-fader {
    background: rgba(0, 0, 0, 0.3);
}

#pauseMenu .slider-mix-fader::-webkit-slider-thumb {
    background: #000000;
}

#pauseMenu .slider-mix-fader::-moz-range-thumb {
    background: #000000;
}

/* ---- Main menu -----------------------------------------------------------
   Its own chrome, not the pause panel's: a full-height blue window over one
   third of the frame, with the other two thirds left clear for the rotating
   wireframe. `slider-menu-mode` hides every in-game readout for as long as it
   is up: the course behind it is a backdrop, and a score climbing under the
   menu reads as the game having started. */

.slider-menu {
    position: fixed;
    inset: 0;
    z-index: 40;
    overflow: hidden;
    pointer-events: none;
}

.slider-menu[hidden] {
    display: none;
}

/* Neither panel is ever `hidden` — they slide past each other, and a display
   swap has no motion to animate. Off-screen is a transform plus `inert`. */
.slider-menu-panel {
    /* The mask's chamfer starts 1100 px above the bottom of a 2000 px-tall file
       and cuts 200 px in, so at a scale that just fits the window it sits at
       55vh — proportional, while the content above it is mostly fixed px, and
       every page's content ends at a different height. So the drop is measured
       per panel by `fitMenuChamfers` and written here as an inline value: the
       mask is pushed DOWN past the bottom of the window (and scaled up by the
       same amount, so it still covers the top), which moves the chamfer without
       touching its 45° angle. This 0 is only what the first frame uses. */
    --menu-mask-drop: 0px;
    --menu-mask-height: calc(100vh + var(--menu-mask-drop));
    /* Both derived from that one scale: 200/2000 of the mask's height is the
       cut, and content in the bottom band pulls back by it. */
    --menu-chamfer: calc(var(--menu-mask-height) * 0.1);
    position: absolute;
    top: 0;
    bottom: 0;
    width: 33.334vw;
    min-width: 300px;
    display: flex;
    flex-direction: column;
    padding: 7vh 0 7vh;
    overflow: hidden;
    pointer-events: auto;
    transition: transform 380ms cubic-bezier(0.22, 0.7, 0.24, 1);
}

/* The panel's silhouette is `images/panel.svg` — a rectangle with a chamfer cut
   out of its INNER edge (the one facing the wireframe). Scaled UNIFORMLY to the
   window's height and anchored to that inner edge: the file is far wider than a
   panel, so the excess runs off the outer edge and is clipped, which is the
   leeway it was drawn oversized for.
   A MASK, not an <img>: the colour stays here, in one place, and the shape
   cannot fall out of sync with it. Behind the content (`z-index: -1`) — the
   panel's transform makes it a stacking context, so this cannot escape it. */
.slider-menu-panel::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: #1633c8;
    -webkit-mask-image: url('images/panel.svg');
    mask-image: url('images/panel.svg');
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: right 0 bottom calc(-1 * var(--menu-mask-drop));
    mask-position: right 0 bottom calc(-1 * var(--menu-mask-drop));
    -webkit-mask-size: auto var(--menu-mask-height);
    mask-size: auto var(--menu-mask-height);
}

/* A right-hand panel's inner edge is its LEFT one: same shape, mirrored. The
   flip is on the whole layer, so the anchoring mirrors with it. */
.slider-menu-panel-right::before {
    transform: scaleX(-1);
}

.slider-menu-panel-left {
    left: 0;
    /* Entrance direction for the staggered items: trail the panel's slide. */
    --menu-item-from: -14px;
    transform: translateX(0);
}

.slider-menu-panel-left.is-out {
    transform: translateX(-100%);
}

.slider-menu-panel-right {
    right: 0;
    --menu-item-from: 14px;
    transform: translateX(0);
}

.slider-menu-panel-right.is-out {
    transform: translateX(100%);
}

/* Shop hub bands. Full-width strips masked by `images/bottomPanel.svg` — the
   bottom one as drawn, the top one flipped — that slide in from the two edges
   and leave the heads centred between them. The mask is stretched to the band
   (the file's 2200x300 is close enough to a full-width band that the chamfer
   still reads at 45 deg); no chamfer fit runs on these, so they are NOT
   `.slider-menu-panel`.
   The band's own type is CENTRED, and every centred/mirrored variant below is
   written as a doubled class — the shared rules it overrides come later in this
   file, and at equal specificity the later one would win. */
.slider-menu-band {
    /* An SVG mask keeps its own aspect ratio (there is no preserveAspectRatio
       override on a mask-image), so the shape is height-fitted and letterboxed.
       The BAND is therefore sized to the file's ratio and centred — element and
       drawn shape are then the same box, which is what keeps the data strips
       inside the panel instead of running off to the window's edges. */
    --band-h: clamp(88px, 13vh, 150px);
    position: absolute;
    left: 0;
    right: 0;
    width: min(100vw, calc(var(--band-h) * 2200 / 300));
    margin: 0 auto;
    height: var(--band-h);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    /* The data strips live in these bands, exactly as they do on a panel —
       fixed px, because the band no longer scales with the window. */
    padding: 34px 0 30px;
    overflow: hidden;
    pointer-events: auto;
    --menu-item-from: 0px;
    transition: transform 380ms cubic-bezier(0.22, 0.7, 0.24, 1);
}

.slider-menu-band::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: #1633c8;
    -webkit-mask-image: url('images/bottomPanel.svg');
    mask-image: url('images/bottomPanel.svg');
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
}

.slider-menu-band-top {
    top: 0;
    transform: translateY(0);
}

/* The chamfered edge has to face the middle of the screen, so the top band is
   the same shape upside down. Flipping the mask layer only, so the text on it
   stays the right way up. */
.slider-menu-band-top::before {
    transform: scaleY(-1);
}

.slider-menu-band-top.is-out {
    transform: translateY(-100%);
}

.slider-menu-band-bottom {
    bottom: 0;
    transform: translateY(0);
}

.slider-menu-band-bottom.is-out {
    transform: translateY(100%);
}

/* The strips hug the band's own edges, not the window's vh rhythm: the band
   is short and fixed, so a vh offset would swim across window sizes. */
.slider-menu-band .slider-menu-diag {
    left: 22px;
    right: 22px;
}

.slider-menu-band .slider-menu-diag-top {
    top: 10px;
}

.slider-menu-band .slider-menu-diag-bottom {
    bottom: 10px;
}

/* Centred variants of the panel's own type, for the bands. */
.slider-menu-kicker.slider-menu-kicker-centre {
    margin-bottom: 0.6vh;
    padding: 0;
    text-align: center;
}

.slider-menu-subtitle.slider-menu-subtitle-centre {
    margin-bottom: 0;
    padding: 0;
    text-align: center;
}

/* FINISHES enters from the LEFT, so its type and its exit mirror the
   right-hand pages: everything hugs the panel's right (inner) edge. */
.slider-menu-subtitle.slider-menu-subtitle-right {
    text-align: right;
    padding: 0 3.2vw 0 2vw;
}

.slider-menu-option.slider-menu-option-right {
    text-align: right;
    align-self: flex-end;
    padding: 0 3.2vw 0 2vw;
    margin-top: 18px;
    background-position: calc(100% - 3.2vw) calc(100% - 2px);
}

/* The department picks sit side by side, not stacked: the band is wide. */
.slider-menu-options.slider-menu-options-centre {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 4vw;
    padding: 0;
}

/* The exit sits over the footer strip's left end, in the same column as it. */
.slider-menu-band .slider-menu-back {
    align-self: flex-start;
    margin-top: 6px;
    margin-left: 22px;
    padding: 0;
    text-align: left;
    background-position: 0 calc(100% - 2px);
}

/* Header/footer data strips, living in the panel's 7vh padding bands. */
.slider-menu-diag {
    position: absolute;
    left: 2vw;
    right: 2vw;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-family: var(--slider-sans);
    font-weight: var(--slider-sans-weight);
    font-size: 11px;
    letter-spacing: 0.18em;
    color: rgba(255, 255, 255, 0.66);
    pointer-events: none;
}

.slider-menu-diag-top {
    top: 2.2vh;
    padding-bottom: 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.25);
}

.slider-menu-diag-bottom {
    bottom: 2.2vh;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
}

/* The strip and its rule are one box, so pulling the box back off the inner
   edge moves both. Which edge that is depends on the side the panel enters
   from — the same mirroring the mask itself gets. */
.slider-menu-panel-left .slider-menu-diag-bottom {
    right: calc(2vw + var(--menu-chamfer));
}

.slider-menu-panel-right .slider-menu-diag-bottom {
    left: calc(2vw + var(--menu-chamfer));
}

.slider-menu-diag-strong {
    font-weight: 400;
    color: #ffffff;
}

/* DOWNLOAD sits in the top strip's corner, under EDIT (web only). */
.slider-menu-download {
    position: absolute;
    top: calc(2.2vh + 30px);
    right: 2vw;
    z-index: 1;
}

.slider-menu-download[hidden] {
    display: none;
}

.slider-menu-download-list {
    display: flex;
    flex-direction: column;
    margin-bottom: 6px;
}

.slider-menu-blink {
    display: inline-block;
    width: 0.55em;
    height: 0.85em;
    margin-left: 0.6em;
    background: currentColor;
    vertical-align: -0.05em;
    animation: sliderMenuBlink 1.1s steps(2, jump-none) infinite;
}

@keyframes sliderMenuBlink {
    from { opacity: 1; }
    to { opacity: 0; }
}

/* Ghost page number: the panel's index as a data-sheet watermark. */
.slider-menu-pageno {
    position: absolute;
    bottom: 5.5vh;
    /* Same 2vw margin as the diag strip below it: the two read as one column. */
    left: 2vw;
    z-index: 0;
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 300;
    font-size: clamp(90px, 10vw, 170px);
    line-height: 1;
    color: rgba(255, 255, 255, 0.12);
    pointer-events: none;
}

.slider-menu-pageno-left {
    left: auto;
    right: 2vw;
}

/* Kicker above the title, aligned with it against the inner edge. */
.slider-menu-kicker {
    margin: 0 0 1.4vh;
    padding: 0 3.2vw 0 2vw;
    font-family: var(--slider-sans);
    font-weight: var(--slider-sans-weight);
    font-size: clamp(10px, 0.9vw, 14px);
    letter-spacing: 0.34em;
    text-align: right;
    color: rgba(255, 255, 255, 0.75);
}

.slider-menu-kicker-left {
    padding: 0 2vw 0 3.2vw;
    text-align: left;
}

/* The title and the options share the panel's INNER edge — the side the
   wireframe is on — so the menu reads as one column against that edge. */
.slider-menu-title {
    margin: 0 0 6vh;
    padding: 0 3.2vw 0 2vw;
    font-family: 'Slider Supply Mono', monospace;
    font-size: clamp(52px, 6.4vw, 108px);
    font-weight: 500;
    line-height: 1;
    letter-spacing: 0.04em;
    text-align: right;
    color: #ffffff;
}

.slider-menu-subtitle {
    margin: 0 0 6vh;
    padding: 0 2vw 0 3.2vw;
    font-family: 'Slider Supply Mono', monospace;
    font-size: clamp(26px, 2.6vw, 44px);
    font-weight: 500;
    line-height: 1;
    letter-spacing: 0.24em;
    text-align: left;
    color: #ffffff;
}

.slider-menu-options {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
    padding: 0 3.2vw 0 2vw;
    counter-reset: menu-opt;
    position: relative;
    z-index: 1;
}

.slider-menu-options-left {
    align-items: flex-start;
    padding: 0 2vw 0 3.2vw;
}

.slider-menu-option {
    /* appearance:none or the UA still owns the hover/active/focus chrome. */
    appearance: none;
    -webkit-appearance: none;
    padding: 4px 0;
    background: transparent;
    border: none;
    font-family: 'Slider Supply Mono', monospace;
    font-size: clamp(18px, 1.9vw, 30px);
    letter-spacing: 0.14em;
    text-align: right;
    color: #ffffff;
    cursor: pointer;
    /* Underline sweep on hover: grows from the option's inner-edge side. */
    background-image: linear-gradient(currentColor, currentColor);
    background-repeat: no-repeat;
    background-position: 100% calc(100% - 2px);
    background-size: 0% 1px;
    transition: background-size 220ms cubic-bezier(0.22, 0.7, 0.24, 1),
        transform 220ms cubic-bezier(0.22, 0.7, 0.24, 1);
}

/* Every listed option carries its index, ledger style. */
.slider-menu-options .slider-menu-option::before {
    counter-increment: menu-opt;
    content: counter(menu-opt, decimal-leading-zero) ' ';
    font-family: var(--slider-mid);
    font-weight: 400;
    font-size: 0.55em;
    letter-spacing: calc(0.1em + var(--slider-mid-track));
    opacity: 0.55;
    margin-right: 0.7em;
    vertical-align: 0.22em;
}

.slider-menu-option-left {
    text-align: left;
    align-self: flex-start;
    padding: 0 2vw 0 3.2vw;
    margin-top: 18px;
    background-position: 3.2vw calc(100% - 2px);
}

/* Inside the list the CONTAINER already pads the inner edge — a second
   3.2vw on each button doubles it and shoves the names off the edge. */
.slider-menu-options-left .slider-menu-option-left {
    padding: 4px 0;
    margin-top: 0;
    background-position: 0 calc(100% - 2px);
}

/* BACK/RESET/CLOSE are exits, not destinations: smaller and dimmer than the
   entries above them, with the panel chrome's // motif standing in for an
   index. */
.slider-menu-back {
    font-size: clamp(12px, 1.1vw, 18px);
    letter-spacing: 0.2em;
    color: rgba(255, 255, 255, 0.7);
}

.slider-menu-back::before {
    content: '// ';
    opacity: 0.6;
}

.slider-menu-back:hover:not(:disabled),
.slider-menu-back:focus-visible:not(:disabled) {
    color: #cfe4ff;
}

/* In the pause card the exits sit flush left like every other row —
   .slider-menu-option's right alignment is a tall-panel convention. */
.slider-pause-page .slider-menu-back {
    align-self: flex-start;
    text-align: left;
    padding: 4px 0;
    margin-top: 10px;
}

.slider-menu-option:hover:not(:disabled),
.slider-menu-option:focus-visible:not(:disabled) {
    color: #cfe4ff;
    outline: none;
    background-size: 100% 1px;
    transform: translateX(-6px);
}

.slider-menu-options-left .slider-menu-option:hover:not(:disabled),
.slider-menu-options-left .slider-menu-option:focus-visible:not(:disabled),
.slider-menu-option-left:hover:not(:disabled),
.slider-menu-option-left:focus-visible:not(:disabled) {
    transform: translateX(6px);
}

/* Placeholders: still listed, but they read as not yet reachable. */
.slider-menu-option:disabled {
    color: rgba(255, 255, 255, 0.38);
    cursor: default;
}

.slider-menu-option:disabled::after {
    content: '//LOCKED';
    font-family: var(--slider-mid);
    font-weight: 400;
    font-size: 0.5em;
    letter-spacing: calc(0.2em + var(--slider-mid-track));
    opacity: 0.7;
    margin-left: 0.9em;
    vertical-align: 0.28em;
}

/* Staggered entrance: items trail the panel slide in its own direction.
   Keyed on `:not(.is-out)` so every arrival — boot unhide and panel swap
   alike — restarts the cascade; `backwards` holds items hidden until their
   delay elapses. */
.slider-menu-panel:not(.is-out) .slider-menu-kicker,
.slider-menu-panel:not(.is-out) .slider-menu-title,
.slider-menu-panel:not(.is-out) .slider-menu-subtitle,
.slider-menu-band:not(.is-out) .slider-menu-kicker,
.slider-menu-band:not(.is-out) .slider-menu-subtitle {
    animation: sliderMenuItemIn 340ms cubic-bezier(0.22, 0.7, 0.24, 1) 120ms backwards;
}

.slider-menu-panel:not(.is-out) .slider-menu-options > *,
.slider-menu-panel:not(.is-out) > .slider-menu-option,
.slider-menu-band:not(.is-out) .slider-menu-options > *,
.slider-menu-band:not(.is-out) > .slider-menu-option {
    animation: sliderMenuItemIn 300ms cubic-bezier(0.22, 0.7, 0.24, 1) 550ms backwards;
}

.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(1) { animation-delay: 200ms; }
.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(2) { animation-delay: 250ms; }
.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(3) { animation-delay: 300ms; }
.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(4) { animation-delay: 350ms; }
.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(5) { animation-delay: 400ms; }
.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(6) { animation-delay: 450ms; }
.slider-menu-panel:not(.is-out) .slider-menu-options > *:nth-child(7) { animation-delay: 500ms; }

@keyframes sliderMenuItemIn {
    from {
        opacity: 0;
        transform: translateX(var(--menu-item-from, 14px));
    }
}

/* ---- Settings page --------------------------------------------------------
   Ledger rows: label left, value boxed at the right edge. Display rows are
   stubs (they flip their value text only); CONTROLS raises the keybind
   window below. */

.slider-menu-section {
    margin: 16px 0 2px;
    font-family: var(--slider-sans);
    font-weight: var(--slider-sans-weight);
    font-size: 11px;
    letter-spacing: 0.3em;
    color: rgba(255, 255, 255, 0.55);
}

.slider-menu-section:first-child {
    margin-top: 0;
}

.slider-menu-settings {
    align-items: stretch;
}

.slider-menu-setting {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 18px;
    padding: 7px 0;
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.16);
    font-family: var(--slider-mid);
    font-weight: 500;
    font-size: clamp(13px, 1.15vw, 18px);
    letter-spacing: calc(0.14em + var(--slider-mid-track));
    text-align: left;
    color: #ffffff;
    cursor: pointer;
}

/* Settings rows answer a pointer or a stick the way the pause buttons do: a
   wash ACROSS the row, not a colour swap on the words alone. The wash bleeds
   past the row through a spread shadow rather than padding, so nothing in the
   column moves when it lights up. `--slider-menu-wash` is redefined on the
   inverted pause card, which is the whole reason it is a token. */
.slider-menu-setting:hover,
.slider-menu-setting:focus-visible,
.slider-menu-setting:focus-within,
.slider-mix-row:hover,
.slider-mix-row:focus-within {
    color: #cfe4ff;
    outline: none;
    background: var(--slider-menu-wash);
    box-shadow: 0 0 0 6px var(--slider-menu-wash);
}

/* The select and the fader are the two controls that would otherwise show the
   browser's own focus ring: they take the row's wash instead. */
.slider-menu-select:hover,
.slider-menu-select:focus-visible {
    outline: none;
    border-color: currentColor;
    background: var(--slider-menu-wash);
}

.slider-mix-fader:hover,
.slider-mix-fader:focus-visible {
    outline: none;
    background: currentColor;
}

.slider-mix-fader:hover::-webkit-slider-thumb,
.slider-mix-fader:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 4px var(--slider-menu-wash);
}

.slider-menu-setting-value {
    font-weight: 700;
    font-size: 0.72em;
    letter-spacing: 0.18em;
    border: 1px solid rgba(255, 255, 255, 0.35);
    padding: 3px 10px;
    min-width: 3.4em;
    text-align: center;
}

/* Customize option chips: one button per variant/colour in the row, styled
   like the ON/OFF value chips; the active one fills white. */
.slider-custom-opts {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 10px;
}

/* The row label sits on the FIRST chip row however many rows the chips wrap
   to: top-aligned, with the label's box (1em line + 4px pads) matching a
   chip's outer height (0.72em × line-height 1/0.72 + 3px pads + 1px borders). */
.slider-menu-setting:has(.slider-custom-opts) {
    align-items: flex-start;
}

.slider-menu-setting:has(.slider-custom-opts) > span {
    line-height: 1;
    padding: 4px 0;
}

/* Every chip the same width (sized to the longest name), so with the shared
   gap and the right-anchored rows, option N sits in the same column on every
   row — names of different lengths no longer stagger the grid. */
.slider-custom-opt {
    /* appearance:none or the UA still owns the hover/active/focus chrome. */
    appearance: none;
    -webkit-appearance: none;
    font-family: inherit;
    font-weight: 700;
    font-size: 0.72em;
    line-height: calc(1 / 0.72);
    letter-spacing: 0.18em;
    color: inherit;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.35);
    padding: 3px 0;
    width: 6.8em;
    text-align: center;
    cursor: pointer;
    box-shadow: none;
    transition: border-color 140ms linear, box-shadow 140ms linear,
        background-color 140ms linear, transform 140ms cubic-bezier(0.22, 0.7, 0.24, 1);
}

.slider-custom-opt:hover:not(:disabled),
.slider-custom-opt:focus-visible:not(:disabled) {
    outline: none;
    border-color: #cfe4ff;
    color: #cfe4ff;
    background-color: rgba(207, 228, 255, 0.12);
    box-shadow: 0 0 0 1px rgba(207, 228, 255, 0.35), 0 0 10px rgba(207, 228, 255, 0.25);
    transform: translateY(-1px);
}

.slider-custom-opt:active:not(:disabled) {
    transform: translateY(0);
}

/* Selected outranks hover: the fill must not wash out under the pointer. */
.slider-custom-opt[aria-pressed="true"],
.slider-custom-opt[aria-pressed="true"]:hover:not(:disabled),
.slider-custom-opt[aria-pressed="true"]:focus-visible:not(:disabled) {
    background: #ffffff;
    border-color: #ffffff;
    color: #2222cc;
}

/* Not bought yet: readable but plainly out of reach, and out of the tab order
   because it is disabled. */
.slider-custom-opt.is-locked {
    color: rgba(255, 255, 255, 0.4);
    border-style: dashed;
    border-color: rgba(255, 255, 255, 0.22);
    cursor: not-allowed;
}

/* Already bought: still listed and still selectable for preview, but greyed
   out — it is no longer for sale, so it must not read as an offer. */
.slider-custom-opt.is-owned,
.slider-custom-opt.is-owned:hover,
.slider-custom-opt.is-owned:focus-visible {
    color: rgba(255, 255, 255, 0.42);
    border-color: rgba(255, 255, 255, 0.22);
    background-color: transparent;
    box-shadow: none;
    transform: none;
    outline: none;
}

.slider-custom-opt.is-owned[aria-pressed="true"],
.slider-custom-opt.is-owned[aria-pressed="true"]:hover,
.slider-custom-opt.is-owned[aria-pressed="true"]:focus-visible {
    background: rgba(255, 255, 255, 0.42);
    border-color: rgba(255, 255, 255, 0.42);
    color: #2b2b52;
}

/* ---- Shop preview ----
   Only the ACQUIRE button lives in the panel; the piece it buys is drawn out in
   the render area beside it. An author `display` outranks the UA's `[hidden]` rule, so hiding the box
   with nothing selected has to be said here. */
.slider-shop-preview[hidden] {
    display: none;
}

.slider-shop-preview {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    padding: 0 3.2vw 0 2vw;
    margin-top: 14px;
}

.slider-menu-options-left ~ .slider-shop-preview {
    align-items: flex-start;
    padding: 0 2vw 0 3.2vw;
}

/* The container already pads to the panel's inner edge, so the button must
   not pad again — only its justification carries over. */
/* Follows the window in, once there is a window to buy from. */
.slider-shop-preview .slider-shop-acquire {
    padding: 0;
    margin-top: 8px;
    font-size: clamp(14px, 1.4vw, 22px);
    opacity: 0;
    transition: opacity 180ms 140ms linear;
}

.slider-shop-preview.is-open .slider-shop-acquire {
    opacity: 1;
}

.slider-shop-preview .slider-menu-option-left.slider-shop-acquire {
    background-position: 0 calc(100% - 2px);
}

.slider-shop-preview .slider-menu-option-right.slider-shop-acquire {
    background-position: 100% calc(100% - 2px);
}

.slider-menu-select {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.35);
    color: #ffffff;
    /* JetBrains, matching the ON/OFF value chips beside it — the chips inherit
       --slider-mid from the row and the select cannot, so it restates it. */
    font-family: var(--slider-mid);
    font-weight: 700;
    font-size: 0.72em;
    letter-spacing: 0.18em;
    padding: 3px 10px;
    text-align: center;
    cursor: pointer;
}

.slider-menu-select option {
    font-family: var(--slider-mid);
    background: #1633c8;
    color: #ffffff;
}

/* The native popup is an OS menu — its own font, its own rounded chrome, and
   nothing in the page can reach it. `appearance: base-select` opts the whole
   control into page styling INCLUDING the popup, which is then a real element
   (`::picker(select)`) laid out in the top layer. Browsers without it keep the
   `appearance: none` control above and their native list; the rules only differ
   in the popup, so there is no second look to maintain. */
@supports (appearance: base-select) {
    .slider-menu-select {
        appearance: base-select;
        /* base-select lays the control out as a flex button, so the value is
           centred by the box, not by text-align. */
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.5em;
    }

    /* The default arrow is an OS glyph: replaced with the panel chrome's own
       marker, so the closed control matches the // motif of the exits. */
    .slider-menu-select::picker-icon {
        content: '//';
        font-size: 0.9em;
        letter-spacing: 0;
        opacity: 0.6;
        transition: transform 160ms cubic-bezier(0.22, 0.7, 0.24, 1);
    }

    .slider-menu-select:open::picker-icon {
        transform: rotate(90deg);
    }

    .slider-menu-select::picker(select) {
        appearance: base-select;
        /* Flush with the control's edges: same border, same fill, no radius and
           no shadow — a ledger list dropped below the row, not a floating card. */
        margin: 0;
        padding: 0;
        border: 1px solid rgba(255, 255, 255, 0.35);
        border-radius: 0;
        box-shadow: none;
        background: #1633c8;
    }

    .slider-menu-select option {
        display: block;
        padding: 5px 10px;
        background: transparent;
        letter-spacing: 0.18em;
        /* The rows already read as a list; a per-row tick is OS chrome. */
        text-align: center;
    }

    .slider-menu-select option::checkmark {
        content: none;
    }

    .slider-menu-select option:checked {
        background: rgba(255, 255, 255, 0.14);
    }

    .slider-menu-select option:hover,
    .slider-menu-select option:focus {
        background: rgba(255, 255, 255, 0.22);
        outline: none;
    }
}

/* ---- Controls window ------------------------------------------------------
   The keybind window CONTROLS raises: a panel-blue card over a dimmed frame,
   rows built from Slider.Input.bindings(). A listening row's key box blinks
   until the next keydown is assigned (Escape cancels — it can't be bound).
   Raised from the main menu's settings page AND the pause menu's, so it
   lives outside both and sits above their z-index (40), below loading (50). */

.slider-menu-controls {
    position: fixed;
    inset: 0;
    z-index: 45;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(5, 6, 8, 0.28);
    pointer-events: auto;
}

.slider-menu-controls[hidden] {
    display: none;
}

.slider-menu-window {
    width: min(540px, 92vw);
    max-height: 84vh;
    overflow-y: auto;
    padding: 20px 26px 22px;
    background: #1633c8;
    border: 1px solid rgba(255, 255, 255, 0.4);
    display: flex;
    flex-direction: column;
    animation: sliderMenuItemIn 260ms cubic-bezier(0.22, 0.7, 0.24, 1) backwards;
}

.slider-menu-window-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-family: var(--slider-sans);
    font-weight: var(--slider-sans-weight);
    font-size: 11px;
    letter-spacing: 0.18em;
    color: rgba(255, 255, 255, 0.66);
    border-bottom: 1px solid rgba(255, 255, 255, 0.25);
    padding-bottom: 8px;
    margin-bottom: 14px;
}

.slider-menu-window-title {
    margin: 0 0 12px;
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 500;
    font-size: clamp(22px, 2vw, 30px);
    letter-spacing: 0.2em;
    color: #ffffff;
}

.slider-menu-bind-list {
    display: flex;
    flex-direction: column;
}

.slider-menu-bind {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    padding: 6px 8px;
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.14);
    font-family: var(--slider-mid);
    font-weight: 400;
    font-size: 14px;
    letter-spacing: calc(0.14em + var(--slider-mid-track));
    text-align: left;
    color: #ffffff;
    cursor: pointer;
}

.slider-menu-bind:hover,
.slider-menu-bind:focus-visible {
    background: rgba(255, 255, 255, 0.08);
    outline: none;
}

.slider-menu-bind-key {
    font-weight: 700;
    font-size: 12px;
    letter-spacing: 0.16em;
    border: 1px solid rgba(255, 255, 255, 0.4);
    padding: 3px 10px;
    min-width: 76px;
    text-align: center;
}

/* One device's column at a time. The bind row carries both cells so a rebind
   can fill either, but showing both asks the player to read the half that is
   not in their hands; `data-input` on the root is the live device. */
.slider-menu-bind-pad {
    display: none;
    align-items: center;
    justify-content: center;
    min-height: 20px;
}

[data-input="pad"] .slider-menu-bind-pad {
    display: inline-flex;
}

[data-input="pad"] .slider-menu-bind-label + .slider-menu-bind-key {
    display: none;
}

/* Nothing to point at, so no pointer. Two independent reasons: the pad has the
   window (the first mouse MOVE flips `data-input` back and returns the
   cursor), or a run is in progress, where the cursor never has a target
   whatever the device is. Written as html[...] * so it outranks the
   cursor: pointer on every button without reaching for !important. */
html[data-input="pad"] *,
html[data-playing] * {
    cursor: none;
}

/* The scan lines TRACK the cursor, so they follow it out of the frame: on the
   pad they point at a cursor that is not there. A run hides them already. */
html[data-input="pad"] .slider-menu-scan {
    display: none;
}

/* Face and d-pad glyphs, drawn as a MASK filled with currentColor: the art is
   one flat shape, so it takes the colour of whatever text it sits beside, and
   the 256px source downscales smoothly (`pixelated` shattered these edges). */
.slider-pad-glyph {
    display: inline-block;
    width: 1.6em;
    height: 1.6em;
    vertical-align: -0.45em;
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
}

.slider-menu-bind.is-listening {
    background: rgba(255, 255, 255, 0.1);
}

.slider-menu-bind.is-listening .slider-menu-bind-key {
    border-color: #cfe4ff;
    color: #cfe4ff;
    animation: sliderMenuBlink 0.6s steps(2, jump-none) infinite;
}

.slider-menu-window-hint {
    margin: 12px 0 14px;
    font-family: 'Slider Supply Mono', monospace;
    font-weight: 400;
    font-size: 11px;
    letter-spacing: 0.2em;
    color: rgba(255, 255, 255, 0.6);
}

.slider-menu-window-row {
    display: flex;
    justify-content: space-between;
}

/* ---- Telemetry scope ------------------------------------------------------
   Corner brackets framing the showcase area, with a readout fed per frame by
   drawShowcase (subject, spin, radius, mesh counts — the wire build's real
   numbers). It swaps sides with the page: the free two-thirds of the frame
   is right of the root panel, left of the stage panel. */

/* Scan line: sweeps the full window top to bottom; the panel paints over it,
   so what shows is the render area. It sits outside
   .slider-menu deliberately — that layer's z-index makes a stacking context,
   and `difference` inside it would blend against nothing instead of the
   canvas. No z-index here: later in the DOM is already above the canvas and
   below the menu. */
/* The scan lines FOLLOW the pointer — one per axis, moved by transform (the
   compositor's job, not layout's). Centred until the pointer first moves. */
.slider-menu-scan {
    position: fixed;
    top: 0;
    left: 0;
    background: #ffffff;
    mix-blend-mode: difference;
    pointer-events: none;
}

.slider-menu-scan-h {
    width: 100vw;
    height: 1px;
    transform: translateY(50vh);
}

.slider-menu-scan-v {
    width: 1px;
    height: 100vh;
    transform: translateX(50vw);
}

.slider-menu[hidden] ~ .slider-menu-scan {
    display: none;
}

.slider-menu-scope {
    position: absolute;
    top: 5vh;
    bottom: 5vh;
    left: calc(max(33.334vw, 300px) + 2.5vw);
    right: 2.5vw;
    pointer-events: none;
    background:
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) left top / 16px 1px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) left top / 1px 16px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) right top / 16px 1px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) right top / 1px 16px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) left bottom / 16px 1px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) left bottom / 1px 16px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) right bottom / 16px 1px,
        linear-gradient(rgba(10, 12, 16, 0.6), rgba(10, 12, 16, 0.6)) right bottom / 1px 16px;
    background-repeat: no-repeat;
}

/* The loading screen's copy of the scope: no panel beside it, so it spans the
   frame, and its field is the dark one — inverted whole rather than restating
   every tick and the readout ink, since the layer holds nothing else. */
.slider-loading-scope {
    left: 2.5vw;
    filter: invert(1);
}

.slider-menu.is-stage .slider-menu-scope {
    left: 2.5vw;
    right: calc(max(33.334vw, 300px) + 2.5vw);
}

.slider-menu-telemetry {
    position: absolute;
    left: 16px;
    bottom: 12px;
    margin: 0;
    font-family: var(--slider-mid);
    font-weight: 400;
    font-size: 12px;
    line-height: 1.7;
    letter-spacing: calc(0.08em + var(--slider-mid-track));
    color: rgba(10, 12, 16, 0.78);
}

/* Stage bio callout: the trick readout's language (white box, hairline
   leader) against the menu's light field, so the line is dark instead. The
   box paints over the line's tail, which is what makes it read as a leader. */
.slider-menu-bio {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.slider-menu-bio-line {
    position: absolute;
    left: 0;
    top: 0;
    height: 1px;
    margin-top: -0.5px;
    background: #050608;
    transform-origin: 0 50%;
}

.slider-menu-bio-box {
    position: absolute;
    left: 0;
    top: 0;
    transform: translate(-50%, -50%);
    box-sizing: border-box;
    width: 26ch;
    padding: 0.6em 0.8em 0.65em;
    border: 1px solid #050608;
    background: #ffffff;
    font-family: var(--slider-mid);
    font-weight: 400;
    font-size: 15.6px;
    line-height: 1.55;
    letter-spacing: calc(0.02em + var(--slider-mid-track));
    color: #10141a;
}

.slider-menu-bio-head {
    display: block;
    margin-bottom: 0.55em;
    font-size: 13px;
    letter-spacing: 0.18em;
    color: rgba(10, 12, 16, 0.55);
}

/* The shop chat hangs BELOW its head rather than centring on a point, and
   drops in so a repeat click on the same head still reads as a new line. */
/* Half again the bio box's type; padding and width are em/ch, so the box
   grows with it. */
.slider-shop-chat-box {
    transform: translate(-50%, 0);
    width: auto;
    max-width: 30ch;
    font-size: 18px;
    animation: slider-shop-chat-in 160ms cubic-bezier(0.22, 0.7, 0.24, 1) both;
}

.slider-shop-chat-box .slider-menu-bio-head {
    font-size: 15px;
}

@keyframes slider-shop-chat-in {
    from { opacity: 0; transform: translate(-50%, -6px); }
    to { opacity: 1; transform: translate(-50%, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .slider-menu *,
    .slider-menu *::before,
    .slider-menu *::after,
    .slider-menu-controls *,
    .slider-pause * {
        animation: none !important;
        transition: none !important;
    }
}


body.slider-ending-mode .slider-run-clock,
body.slider-ending-mode .slider-score-total {
    display: none;
}

body.slider-menu-mode .slider-hud,
body.slider-menu-mode .slider-hud-corner,
body.slider-menu-mode .slider-run-clock,
body.slider-menu-mode .slider-score-total,
body.slider-menu-mode .slider-combo-callout,
body.slider-menu-mode .slider-trick-callout,
body.slider-menu-mode .slider-callout-lines,
body.slider-menu-mode .slider-score-bank-pixels,
body.slider-menu-mode .slider-countdown,
body.slider-menu-mode .slider-tier-flash {
    display: none;
}

/* ---- Stats page ---------------------------------------------------------
   The totems' ASCII: course tags at each base, one tag per ring on the
   pointed-at totem, all placed by projection every frame (transform only —
   a left/top write would relayout a full-viewport layer per frame). */
.slider-stats-tag {
    position: absolute;
    left: 0;
    top: 0;
    margin: 0;
    /* --tag-scale rides the carousel's turn (set per frame), so a name
       shrinks continuously with its totem rather than stepping. */
    transform: translate(-50%, -50%) scale(var(--tag-scale, 1));
    font-family: var(--slider-mid);
    font-weight: 400;
    font-size: 12px;
    line-height: 1.3;
    letter-spacing: calc(0.08em + var(--slider-mid-track));
    white-space: pre;
    color: rgba(10, 12, 16, 0.78);
    /* The layer is pointer-transparent; the tags themselves are targets. */
    pointer-events: auto;
}
.slider-stats-tag.is-name {
    cursor: pointer;
}
.slider-stats-tag.is-ring {
    /* Clear of the ring's 2 px stroke and its offset passes. */
    transform: translate(1em, -50%);
}
/* A box docked beside a tag: left/top name its bottom-left corner, or
   its left-middle for the centred form. */
.slider-menu-bio-box.is-docked {
    transform: translate(0, -100%);
}
.slider-menu-bio-box.is-docked-mid {
    transform: translate(0, -50%);
}
/* The stats box sizes to its ASCII rows rather than the bio's fixed 26ch. */
#statsCallBox {
    width: max-content;
    max-width: 60vw;
}
.slider-stats-tag.is-lit {
    color: #10141a;
    font-weight: 500;
}
.slider-stats-ascii {
    margin: 0;
    font: inherit;
    line-height: 1.55;
    white-space: pre;
}
