/* Self-hosted fonts (was: fonts.googleapis.com).
   Only the latin + latin-ext subsets of the two faces actually used are
   included; IBM Plex Mono was being requested and never used. Same files
   Google would have served, pinned so the album renders identically for as
   long as it exists — and no third-party request before a note plays. */

@font-face {
    font-family: 'EB Garamond';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url('vendor/fonts/eb-garamond-400-italic-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'EB Garamond';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url('vendor/fonts/eb-garamond-400-italic-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Jost';
    font-style: normal;
    font-weight: 200;
    font-display: swap;
    src: url('vendor/fonts/jost-200-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Jost';
    font-style: normal;
    font-weight: 200;
    font-display: swap;
    src: url('vendor/fonts/jost-200-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* === Design Tokens === */

:root {
    --bg:        #B8D5DD;
    --ink:       #111;
    --title-ink: #404040;
    --muted:     #6b7d82;
    --line:      #9da9ac;
}

/* === Base Styles === */

* {
    box-sizing: border-box;
}

/* The `hidden` attribute must win over our own display rules. The shared button
   reset below sets `display: flex`, which silently defeats `hidden` on any
   <button> — that's why the Android-only install button rendered on iOS too.
   The credits overlay hit the same trap earlier and got its own rule; this one
   covers the whole document so it can't happen a third time. */
[hidden] {
    display: none !important;
}

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    overflow: hidden;
    /* Kills double-tap-to-zoom, which is easy to trigger by accident on the
       transport. Set on the root, not just body: touch-action isn't inherited,
       so on body alone every element inside computes to `auto` and whether the
       browser walks up to find it depends on scroll-container details that iOS
       treats its own way.

       `manipulation` still permits pinch — deliberately. Pinch is the accessible
       escape hatch for anyone who needs to magnify, and it's far harder to
       trigger by mistake than a double tap. The drag surfaces (fader strip,
       sample strip) set `touch-action: none` separately, which is stricter. */
    touch-action: manipulation;
}

body {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #FFF8DC;
    font-family: -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
    color: var(--ink);
    transition: background-color 0.3s ease;
}

ul {
    list-style-type: none;
    margin-top: -0.5rem;
    padding-left: 1.5rem;
}

/* === Container — phone-sized card, centred/top-anchored on larger screens === */

.container {
    width: 100%;
    max-width: 380px;
    height: 100dvh;
    /* Cap the height so on large screens (iPad, desktop) the player stays a
       phone-sized card centred in the track-colour field, instead of
       stretching to full viewport height and spreading its sections apart.
       On a phone, 100dvh is below this cap, so nothing changes there. */
    max-height: 780px;
    padding: 28px 18px 20px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
}

/* === Section: Header === */

header {
    text-align: left;
    flex-shrink: 0;
}

header h1 {
    font-family: 'Jost', sans-serif;
    font-weight: 200;
    /* Base size for iPad/desktop/tall phones. The mini re-asserts 2.4rem in
       its @media (max-height: 700px) block, so this only affects larger
       screens — keeps "Return to Repeat" on one line in the 380px column. */
    font-size: 2.2rem;
    line-height: 1.06;
    letter-spacing: 0.055em;
    text-transform: uppercase;
    color: var(--title-ink);
    margin: 0;
}

/* === Section: Track Name === */

.track-name {
    flex-shrink: 0;
    padding: 20px 0 0;
}

.track-name .track-title {
    font-family: -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 3rem;      /* 40px */
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.06em;
    text-transform: lowercase;
    color: var(--ink);
    margin: 0 0 36px;
}

/* Single curated line shown BELOW the track controls inside .track-area.
   width: 100% is required for text-align: right to behave inside the flex
   column (otherwise the element would shrink to fit its text). */
.track-tagline {
    font-family: 'EB Garamond', serif;
    font-style: italic;
    font-weight: 400;
    font-size: 18px;
    line-height: 1;
    letter-spacing: 0.01em;
    text-transform: lowercase;
    color: var(--ink);
    opacity: 0.8;
    text-align: right;
    width: 100%;
    margin: auto 0 0;
    padding-right: 4px;
}

/* Collapse when there is no tagline for this track */
.track-tagline:empty {
    display: none;
    margin: 0;
}

/* === Section: Track Area (flex-grows to fill) === */

.track-area {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    overflow: hidden;
    min-height: 0;
    /* Top pad pushes the controls down from the track title; bottom pad is the
       gap to the VU LEDs. The tagline sticks to the bottom via margin-top:auto. */
    padding: 80px 0 32px;
}

/* === Section: VU LEDs (Braun inspired, round) === */

.vu-leds {
    flex-shrink: 0;
    height: 24px;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 20px;
    margin-bottom: 10px;
}

.vu-segment {
    width: 8px;
    height: 8px;
    flex-shrink: 0;
    border-radius: 50%;
    background: #2a2a2a;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
    transition: background 0.06s;
}

/* Green segments (1-3) when lit */
.vu-segment.lit[data-level="1"],
.vu-segment.lit[data-level="2"],
.vu-segment.lit[data-level="3"] {
    background: #5ab860;
}

/* Yellow segment (4) when lit */
.vu-segment.lit[data-level="4"] {
    background: #d4b840;
}

/* Red segment (5) when lit */
.vu-segment.lit[data-level="5"] {
    background: #d45040;
}

/* === Section: Transport === */

.transport {
    flex-shrink: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 0 10px;
}

/* === Transport Buttons (Rams/NAD style) === */

.transport-btn {
    display: flex;
    justify-content: center;
    align-items: center;
    border: 2px solid rgba(0, 0, 0, 0.22);
    border-radius: 4px;
    background: transparent;
    color: #333;
    cursor: pointer;
    transition: background 0.12s, opacity 0.12s;
    overflow: hidden;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
}

.transport-btn .btn-icon {
    font-size: 0.95rem;
    text-align: center;
    color: rgba(0, 0, 0, 0.45);
}

/* Inline SVG icons (was: Font Awesome web font). Sized in em and filled with
   currentColor so font-size and color still drive them exactly as they did when
   they were glyphs — every existing rule, light-ui overrides included, keeps
   working untouched.

   Two deliberate choices, both to avoid engine differences that render the
   icons invisible:
   - The sprite is hidden by collapsing it to zero size rather than
     `display: none`, which stops <use> resolving its symbols in some engines.
   - Both width AND height are set. Relying on `width: auto` to infer the box
     from the viewBox aspect ratio works in Blink but not dependably in WebKit
     or Gecko, where it can compute to nothing — the icon is present but
     invisible. A square em box costs nothing: each icon keeps its own
     proportions inside it via preserveAspectRatio, so it renders at exactly
     the height the old font glyph did. */
.icon-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

svg.btn-icon,
svg.loading-spinner {
    width: 1em;
    height: 1em;
    fill: currentColor;
}

.transport-btn:active,
.transport-btn.pressed {
    background: rgba(0, 0, 0, 0.08);
    border-color: rgba(0, 0, 0, 0.35);
}

.transport-btn:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

/* === Play Button === */

#play-button {
    width: 100%;
    height: 72px;
}

#play-button.loading {
    pointer-events: none;
}

#play-button.loading .btn-icon {
    display: none;
}

#play-button .loading-spinner {
    font-size: 0.85rem;
    color: rgba(0, 0, 0, 0.35);
    animation: spin 1.2s linear infinite;
}

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

#play-button.locked {
    opacity: 0.2;
}

/* === Navigation Buttons === */

.nav-buttons-container {
    display: flex;
    width: 100%;
    gap: 6px;
}

#next-button,
#shuffle-button {
    flex: 1;
    height: 72px;
}

/* === Shared button reset (for non-transport buttons) === */

button {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 1.5rem;
    padding: 0;
    border: 2px solid #000;
    background: none;
    cursor: pointer;
    transition: opacity 0.15s;
    overflow: hidden;
    color: black;
}

button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

button:active {
    background: rgba(0, 0, 0, 0.1);
}

/* === Track Controls (mute buttons, faders, sample buttons) ===

   Control dimensions below are in **px, not rem**, on purpose. A listener who
   raises their browser's text size (Chrome Android's "Text scaling", common on
   Samsung) scales the root font, and rem-based button sizes grew with it: at a
   107% setting the 72px toggles became 77px, the four birds on Track 3 wrapped
   to two rows, and the controls overflowed onto the lyric line. These are
   graphics, not type — they shouldn't follow a text preference. Typography
   still scales, which is the point of the setting. At the default 16px root
   these values are identical to the rem ones they replaced. */

/* === Track Controls (mute buttons, faders, sample buttons) === */

.track-controls {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    width: 100%;
    min-height: 0;
}

.track-controls:empty {
    display: none;
}

/* Control buttons (mute, sample, cycle) */
.control-button {
    width: 72px;
    height: 56px;
    font-size: 1.5rem;
}

.control-button.muted {
    background: rgba(0, 0, 0, 0.15);
}

/* Toggle button (transport-styled mute with LED) */
.toggle-control {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.toggle-led {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #2a2a2a;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
    transition: background 0.15s;
}

.toggle-led.on {
    background: #5ab860;
}

/* Effect LED — color is set inline by JS during animation, transition is short
   so each frame's color change is smooth without lagging behind */
.toggle-led.effect-led {
    transition: background 0.05s linear;
}

.toggle-btn {
    width: 72px;
    height: 72px;
}

.toggle-btn .toggle-img {
    height: 80%;
    width: 80%;
    object-fit: contain;
    opacity: 0.45;
    pointer-events: none;
}

.toggle-btn:active,
.toggle-btn.pressed {
    background: transparent;
}

/* Stem select group (Track 9 cat buttons — radio-group stem selector) */
.stem-select-group {
    display: flex;
    gap: 10px;
    justify-content: center;
}

/* Stem-select radio group buttons */
.stem-select-btn {
    width: 72px;
    height: 72px;
}

/* Bird silhouette inside a sample-strip button — the PNG is used as a CSS mask,
   so background-color (set per-button via inline style) becomes the bird's colour.
   Explicit rem units avoid flex layout's percentage-resolution quirks. */
.strip-img {
    width: 56px;
    height: 56px;
    background-color: rgba(0, 0, 0, 0.5);  /* fallback if no color set */
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    pointer-events: none;
}

/* Sample button strip — edge-to-edge, no gap */
.sample-strip {
    display: flex;
    gap: 0;
    width: 100%;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.sample-strip .sample-button {
    flex: 1;
    height: 112px;
    border-radius: 0;
    border: 2px solid rgba(0, 0, 0, 0.22);
    border-right: none;
    font-size: 1.8rem;
}

.sample-strip .sample-button:last-child {
    border-right: 2px solid rgba(0, 0, 0, 0.22);
}

.sample-strip .sample-button.active {
    background: rgba(0, 0, 0, 0.08);
    border-color: rgba(0, 0, 0, 0.35);
}

/* Disable browser :active state inside strip — we manage it via .active class */
.sample-strip .sample-button:active {
    background: none;
}

/* Fader strip (Track 7 drones) — three separate columns matching the mute
   button width. Set the overall height here to make the faders "as tall as
   fits". translateY nudges the whole strip up from vertical centre — make the
   negative value larger to raise the faders higher. */
.fader-strip {
    display: flex;
    gap: 10px;
    justify-content: center;
    width: 100%;
    height: 240px;
    /* Never taller than the space the controls row actually got. The height
       above is a wish, not a promise: on a short viewport the row is smaller
       than 15rem, and since the row centres its content and doesn't clip, the
       strip would otherwise overflow and paint over the lyric line below.
       Seen on iOS Firefox (permanent bottom toolbar → less viewport than
       Safari), but it's a height problem, not an engine one — Safari clears it
       by ~11px on an iPhone 13 mini, which is luck rather than design. */
    max-height: 100%;
    transform: translateY(-50px);
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

/* When the controls row holds the fader strip, let it grow and centre the
   faders vertically in the available space (keeps them off the lyric line). */
.track-controls:has(.fader-strip) {
    flex: 1 1 auto;
    align-items: center;
    min-height: 0;
}

.fader-col {
    width: 72px;
    height: 100%;
    position: relative;
    overflow: hidden;
    border: 2px solid rgba(0, 0, 0, 0.22);
    border-radius: 4px;
    cursor: ns-resize;
}

/* Filled area below the set position. The fill is a shared translucent wash
   (same on all three faders); the top edge is the handle, tinted per-note via
   inline style from JS. Adjust the handle thickness with border-top-width. */
.fader-fill {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 0%;
    background: rgba(235, 230, 220, 0.12);
    border-top: 6px solid rgba(235, 230, 220, 0.7);  /* fallback if no colour set */
}

/* === Locked Track 11 === */

/* Blurred eel that sharpens with album completions.
   Inert: no border, no LED, not interactive. The per-stage blur is set
   inline by JS; the transition eases it if it ever changes live. */
.locked-eel {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
}

.locked-eel-img {
    width: 112px;
    height: auto;
    opacity: 0.8;
    pointer-events: none;
    transition: filter 0.6s ease;
}

.container.locked .track-title {
    filter: blur(2px);
}

/* === Footer === */

footer {
    flex-shrink: 0;
    text-align: center;
    padding: 14px 0 8px;
}

footer p {
    margin: 0;
    font-size: 0.7rem;
    font-weight: 100;
    word-spacing: 0.1em;
    color: var(--ink);
}

#credits-trigger {
    cursor: pointer;
    display: inline-block;
}

/* === Credits window === */

/* Full-screen blurred backdrop + click-to-close target */
.credits-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    background: rgba(0, 0, 0, 0.18);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.credits-overlay[hidden],
.credits-window[hidden] {
    display: none;
}

/* Centred over the card (.container), not the viewport — so it sits over the
   player on large screens where the card is anchored near the top. */
.credits-window {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 101;
    width: 280px;
    max-width: calc(100% - 36px);
    padding: 34px 28px;
    border: 2px solid rgba(0, 0, 0, 0.22);
    border-radius: 4px;
    background: transparent;
    font-family: 'EB Garamond', serif;
    font-weight: 400;
    font-size: 18px;
    line-height: 1.6;
    color: var(--ink);
    text-align: left;
}

.credits-window p {
    margin: 0 0 18px;
}

.credits-window p:last-child {
    margin-bottom: 0;
}

.credits-window a {
    color: inherit;
    text-decoration: none;
}

/* Install invitation inside the NOTES window. Deliberately quiet: separated by
   a hairline rather than a box, and in the same EB Garamond as the credits it
   sits under, so it reads as one more line of the album's own text rather than
   an advertisement wearing the album's clothes. */
.install-invite {
    margin-top: 22px;
    padding-top: 16px;
    border-top: 1px solid rgba(0, 0, 0, 0.18);
}

.install-invite p {
    margin: 0;
    opacity: 0.85;
}

/* The iOS share glyph, set inline in the sentence at the size of the text. */
.install-invite .inline-icon {
    width: 0.85em;
    height: 0.85em;
    fill: currentColor;
    vertical-align: -0.08em;
    margin: 0 0.1em;
}

/* Android's real install button — the credits window's own border language,
   not a call-to-action colour. */
.install-invite button {
    margin-top: 14px;
    padding: 8px 16px;
    border: 2px solid rgba(0, 0, 0, 0.22);
    border-radius: 4px;
    background: transparent;
    font-family: 'EB Garamond', serif;
    font-size: 16px;
    color: var(--ink);
    cursor: pointer;
}

.install-invite button:active {
    background: rgba(0, 0, 0, 0.08);
}

.container.light-ui .install-invite {
    border-top-color: rgba(235, 230, 220, 0.3);
}

.container.light-ui .install-invite button {
    border-color: rgba(235, 230, 220, 0.45);
}

.container.light-ui .install-invite button:active {
    background: rgba(235, 230, 220, 0.12);
}

/* Light UI: lighten the window border to match the other controls */
.container.light-ui .credits-window,
.container.light-ui .rotate-hint p {
    border-color: rgba(235, 230, 220, 0.45);
}

/* === Mobile Responsive === */

@media (max-width: 600px) {
    /* .container {
        padding: 20px 14px 16px;
    } */

    .container {
        padding: 16px 14px 12px;
    }

    .control-button {
        width: 48px;
        height: 48px;
        font-size: 1.3rem;
    }

    .sample-strip .sample-button {
        height: 96px;
    }

    .strip-img {
        width: 48px;
        height: 48px;
    }

    .fader-strip {
        height: 224px;
    }
}

/* Small phones (iPhone Mini etc.) */
@media (max-height: 700px) {
    .container {
        padding: 16px 14px 12px;
    }

    header h1 {
        font-size: 2.4rem;
    }

    .track-name {
        padding: 12px 0 0;
    }

    .track-name .track-title {
        /* font-size unchanged from base (3rem); only the spacing tightens */
        margin-bottom: 20px;
    }

    #play-button {
        height: 60.8px;
    }

    #next-button,
    #shuffle-button {
        height: 60.8px;
    }

    .vu-leds {
        height: 18px;
    }

    .sample-strip .sample-button {
        height: 88px;
    }

    .strip-img {
        width: 44px;
        height: 44px;
    }

    .fader-strip {
        height: 176px;
    }

    footer {
        padding: 6px 0 4px 0;
    }
}

/* Cramped viewports (Android Chrome ~620px, iOS Firefox ~610px — both keep a
   permanent toolbar where Safari doesn't). The 80px above the controls is a
   fixed cost the card can't afford here: it holds the buttons low AND starves
   the fader row, since the faders clamp to whatever height is left. Trimming it
   gives both back at once — at 360x620 the faders go 128px to 160px and the
   controls rise 32px, with the lyric line unmoved (it's bottom-anchored).

   640px threshold: iPhone 13 mini in Safari sits comfortably above it, so the
   device most listeners will use keeps the spacing it was tuned with. */
@media (max-height: 640px) {
    .track-area {
        padding-top: 48px;
    }

    /* The fader strip's -50px nudge was tuned against the 80px padding above.
       With 48px it lifts the strip past the top of .track-area, which clips
       (overflow: hidden) and eats the 2px top border — the faders render as
       open-topped boxes. Keep the nudge smaller than the padding so there's
       always headroom; 24px leaves 24px above and a 24px gap to the lyric
       line below. */
    .fader-strip {
        transform: translateY(-24px);
    }
}

/* Narrow phones (Android 360px and under). At 2.4rem "RETURN TO REPEAT" needs
   344px of line but only 332px is available at a 360px viewport, so it wraps to
   a third line and eats ~41px of height the card can't spare — which is what
   pushes the controls down onto the lyric line and squashes the faders.
   Width-scoped, so the iPhone 13 mini (375px) and everything larger keep the
   size they were tuned at. Placed after the short-screen block so it wins for
   narrow phones at any height. Measured on a Samsung Galaxy Xcover 5. */
@media (max-width: 374px) {
    header h1 {
        /* min() so a raised text-size setting can't push the line back into a
           wrap: 9.2vw is 33px at 360px wide, which always fits the column. */
        font-size: min(2.1rem, 9.2vw);   /* line needs ~301px of 332px */
    }

    /* Track 3's four toggles are the widest row in the album, and at 72px + 10px
       gaps they need 318px. A 360px phone leaves 332px — 14px of slack — and
       anything under 346px wraps them onto a second row, which then overflows
       onto the lyric line (found on a Galaxy Xcover 5, whose ~2.1 DPR gives a
       343px viewport). Smaller buttons and tighter gaps below the mini's 375px
       bring the row to 280px, which clears even a 320px screen. */
    .track-controls {
        gap: 8px;
    }

    .toggle-btn,
    .stem-select-btn {
        width: 64px;
        height: 64px;
    }

    .fader-col {
        width: 64px;   /* keep the control widths consistent across tracks */
    }
}

/* Very narrow (old iPhone SE, small Androids at 320px): 2.1rem wraps again. */
@media (max-width: 339px) {
    header h1 {
        font-size: min(1.9rem, 9.2vw);   /* line needs ~272px of 292px */
    }
}

/* Tall screens (iPad portrait, large desktop windows): anchor the capped card
   near the top with a fixed offset instead of dead-centring it. Phones stay
   below this breakpoint, so their layout is untouched. The 900px floor keeps
   the offset safely smaller than the available excess, so it never clips. */
@media (min-height: 900px) {
    body {
        align-items: flex-start;
    }

    .container {
        margin-top: 80px;
    }
}

/* Large, tall viewports (iPad portrait, big desktop windows): scale the whole
   card up instead of leaving it phone-sized in a field of dead space. On iPad
   Air 13" portrait the 380x780 card used only 60% of the height with 440px
   empty below it, while landscape looked right — the proportions were already
   correct, there was just too little of them.

   zoom, not transform: scale(). zoom scales layout, so hit-testing, the fader
   drag maths and the fixed credits backdrop all keep working; a transform would
   make .container the containing block for the position:fixed backdrop and
   shrink it to the card. Unsupported zoom degrades to the compact layout.

   The card plus its 80px top offset both scale, so the footprint is 860 * zoom.
   Each step stays comfortably inside its own breakpoint so the bottom of the
   card can never be clipped by the overflow:hidden body — Safari's toolbar
   eats into the viewport and the steps have to survive that. */
@media (min-width: 768px) and (min-height: 1100px) {
    .container {
        zoom: 1.2;   /* 1032px of 1100 */
    }
}

@media (min-width: 768px) and (min-height: 1250px) {
    .container {
        zoom: 1.35;  /* 1161px of 1250 — the iPad Air 13" portrait case */
    }
}

@media (min-width: 768px) and (min-height: 1400px) {
    .container {
        zoom: 1.5;   /* 1290px of 1400 */
    }
}

/* === Phone landscape: ask for portrait instead of showing the clipped card ===

   iOS Safari refuses screen.orientation.lock() in an ordinary tab, so a real
   orientation lock is impossible for someone who just browsed to the site — all
   we can do is decline to render the landscape layout, which otherwise clips
   (the card is portrait by design and the controls fall off the bottom).

   Scoped deliberately: max-height keeps iPad landscape out of it (that case
   looks good and stays), and hover:none keeps a desktop user with a short, wide
   window from being told to rotate their monitor. Audio keeps playing
   underneath — rotating mid-listen shouldn't punish the listener.

   Borrows the credits window's look: same blurred backdrop, same bordered box. */

.rotate-hint {
    display: none;
}

@media (orientation: landscape) and (max-height: 500px) and (hover: none) {
    .rotate-hint {
        position: fixed;
        inset: 0;
        z-index: 200;   /* above the credits window (101) */
        display: flex;
        justify-content: center;
        align-items: center;
        background: rgba(0, 0, 0, 0.18);
        backdrop-filter: blur(6px);
        -webkit-backdrop-filter: blur(6px);
    }

    .rotate-hint p {
        margin: 0;
        padding: 28px 32px;
        border: 2px solid rgba(0, 0, 0, 0.22);
        border-radius: 4px;
        font-family: 'EB Garamond', serif;
        font-size: 18px;
        line-height: 1.6;
        color: var(--ink);
        text-align: center;
    }
}

/* === Light UI mode (dark track backgrounds) === */

.container.light-ui {
    --ink:       rgba(235, 230, 220, 0.80);
    --title-ink: rgba(235, 230, 220, 0.80);
    --muted:      rgba(235, 230, 220, 0.6);
}

.container.light-ui .transport-btn {
    border-color: rgba(235, 230, 220, 0.45);
}

.container.light-ui .transport-btn .btn-icon {
    color: rgba(235, 230, 220, 0.7);
}

.container.light-ui .transport-btn:active,
.container.light-ui .transport-btn.pressed {
    background: rgba(235, 230, 220, 0.12);
    border-color: rgba(235, 230, 220, 0.65);
}

.container.light-ui button {
    border-color: rgba(235, 230, 220, 0.45);
    color: rgba(235, 230, 220, 0.7);
}

.container.light-ui button:active {
    background: rgba(235, 230, 220, 0.12);
}

.container.light-ui .sample-strip .sample-button {
    border-color: rgba(235, 230, 220, 0.45);
}

.container.light-ui .sample-strip .sample-button:last-child {
    border-right-color: rgba(235, 230, 220, 0.45);
}

.container.light-ui .sample-strip .sample-button.active {
    background: rgba(235, 230, 220, 0.12);
    border-color: rgba(235, 230, 220, 0.65);
}

.container.light-ui .fader-col {
    border-color: rgba(235, 230, 220, 0.45);
}
